From 283b8fa35664f16cc95d7adb98bf5bacf2d2ba2a Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 21 Apr 2023 01:17:33 +0000 Subject: [PATCH 001/112] Rebuild ARM templates --- Environments/FunctionApp/azuredeploy.json | 4 ++-- Environments/Sandbox/azuredeploy.json | 4 ++-- Environments/WebApp/azuredeploy.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index 0e7a5335..139c8777 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.15.31.15270", - "templateHash": "135257884104161714" + "version": "0.16.2.56959", + "templateHash": "14714597139081137006" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index 6d19ce0d..a2039127 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.15.31.15270", - "templateHash": "17750672647523699609" + "version": "0.16.2.56959", + "templateHash": "8154980294172562107" } }, "resources": [] diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 6e873964..4d6743d8 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.15.31.15270", - "templateHash": "6640258515963112941" + "version": "0.16.2.56959", + "templateHash": "3666499352609317658" } }, "parameters": { From aca80c3cf66a44dac7b9c3901e20aa3126863910 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 21 Apr 2023 09:26:33 +0800 Subject: [PATCH 002/112] add OpenAI search template --- Environments/OpenAISearch/abbreviations.json | 135 ++ Environments/OpenAISearch/azuredeploy.json | 1813 +++++++++++++++++ .../core/ai/cognitiveservices.bicep | 38 + .../OpenAISearch/core/host/appservice.bicep | 100 + .../core/host/appserviceplan.bicep | 21 + .../core/search/search-services.bicep | 41 + .../OpenAISearch/core/security/role.bicep | 20 + .../core/storage/storage-account.bicep | 58 + Environments/OpenAISearch/main.bicep | 274 +++ Environments/OpenAISearch/manifest.yaml | 25 + 10 files changed, 2525 insertions(+) create mode 100644 Environments/OpenAISearch/abbreviations.json create mode 100644 Environments/OpenAISearch/azuredeploy.json create mode 100644 Environments/OpenAISearch/core/ai/cognitiveservices.bicep create mode 100644 Environments/OpenAISearch/core/host/appservice.bicep create mode 100644 Environments/OpenAISearch/core/host/appserviceplan.bicep create mode 100644 Environments/OpenAISearch/core/search/search-services.bicep create mode 100644 Environments/OpenAISearch/core/security/role.bicep create mode 100644 Environments/OpenAISearch/core/storage/storage-account.bicep create mode 100644 Environments/OpenAISearch/main.bicep create mode 100644 Environments/OpenAISearch/manifest.yaml diff --git a/Environments/OpenAISearch/abbreviations.json b/Environments/OpenAISearch/abbreviations.json new file mode 100644 index 00000000..703e5038 --- /dev/null +++ b/Environments/OpenAISearch/abbreviations.json @@ -0,0 +1,135 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json new file mode 100644 index 00000000..e5a804d3 --- /dev/null +++ b/Environments/OpenAISearch/azuredeploy.json @@ -0,0 +1,1813 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "14862612510019136859" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "appServicePlanName": { + "type": "string", + "defaultValue": "" + }, + "backendServiceName": { + "type": "string", + "defaultValue": "" + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]" + }, + "searchServiceName": { + "type": "string", + "defaultValue": "" + }, + "searchServiceResourceGroupLocation": { + "type": "string", + "defaultValue": "[parameters('location')]" + }, + "searchServiceSkuName": { + "type": "string", + "defaultValue": "standard" + }, + "searchIndexName": { + "type": "string", + "defaultValue": "gptkbindex" + }, + "storageAccountName": { + "type": "string", + "defaultValue": "" + }, + "storageResourceGroupLocation": { + "type": "string", + "defaultValue": "[parameters('location')]" + }, + "storageContainerName": { + "type": "string", + "defaultValue": "content" + }, + "openAiServiceName": { + "type": "string", + "defaultValue": "" + }, + "openAiResourceGroupLocation": { + "type": "string", + "defaultValue": "[parameters('location')]" + }, + "openAiSkuName": { + "type": "string", + "defaultValue": "S0" + }, + "formRecognizerServiceName": { + "type": "string", + "defaultValue": "" + }, + "formRecognizerResourceGroupLocation": { + "type": "string", + "defaultValue": "[parameters('location')]" + }, + "formRecognizerSkuName": { + "type": "string", + "defaultValue": "S0" + }, + "gptDeploymentName": { + "type": "string", + "defaultValue": "davinci" + }, + "gptModelName": { + "type": "string", + "defaultValue": "text-davinci-003" + }, + "chatGptDeploymentName": { + "type": "string", + "defaultValue": "chat" + }, + "chatGptModelName": { + "type": "string", + "defaultValue": "gpt-35-turbo" + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "appserviceplan", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('appServicePlanName'))), createObject('value', parameters('appServicePlanName')), createObject('value', format('{0}{1}', variables('abbrs').webServerFarms, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "sku": { + "value": { + "name": "B1", + "capacity": 1 + } + }, + "kind": { + "value": "linux" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "1837336361730027989" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "kind": { + "type": "string", + "defaultValue": "" + }, + "reserved": { + "type": "bool", + "defaultValue": true + }, + "sku": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "kind": "[parameters('kind')]", + "properties": { + "reserved": "[parameters('reserved')]" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Web/serverfarms', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "web", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('backendServiceName'))), createObject('value', parameters('backendServiceName')), createObject('value', format('{0}backend-{1}', variables('abbrs').webSitesAppService, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(variables('tags'), createObject('azd-service-name', 'backend'))]" + }, + "appServicePlanId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'appserviceplan'), '2022-09-01').outputs.id.value]" + }, + "runtimeName": { + "value": "python" + }, + "runtimeVersion": { + "value": "3.10" + }, + "scmDoBuildDuringDeployment": { + "value": true + }, + "managedIdentity": { + "value": true + }, + "appSettings": { + "value": { + "AZURE_STORAGE_ACCOUNT": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]", + "AZURE_STORAGE_CONTAINER": "[parameters('storageContainerName')]", + "AZURE_OPENAI_SERVICE": "[reference(resourceId('Microsoft.Resources/deployments', 'openai'), '2022-09-01').outputs.name.value]", + "AZURE_SEARCH_INDEX": "[parameters('searchIndexName')]", + "AZURE_SEARCH_SERVICE": "[reference(resourceId('Microsoft.Resources/deployments', 'search-service'), '2022-09-01').outputs.name.value]", + "AZURE_OPENAI_GPT_DEPLOYMENT": "[parameters('gptDeploymentName')]", + "AZURE_OPENAI_CHATGPT_DEPLOYMENT": "[parameters('chatGptDeploymentName')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "935190995104945414" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "managedIdentity": { + "type": "bool", + "defaultValue": "[not(empty(parameters('keyVaultName')))]" + }, + "runtimeName": { + "type": "string", + "allowedValues": [ + "dotnet", + "dotnetcore", + "dotnet-isolated", + "node", + "python", + "java", + "powershell", + "custom" + ] + }, + "runtimeNameAndVersion": { + "type": "string", + "defaultValue": "[format('{0}|{1}', parameters('runtimeName'), parameters('runtimeVersion'))]" + }, + "runtimeVersion": { + "type": "string" + }, + "kind": { + "type": "string", + "defaultValue": "app,linux" + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "alwaysOn": { + "type": "bool", + "defaultValue": true + }, + "appCommandLine": { + "type": "string", + "defaultValue": "" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": false + }, + "enableOryxBuild": { + "type": "bool", + "defaultValue": "[contains(parameters('kind'), 'linux')]" + }, + "functionAppScaleLimit": { + "type": "int", + "defaultValue": -1 + }, + "linuxFxVersion": { + "type": "string", + "defaultValue": "[parameters('runtimeNameAndVersion')]" + }, + "minimumElasticInstanceCount": { + "type": "int", + "defaultValue": -1 + }, + "numberOfWorkers": { + "type": "int", + "defaultValue": -1 + }, + "scmDoBuildDuringDeployment": { + "type": "bool", + "defaultValue": false + }, + "use32BitWorkerProcess": { + "type": "bool", + "defaultValue": false + }, + "ftpsState": { + "type": "string", + "defaultValue": "FtpsOnly" + }, + "healthCheckPath": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'appsettings')]", + "properties": "[union(parameters('appSettings'), createObject('SCM_DO_BUILD_DURING_DEPLOYMENT', string(parameters('scmDoBuildDuringDeployment')), 'ENABLE_ORYX_BUILD', string(parameters('enableOryxBuild'))), if(not(empty(parameters('applicationInsightsName'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString), createObject()), if(not(empty(parameters('keyVaultName'))), createObject('AZURE_KEY_VAULT_ENDPOINT', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri), createObject()))]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'logs')]", + "properties": { + "applicationLogs": { + "fileSystem": { + "level": "Verbose" + } + }, + "detailedErrorMessages": { + "enabled": true + }, + "failedRequestsTracing": { + "enabled": true + }, + "httpLogs": { + "fileSystem": { + "enabled": true, + "retentionInDays": 1, + "retentionInMb": 35 + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]", + "[resourceId('Microsoft.Web/sites/config', parameters('name'), 'appsettings')]" + ] + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "serverFarmId": "[parameters('appServicePlanId')]", + "siteConfig": { + "linuxFxVersion": "[parameters('linuxFxVersion')]", + "alwaysOn": "[parameters('alwaysOn')]", + "ftpsState": "[parameters('ftpsState')]", + "appCommandLine": "[parameters('appCommandLine')]", + "numberOfWorkers": "[if(not(equals(parameters('numberOfWorkers'), -1)), parameters('numberOfWorkers'), null())]", + "minimumElasticInstanceCount": "[if(not(equals(parameters('minimumElasticInstanceCount'), -1)), parameters('minimumElasticInstanceCount'), null())]", + "use32BitWorkerProcess": "[parameters('use32BitWorkerProcess')]", + "functionAppScaleLimit": "[if(not(equals(parameters('functionAppScaleLimit'), -1)), parameters('functionAppScaleLimit'), null())]", + "healthCheckPath": "[parameters('healthCheckPath')]", + "cors": { + "allowedOrigins": "[union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins'))]" + } + }, + "clientAffinityEnabled": "[parameters('clientAffinityEnabled')]", + "httpsOnly": true + }, + "identity": { + "type": "[if(parameters('managedIdentity'), 'SystemAssigned', 'None')]" + } + } + ], + "outputs": { + "identityPrincipalId": { + "type": "string", + "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01', 'full').identity.principalId, '')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "value": "[format('https://{0}', reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01').defaultHostName)]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'appserviceplan')]", + "[resourceId('Microsoft.Resources/deployments', 'openai')]", + "[resourceId('Microsoft.Resources/deployments', 'search-service')]", + "[resourceId('Microsoft.Resources/deployments', 'storage')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "openai", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('openAiServiceName'))), createObject('value', parameters('openAiServiceName')), createObject('value', format('{0}{1}', variables('abbrs').cognitiveServicesAccounts, variables('resourceToken'))))]", + "location": { + "value": "[parameters('openAiResourceGroupLocation')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "sku": { + "value": { + "name": "[parameters('openAiSkuName')]" + } + }, + "deployments": { + "value": [ + { + "name": "[parameters('gptDeploymentName')]", + "model": { + "format": "OpenAI", + "name": "[parameters('gptModelName')]", + "version": "1" + }, + "scaleSettings": { + "scaleType": "Standard" + } + }, + { + "name": "[parameters('chatGptDeploymentName')]", + "model": { + "format": "OpenAI", + "name": "[parameters('chatGptModelName')]", + "version": "0301" + }, + "scaleSettings": { + "scaleType": "Standard" + } + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "15292359723056804408" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "customSubDomainName": { + "type": "string", + "defaultValue": "[parameters('name')]" + }, + "deployments": { + "type": "array", + "defaultValue": [] + }, + "kind": { + "type": "string", + "defaultValue": "OpenAI" + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled" + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "S0" + } + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2022-10-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "customSubDomainName": "[parameters('customSubDomainName')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]" + }, + "sku": "[parameters('sku')]" + }, + { + "copy": { + "name": "deployment", + "count": "[length(parameters('deployments'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", + "properties": { + "model": "[parameters('deployments')[copyIndex()].model]", + "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]", + "scaleSettings": "[parameters('deployments')[copyIndex()].scaleSettings]" + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + ] + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2022-10-01').endpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "formrecognizer", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('formRecognizerServiceName'))), createObject('value', parameters('formRecognizerServiceName')), createObject('value', format('{0}{1}', variables('abbrs').cognitiveServicesFormRecognizer, variables('resourceToken'))))]", + "kind": { + "value": "FormRecognizer" + }, + "location": { + "value": "[parameters('formRecognizerResourceGroupLocation')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "sku": { + "value": { + "name": "[parameters('formRecognizerSkuName')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "15292359723056804408" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "customSubDomainName": { + "type": "string", + "defaultValue": "[parameters('name')]" + }, + "deployments": { + "type": "array", + "defaultValue": [] + }, + "kind": { + "type": "string", + "defaultValue": "OpenAI" + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled" + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "S0" + } + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2022-10-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "customSubDomainName": "[parameters('customSubDomainName')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]" + }, + "sku": "[parameters('sku')]" + }, + { + "copy": { + "name": "deployment", + "count": "[length(parameters('deployments'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", + "properties": { + "model": "[parameters('deployments')[copyIndex()].model]", + "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]", + "scaleSettings": "[parameters('deployments')[copyIndex()].scaleSettings]" + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + ] + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2022-10-01').endpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-service", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('searchServiceName'))), createObject('value', parameters('searchServiceName')), createObject('value', format('gptkb-{0}', variables('resourceToken'))))]", + "location": { + "value": "[parameters('searchServiceResourceGroupLocation')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "authOptions": { + "value": { + "aadOrApiKey": { + "aadAuthFailureMode": "http401WithBearerChallenge" + } + } + }, + "sku": { + "value": { + "name": "[parameters('searchServiceSkuName')]" + } + }, + "semanticSearch": { + "value": "free" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "11912420961517980450" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "standard" + } + }, + "authOptions": { + "type": "object", + "defaultValue": {} + }, + "semanticSearch": { + "type": "string", + "defaultValue": "disabled" + } + }, + "resources": [ + { + "type": "Microsoft.Search/searchServices", + "apiVersion": "2021-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "authOptions": "[parameters('authOptions')]", + "disableLocalAuth": false, + "disabledDataExfiltrationOptions": [], + "encryptionWithCmk": { + "enforcement": "Unspecified" + }, + "hostingMode": "default", + "networkRuleSet": { + "bypass": "None", + "ipRules": [] + }, + "partitionCount": 1, + "publicNetworkAccess": "Enabled", + "replicaCount": 1, + "semanticSearch": "[parameters('semanticSearch')]" + }, + "sku": "[parameters('sku')]" + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" + }, + "endpoint": { + "type": "string", + "value": "[format('https://{0}.search.windows.net/', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('storageAccountName'))), createObject('value', parameters('storageAccountName')), createObject('value', format('{0}{1}', variables('abbrs').storageStorageAccounts, variables('resourceToken'))))]", + "location": { + "value": "[parameters('storageResourceGroupLocation')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "sku": { + "value": { + "name": "Standard_ZRS" + } + }, + "deleteRetentionPolicy": { + "value": { + "enabled": true, + "days": 2 + } + }, + "containers": { + "value": [ + { + "name": "[parameters('storageContainerName')]", + "publicAccess": "None" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "14234446244824387754" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Hot", + "Cool", + "Premium" + ] + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": true + }, + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false + }, + "deleteRetentionPolicy": { + "type": "object", + "defaultValue": {} + }, + "dnsEndpointType": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "AzureDnsZone", + "Standard" + ] + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2" + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2" + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ] + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Standard_LRS" + } + }, + "containers": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [ + { + "copy": { + "name": "container", + "count": "[length(parameters('containers'))]" + }, + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}/{2}', parameters('name'), 'default', parameters('containers')[copyIndex()].name)]", + "properties": { + "publicAccess": "[if(contains(parameters('containers')[copyIndex()], 'publicAccess'), parameters('containers')[copyIndex()].publicAccess, 'None')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('name'), 'default')]" + ] + }, + { + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "properties": { + "deleteRetentionPolicy": "[parameters('deleteRetentionPolicy')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "sku": "[parameters('sku')]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "dnsEndpointType": "[parameters('dnsEndpointType')]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "Allow" + }, + "publicNetworkAccess": "[parameters('publicNetworkAccess')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "primaryEndpoints": { + "type": "object", + "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-05-01').primaryEndpoints]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "openai-role-user", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "formrecognizer-role-user", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "a97b65f3-24c7-4388-baec-2e87135dc908" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage-role-user", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "2a2b9908-6ea1-4ae2-8e65-a410df84e7d1" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage-contribrole-user", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "ba92f5b4-2d11-453d-a403-e96b0029c9fe" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-role-user", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "1407120a-92aa-4202-b7e9-c0e197c71c8f" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-contrib-role-user", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "8ebe5a00-799e-43f5-93ac-243d3dce84a7" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "openai-role-backend", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.identityPrincipalId.value]" + }, + "roleDefinitionId": { + "value": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd" + }, + "principalType": { + "value": "ServicePrincipal" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage-role-backend", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.identityPrincipalId.value]" + }, + "roleDefinitionId": { + "value": "2a2b9908-6ea1-4ae2-8e65-a410df84e7d1" + }, + "principalType": { + "value": "ServicePrincipal" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-role-backend", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.identityPrincipalId.value]" + }, + "roleDefinitionId": { + "value": "1407120a-92aa-4202-b7e9-c0e197c71c8f" + }, + "principalType": { + "value": "ServicePrincipal" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + } + ], + "outputs": { + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_RESOURCE_GROUP": { + "type": "string", + "value": "[parameters('resourceGroupName')]" + }, + "AZURE_OPENAI_SERVICE": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'openai'), '2022-09-01').outputs.name.value]" + }, + "AZURE_OPENAI_GPT_DEPLOYMENT": { + "type": "string", + "value": "[parameters('gptDeploymentName')]" + }, + "AZURE_OPENAI_CHATGPT_DEPLOYMENT": { + "type": "string", + "value": "[parameters('chatGptDeploymentName')]" + }, + "AZURE_FORMRECOGNIZER_SERVICE": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'formrecognizer'), '2022-09-01').outputs.name.value]" + }, + "AZURE_SEARCH_INDEX": { + "type": "string", + "value": "[parameters('searchIndexName')]" + }, + "AZURE_SEARCH_SERVICE": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'search-service'), '2022-09-01').outputs.name.value]" + }, + "AZURE_STORAGE_ACCOUNT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" + }, + "AZURE_STORAGE_CONTAINER": { + "type": "string", + "value": "[parameters('storageContainerName')]" + }, + "BACKEND_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.uri.value]" + } + } +} \ No newline at end of file diff --git a/Environments/OpenAISearch/core/ai/cognitiveservices.bicep b/Environments/OpenAISearch/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..39108a39 --- /dev/null +++ b/Environments/OpenAISearch/core/ai/cognitiveservices.bicep @@ -0,0 +1,38 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2022-10-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + scaleSettings: deployment.scaleSettings + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/OpenAISearch/core/host/appservice.bicep b/Environments/OpenAISearch/core/host/appservice.bicep new file mode 100644 index 00000000..c90c2491 --- /dev/null +++ b/Environments/OpenAISearch/core/host/appservice.bicep @@ -0,0 +1,100 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configAppSettings 'config' = { + name: 'appsettings' + properties: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [ + configAppSettings + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/OpenAISearch/core/host/appserviceplan.bicep b/Environments/OpenAISearch/core/host/appserviceplan.bicep new file mode 100644 index 00000000..c444f406 --- /dev/null +++ b/Environments/OpenAISearch/core/host/appserviceplan.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/OpenAISearch/core/search/search-services.bicep b/Environments/OpenAISearch/core/search/search-services.bicep new file mode 100644 index 00000000..894716a1 --- /dev/null +++ b/Environments/OpenAISearch/core/search/search-services.bicep @@ -0,0 +1,41 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param semanticSearch string = 'disabled' + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + authOptions: authOptions + disableLocalAuth: false + disabledDataExfiltrationOptions: [] + encryptionWithCmk: { + enforcement: 'Unspecified' + } + hostingMode: 'default' + networkRuleSet: { + bypass: 'None' + ipRules: [] + } + partitionCount: 1 + publicNetworkAccess: 'Enabled' + replicaCount: 1 + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name diff --git a/Environments/OpenAISearch/core/security/role.bicep b/Environments/OpenAISearch/core/security/role.bicep new file mode 100644 index 00000000..dca01e18 --- /dev/null +++ b/Environments/OpenAISearch/core/security/role.bicep @@ -0,0 +1,20 @@ +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/OpenAISearch/core/storage/storage-account.bicep b/Environments/OpenAISearch/core/storage/storage-account.bicep new file mode 100644 index 00000000..b6dd9891 --- /dev/null +++ b/Environments/OpenAISearch/core/storage/storage-account.bicep @@ -0,0 +1,58 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ 'Hot', 'Cool', 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = false +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Disabled' +param sku object = { name: 'Standard_LRS' } + +param containers array = [] + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Allow' + } + publicNetworkAccess: publicNetworkAccess + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep new file mode 100644 index 00000000..e481c2a8 --- /dev/null +++ b/Environments/OpenAISearch/main.bicep @@ -0,0 +1,274 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string + +param appServicePlanName string = '' +param backendServiceName string = '' +param resourceGroupName string = resourceGroup().name + +param searchServiceName string = '' +param searchServiceResourceGroupLocation string = location + +param searchServiceSkuName string = 'standard' +param searchIndexName string = 'gptkbindex' + +param storageAccountName string = '' +param storageResourceGroupLocation string = location +param storageContainerName string = 'content' + +param openAiServiceName string = '' +param openAiResourceGroupLocation string = location + +param openAiSkuName string = 'S0' + +param formRecognizerServiceName string = '' +param formRecognizerResourceGroupLocation string = location + +param formRecognizerSkuName string = 'S0' + +param gptDeploymentName string = 'davinci' +param gptModelName string = 'text-davinci-003' +param chatGptDeploymentName string = 'chat' +param chatGptModelName string = 'gpt-35-turbo' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan 'core/host/appserviceplan.bicep' = { + name: 'appserviceplan' + params: { + name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' + location: location + tags: tags + sku: { + name: 'B1' + capacity: 1 + } + kind: 'linux' + } +} + +// The application frontend +module backend 'core/host/appservice.bicep' = { + name: 'web' + params: { + name: !empty(backendServiceName) ? backendServiceName : '${abbrs.webSitesAppService}backend-${resourceToken}' + location: location + tags: union(tags, { 'azd-service-name': 'backend' }) + appServicePlanId: appServicePlan.outputs.id + runtimeName: 'python' + runtimeVersion: '3.10' + scmDoBuildDuringDeployment: true + managedIdentity: true + appSettings: { + AZURE_STORAGE_ACCOUNT: storage.outputs.name + AZURE_STORAGE_CONTAINER: storageContainerName + AZURE_OPENAI_SERVICE: openAi.outputs.name + AZURE_SEARCH_INDEX: searchIndexName + AZURE_SEARCH_SERVICE: searchService.outputs.name + AZURE_OPENAI_GPT_DEPLOYMENT: gptDeploymentName + AZURE_OPENAI_CHATGPT_DEPLOYMENT: chatGptDeploymentName + } + } +} + +module openAi 'core/ai/cognitiveservices.bicep' = { + name: 'openai' + params: { + name: !empty(openAiServiceName) ? openAiServiceName : '${abbrs.cognitiveServicesAccounts}${resourceToken}' + location: openAiResourceGroupLocation + tags: tags + sku: { + name: openAiSkuName + } + deployments: [ + { + name: gptDeploymentName + model: { + format: 'OpenAI' + name: gptModelName + version: '1' + } + scaleSettings: { + scaleType: 'Standard' + } + } + { + name: chatGptDeploymentName + model: { + format: 'OpenAI' + name: chatGptModelName + version: '0301' + } + scaleSettings: { + scaleType: 'Standard' + } + } + ] + } +} + +module formRecognizer 'core/ai/cognitiveservices.bicep' = { + name: 'formrecognizer' + params: { + name: !empty(formRecognizerServiceName) ? formRecognizerServiceName : '${abbrs.cognitiveServicesFormRecognizer}${resourceToken}' + kind: 'FormRecognizer' + location: formRecognizerResourceGroupLocation + tags: tags + sku: { + name: formRecognizerSkuName + } + } +} + +module searchService 'core/search/search-services.bicep' = { + name: 'search-service' + params: { + name: !empty(searchServiceName) ? searchServiceName : 'gptkb-${resourceToken}' + location: searchServiceResourceGroupLocation + tags: tags + authOptions: { + aadOrApiKey: { + aadAuthFailureMode: 'http401WithBearerChallenge' + } + } + sku: { + name: searchServiceSkuName + } + semanticSearch: 'free' + } +} + +module storage 'core/storage/storage-account.bicep' = { + name: 'storage' + params: { + name: !empty(storageAccountName) ? storageAccountName : '${abbrs.storageStorageAccounts}${resourceToken}' + location: storageResourceGroupLocation + tags: tags + publicNetworkAccess: 'Enabled' + sku: { + name: 'Standard_ZRS' + } + deleteRetentionPolicy: { + enabled: true + days: 2 + } + containers: [ + { + name: storageContainerName + publicAccess: 'None' + } + ] + } +} + +// USER ROLES +module openAiRoleUser 'core/security/role.bicep' = { + name: 'openai-role-user' + params: { + principalId: principalId + roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + principalType: 'User' + } +} + +module formRecognizerRoleUser 'core/security/role.bicep' = { + name: 'formrecognizer-role-user' + params: { + principalId: principalId + roleDefinitionId: 'a97b65f3-24c7-4388-baec-2e87135dc908' + principalType: 'User' + } +} + +module storageRoleUser 'core/security/role.bicep' = { + name: 'storage-role-user' + params: { + principalId: principalId + roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + principalType: 'User' + } +} + +module storageContribRoleUser 'core/security/role.bicep' = { + name: 'storage-contribrole-user' + params: { + principalId: principalId + roleDefinitionId: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' + principalType: 'User' + } +} + +module searchRoleUser 'core/security/role.bicep' = { + name: 'search-role-user' + params: { + principalId: principalId + roleDefinitionId: '1407120a-92aa-4202-b7e9-c0e197c71c8f' + principalType: 'User' + } +} + +module searchContribRoleUser 'core/security/role.bicep' = { + name: 'search-contrib-role-user' + params: { + principalId: principalId + roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' + principalType: 'User' + } +} + +// SYSTEM IDENTITIES +module openAiRoleBackend 'core/security/role.bicep' = { + name: 'openai-role-backend' + params: { + principalId: backend.outputs.identityPrincipalId + roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + principalType: 'ServicePrincipal' + } +} + +module storageRoleBackend 'core/security/role.bicep' = { + name: 'storage-role-backend' + params: { + principalId: backend.outputs.identityPrincipalId + roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + principalType: 'ServicePrincipal' + } +} + +module searchRoleBackend 'core/security/role.bicep' = { + name: 'search-role-backend' + params: { + principalId: backend.outputs.identityPrincipalId + roleDefinitionId: '1407120a-92aa-4202-b7e9-c0e197c71c8f' + principalType: 'ServicePrincipal' + } +} + +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_RESOURCE_GROUP string = resourceGroupName + +output AZURE_OPENAI_SERVICE string = openAi.outputs.name +output AZURE_OPENAI_GPT_DEPLOYMENT string = gptDeploymentName +output AZURE_OPENAI_CHATGPT_DEPLOYMENT string = chatGptDeploymentName + +output AZURE_FORMRECOGNIZER_SERVICE string = formRecognizer.outputs.name + +output AZURE_SEARCH_INDEX string = searchIndexName +output AZURE_SEARCH_SERVICE string = searchService.outputs.name + +output AZURE_STORAGE_ACCOUNT string = storage.outputs.name +output AZURE_STORAGE_CONTAINER string = storageContainerName + +output BACKEND_URI string = backend.outputs.uri diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml new file mode 100644 index 00000000..c050c41c --- /dev/null +++ b/Environments/OpenAISearch/manifest.yaml @@ -0,0 +1,25 @@ +name: azure-search-openai-demo +version: 1.0.0 +summary: OpenAI search app environment +description: Deploys an OpenAI search demo application +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: true + + - id: location + name: location + description: 'Location of the resources' + type: string + required: true + + - id: principalId + name: principalId + description: 'Id of the user or app to assign application roles' + type: string + required: true \ No newline at end of file From ff05c0ec1689186be8e778e86dce09abe9f39dfb Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 28 Apr 2023 11:01:18 +0800 Subject: [PATCH 003/112] add readme and optimize scripts --- Environments/OpenAISearch/README.md | 4 +++ Environments/OpenAISearch/azuredeploy.json | 14 ++------- Environments/OpenAISearch/main.bicep | 5 +-- Environments/OpenAISearch/manifest.yaml | 36 +++++++++++++++++----- Environments/README.md | 1 + 5 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 Environments/OpenAISearch/README.md diff --git a/Environments/OpenAISearch/README.md b/Environments/OpenAISearch/README.md new file mode 100644 index 00000000..2675bf24 --- /dev/null +++ b/Environments/OpenAISearch/README.md @@ -0,0 +1,4 @@ +# OpenAI Search +This template helps the developer quickly setup the infrastructure about OpenAI Search with enterprise own data for searching. + +**Please note:** This template is just hosting the infrastructure. If the developer wants to deploy the application and prepare the data, please refer to the related [AZD repo](https://github.com/Azure-Samples/azure-search-openai-demo). Typically, the developer can upload to app service by VS Code and upload the required test pdf files by executing the script "scripts/prepdocs.ps1" diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index e5a804d3..6d67f1f7 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.16.2.56959", - "templateHash": "14862612510019136859" + "templateHash": "2447162074270477957" } }, "parameters": { @@ -19,10 +19,10 @@ }, "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Primary location for all resources" - }, - "minLength": 1 + } }, "appServicePlanName": { "type": "string", @@ -32,10 +32,6 @@ "type": "string", "defaultValue": "" }, - "resourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]" - }, "searchServiceName": { "type": "string", "defaultValue": "" @@ -1769,10 +1765,6 @@ "type": "string", "value": "[tenant().tenantId]" }, - "AZURE_RESOURCE_GROUP": { - "type": "string", - "value": "[parameters('resourceGroupName')]" - }, "AZURE_OPENAI_SERVICE": { "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', 'openai'), '2022-09-01').outputs.name.value]" diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep index e481c2a8..e9f046e7 100644 --- a/Environments/OpenAISearch/main.bicep +++ b/Environments/OpenAISearch/main.bicep @@ -3,13 +3,11 @@ @description('Name of the the environment which is used to generate a short unique hash used in all resources.') param environmentName string -@minLength(1) @description('Primary location for all resources') -param location string +param location string = resourceGroup().location param appServicePlanName string = '' param backendServiceName string = '' -param resourceGroupName string = resourceGroup().name param searchServiceName string = '' param searchServiceResourceGroupLocation string = location @@ -257,7 +255,6 @@ module searchRoleBackend 'core/security/role.bicep' = { output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId -output AZURE_RESOURCE_GROUP string = resourceGroupName output AZURE_OPENAI_SERVICE string = openAi.outputs.name output AZURE_OPENAI_GPT_DEPLOYMENT string = gptDeploymentName diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index c050c41c..020af909 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -12,14 +12,36 @@ parameters: type: string required: true - - id: location - name: location - description: 'Location of the resources' - type: string - required: true - - id: principalId name: principalId description: 'Id of the user or app to assign application roles' type: string - required: true \ No newline at end of file + required: true + + - id: backendServiceName + name: backendServiceName + description: 'Name of app service' + type: string + required: false + default: "" + + - id: storageAccountName + name: storageAccountName + description: 'Name of storage account' + type: string + required: false + default: "" + + - id: searchServiceName + name: searchServiceName + description: 'Name of search service' + type: string + required: false + default: "" + + - id: formRecognizerServiceName + name: formRecognizerServiceName + description: 'Name of form recognizer service' + type: string + required: false + default: "" \ No newline at end of file diff --git a/Environments/README.md b/Environments/README.md index ae20f97d..2df66dcc 100644 --- a/Environments/README.md +++ b/Environments/README.md @@ -7,6 +7,7 @@ The sample Catalog consists of a few catalog items (ARM Template + associated ma - [Function App](FunctionApp): Deploys an Azure Function App, Storage Account, and Application Insights - [Sandbox](Sandbox): Deploys an empty "sandbox" environment - [Web App](WebApp): Deploys an Azure Web App without a data store +- [OpenAI Search](OpenAISearch): Deploys Azure Web App, OpenAI service and Cogonitive search ## ARM and Bicep From 7c1c12843e00e4e18822302a0705dc4f45748b9b Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 8 May 2023 09:40:33 +0800 Subject: [PATCH 004/112] update read me --- Environments/OpenAISearch/README.md | 59 ++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/Environments/OpenAISearch/README.md b/Environments/OpenAISearch/README.md index 2675bf24..924ba3ee 100644 --- a/Environments/OpenAISearch/README.md +++ b/Environments/OpenAISearch/README.md @@ -1,4 +1,59 @@ # OpenAI Search -This template helps the developer quickly setup the infrastructure about OpenAI Search with enterprise own data for searching. +This template helps the developer quickly set up the infrastructure about OpenAI Search with enterprise own data for searching. -**Please note:** This template is just hosting the infrastructure. If the developer wants to deploy the application and prepare the data, please refer to the related [AZD repo](https://github.com/Azure-Samples/azure-search-openai-demo). Typically, the developer can upload to app service by VS Code and upload the required test pdf files by executing the script "scripts/prepdocs.ps1" +This repo is the just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/azure-search-openai-demo) + +## Prerequisites +Ensure that your machine has installed the following items +- [Python 3+](https://www.python.org/downloads/) + - **Important**: Python and the pip package manager must be in the path in Windows for the setup scripts to work. + - **Important**: Ensure you can run `python --version` from console. On Ubuntu, you might need to run `sudo apt install python-is-python3` to link `python` to `python3`. +- [Node.js](https://nodejs.org/en/download/) +- [Git](https://git-scm.com/downloads) +- [Powershell 7+ (pwsh)](https://github.com/powershell/powershell) - For Windows users only. + - **Important**: Ensure you can run `pwsh.exe` from a PowerShell command. If this fails, you likely need to upgrade PowerShell. + +>NOTE: Your Azure Account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator) or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner). + +- [Dev Center](https://learn.microsoft.com/en-us/azure/deployment-environments/quickstart-create-and-configure-devcenter), [Project](https://learn.microsoft.com/en-us/azure/deployment-environments/quickstart-create-and-configure-projects) and [Catalog](https://learn.microsoft.com/en-us/azure/deployment-environments/how-to-configure-catalog) + - **Important**: Ensure the user is assigned role "Deployment Environments User" to the project +- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) and [Azure CLI extension for Dev Center](https://learn.microsoft.com/en-us/azure/deployment-environments/how-to-install-devcenter-cli-extension) +- Clone the application and data [repo](https://github.com/luxu-ms/azure-search-openai-demo) + +## How to setup +Typically, there are two ways to quickly setup your own OpenAI and Cognitive search application. +1. Leverage the script "deploy.ps1" in the root folder to automatically deploy. +2. Manually deploy the infra, application and data. + +### Leverage the script "deploy.ps1" in the root folder to automatically deploy +1. Use "az login" to login +2. Run the script "deploy.ps1" in the PowerShell by the command below: +``` +.\deploy.ps1 +``` +>NOTE: is the user id that you use in step 1. If you do not know the user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find the "Object ID" + +### Manually deploy the infra, application and data +1. Use [Dev Portal](https://devportal.microsoft.com/) to deploy the infra +* Click "+New" -> "New environment" +* Give a name for the environment name (e.g. dev1) +* Select the environment type (e.g. Dev/Test) +* Select catalog item (e.g. azure-search-openai-demo) +* Click "Next" +* Input the required parameters (e.g. "environmentName" is dev1, "principalId" is your user's Object ID which can be found in the Azure Active Directory's users) +* Click "Create" + +2. Deploy the application to App service +* Go to the folder "app" in root folder, run "start.ps1" to build backend and frontend +* In VS Code, right click "backend" folder and select "Deploy to Web App.." (Note: If no such option, please install "Azure Tools" extension in VS Code) +* Follow the guide to deploy to your app service which is created in step 1 + +3. Upload the test data to storage account +In step 1, find the created resource: storage account name, search service name and form recognizer service name. +Execute the commands below to upload the test data. +``` +$storageAccountName='' +$searchServiceName='' +$formRecognizerServiceName='
' +.\scripts\prepdocs.ps1 $storageAccountName $searchServiceName $formRecognizerServiceName +``` From 4910dbd64a6ea76c49fb079ef113a40fcd24f273 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 8 May 2023 11:47:52 +0800 Subject: [PATCH 005/112] fix principal id displaying issue --- Environments/OpenAISearch/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/OpenAISearch/README.md b/Environments/OpenAISearch/README.md index 924ba3ee..f7712ec9 100644 --- a/Environments/OpenAISearch/README.md +++ b/Environments/OpenAISearch/README.md @@ -31,7 +31,7 @@ Typically, there are two ways to quickly setup your own OpenAI and Cognitive sea ``` .\deploy.ps1 ``` ->NOTE: is the user id that you use in step 1. If you do not know the user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find the "Object ID" +>NOTE: "principal id" is the user id that you use in step 1. If you do not know the user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find the "Object ID" ### Manually deploy the infra, application and data 1. Use [Dev Portal](https://devportal.microsoft.com/) to deploy the infra From fe3506b3c13bd6d7beefdcdcdd6e4ac62db27be8 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 10 May 2023 08:43:08 +0800 Subject: [PATCH 006/112] add Container App template --- Environments/ContainerApp/README.md | 33 + Environments/ContainerApp/api.bicep | 32 + Environments/ContainerApp/azuredeploy.json | 792 +++++++++++ .../core/database/cosmos/cosmos-account.bicep | 48 + .../cosmos/mongo/cosmos-mongo-account.bicep | 22 + .../cosmos/mongo/cosmos-mongo-db.bicep | 46 + .../cosmos/sql/cosmos-sql-account.bicep | 21 + .../database/cosmos/sql/cosmos-sql-db.bicep | 73 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 18 + .../cosmos/sql/cosmos-sql-role-def.bicep | 29 + .../core/database/sqlserver/sqlserver.bicep | 129 ++ .../core/gateway/apim-api-policy.xml | 92 ++ .../ContainerApp/core/gateway/apim.bicep | 61 + .../ContainerApp/core/host/appservice.bicep | 98 ++ .../core/host/appserviceplan.bicep | 20 + .../core/host/container-app.bicep | 77 + .../host/container-apps-environment.bicep | 26 + .../core/host/container-apps.bicep | 30 + .../core/host/container-registry.bicep | 36 + .../ContainerApp/core/host/functions.bicep | 82 ++ .../ContainerApp/core/host/staticwebapp.bicep | 21 + .../applicationinsights-dashboard.bicep | 1235 +++++++++++++++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 21 + .../core/monitor/monitoring.bicep | 31 + .../core/security/keyvault-access.bicep | 21 + .../core/security/keyvault-secret.bicep | 30 + .../ContainerApp/core/security/keyvault.bicep | 25 + .../core/storage/storage-account.bicep | 38 + Environments/ContainerApp/main.bicep | 76 + Environments/ContainerApp/manifest.yaml | 14 + 31 files changed, 3307 insertions(+) create mode 100644 Environments/ContainerApp/README.md create mode 100644 Environments/ContainerApp/api.bicep create mode 100644 Environments/ContainerApp/azuredeploy.json create mode 100644 Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/ContainerApp/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/ContainerApp/core/gateway/apim-api-policy.xml create mode 100644 Environments/ContainerApp/core/gateway/apim.bicep create mode 100644 Environments/ContainerApp/core/host/appservice.bicep create mode 100644 Environments/ContainerApp/core/host/appserviceplan.bicep create mode 100644 Environments/ContainerApp/core/host/container-app.bicep create mode 100644 Environments/ContainerApp/core/host/container-apps-environment.bicep create mode 100644 Environments/ContainerApp/core/host/container-apps.bicep create mode 100644 Environments/ContainerApp/core/host/container-registry.bicep create mode 100644 Environments/ContainerApp/core/host/functions.bicep create mode 100644 Environments/ContainerApp/core/host/staticwebapp.bicep create mode 100644 Environments/ContainerApp/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/ContainerApp/core/monitor/applicationinsights.bicep create mode 100644 Environments/ContainerApp/core/monitor/loganalytics.bicep create mode 100644 Environments/ContainerApp/core/monitor/monitoring.bicep create mode 100644 Environments/ContainerApp/core/security/keyvault-access.bicep create mode 100644 Environments/ContainerApp/core/security/keyvault-secret.bicep create mode 100644 Environments/ContainerApp/core/security/keyvault.bicep create mode 100644 Environments/ContainerApp/core/storage/storage-account.bicep create mode 100644 Environments/ContainerApp/main.bicep create mode 100644 Environments/ContainerApp/manifest.yaml diff --git a/Environments/ContainerApp/README.md b/Environments/ContainerApp/README.md new file mode 100644 index 00000000..ae7f20cc --- /dev/null +++ b/Environments/ContainerApp/README.md @@ -0,0 +1,33 @@ +# Container App +This template helps the developer quickly set up the infrastructure about Container App. + +This repo is just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/simple-flask-api-container.git) + +## Prerequisites +* Docker +* Azure Deployment Environment has provisioned the environment + +## QuickStart +1. Clone this repo + +2. Go to the folder "src", ensure you have login to docker server. +``` +docker login +``` +>NOTE: You can go to Azure Portal and find the "Access Keys" info in Azure Container Registry which is provisioned by ADE. + +use docker build to create the image. +``` +docker build -t /simple-flask-api-container:0.0.1 . +``` + +After the build completed, use docker push to upload to container registry. +``` +docker push /simple-flask-api-container:0.0.1 +``` + +3. Go to Container Apps in Azure Portal, select the Container App provisioned, click "Containers", click "Edit and Deploy", select the container image and click "Edit". +4. Select "Azure Container Registry" for "Image Source", select "Registry", "Image" and "Image tag" and click "Save", click "Create". Wait a while to let it take effect. + +## How to verify +Go to your Container App's overview, click "Application Url" and add "/generate_name" in the URL, there will be a generated name. \ No newline at end of file diff --git a/Environments/ContainerApp/api.bicep b/Environments/ContainerApp/api.bicep new file mode 100644 index 00000000..6bb88c05 --- /dev/null +++ b/Environments/ContainerApp/api.bicep @@ -0,0 +1,32 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param imageName string = '' +param keyVaultName string +param serviceName string = 'api' + +module app 'core/host/container-app.bicep' = { + name: '${serviceName}-container-app-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + imageName: !empty(imageName) ? imageName : 'nginx:latest' + keyVaultName: keyVault.name + targetPort: 5000 + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = app.outputs.identityPrincipalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json new file mode 100644 index 00000000..d5bf6760 --- /dev/null +++ b/Environments/ContainerApp/azuredeploy.json @@ -0,0 +1,792 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "6767977252020051216" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "defaultValue": "test", + "metadata": { + "description": "Name which is used to generate a short unique hash for each resource" + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "apiImageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The image name for the api service" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "prefix": "[format('{0}-{1}', parameters('environmentName'), variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('kv-{0}', take(replace(variables('prefix'), '-', ''), 17))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "10766203656109589284" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "containerAppsEnvironmentName": { + "value": "[format('cae-{0}', variables('prefix'))]" + }, + "containerRegistryName": { + "value": "[format('cr{0}', replace(variables('prefix'), '-', ''))]" + }, + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "12550809022825447853" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "242147059711260467" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-registry', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "14762338059257613188" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": true + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices" + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled" + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled" + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "registryLoginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.loginServer.value]" + }, + "registryName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('ca-{0}', take(variables('prefix'), 19))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "imageName": { + "value": "[parameters('apiImageName')]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "16445015047461323116" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "imageName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "api" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app-module', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), createObject('value', 'nginx:latest'))]", + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "targetPort": { + "value": 5000 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "7411744424728408137" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "containerName": { + "type": "string", + "defaultValue": "main" + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "" + }, + "env": { + "type": "array", + "defaultValue": [] + }, + "external": { + "type": "bool", + "defaultValue": true + }, + "imageName": { + "type": "string" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "managedIdentity": { + "type": "bool", + "defaultValue": "[not(empty(parameters('keyVaultName')))]" + }, + "targetPort": { + "type": "int", + "defaultValue": 80 + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g. 0.5" + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g. 1Gi" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[if(parameters('managedIdentity'), 'SystemAssigned', 'None')]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "single", + "ingress": { + "external": "[parameters('external')]", + "targetPort": "[parameters('targetPort')]", + "transport": "auto" + }, + "secrets": [ + { + "name": "registry-password", + "value": "[listCredentials(resourceId('Microsoft.ContainerRegistry/registries', parameters('containerRegistryName')), '2022-02-01-preview').passwords[0].value]" + } + ], + "registries": [ + { + "server": "[format('{0}.azurecr.io', parameters('containerRegistryName'))]", + "username": "[parameters('containerRegistryName')]", + "passwordSecretRef": "registry-password" + } + ] + }, + "template": { + "containers": [ + { + "image": "[parameters('imageName')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ] + } + } + } + ], + "outputs": { + "identityPrincipalId": { + "type": "string", + "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2022-03-01', 'full').identity.principalId, '')]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "value": "[format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2022-03-01').configuration.ingress.fqdn)]" + } + } + } + } + } + ], + "outputs": { + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app-module', parameters('serviceName'))), '2022-09-01').outputs.identityPrincipalId.value]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app-module', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_API_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app-module', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_API_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app-module', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('log-{0}', variables('prefix'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "10960702046792783086" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryLoginServer.value]" + }, + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID.value]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_NAME.value]" + }, + "SERVICE_API_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value]" + }, + "SERVICE_API_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_IMAGE_NAME.value]" + }, + "SERVICE_API_ENDPOINTS": { + "type": "array", + "value": [ + "[format('{0}/generate_name', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" + ] + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + } +} \ No newline at end of file diff --git a/Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep b/Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6bc1f2eb --- /dev/null +++ b/Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,48 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..bd2a2b5f --- /dev/null +++ b/Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,22 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2c9688e0 --- /dev/null +++ b/Environments/ContainerApp/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,46 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..e8b030f6 --- /dev/null +++ b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..5a4de209 --- /dev/null +++ b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,73 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefintion 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefintion.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefintion.outputs.id diff --git a/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..6855edfe --- /dev/null +++ b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,18 @@ +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..cfb40335 --- /dev/null +++ b/Environments/ContainerApp/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,29 @@ +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/ContainerApp/core/database/sqlserver/sqlserver.bicep b/Environments/ContainerApp/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..821a9087 --- /dev/null +++ b/Environments/ContainerApp/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,129 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/ContainerApp/core/gateway/apim-api-policy.xml b/Environments/ContainerApp/core/gateway/apim-api-policy.xml new file mode 100644 index 00000000..2a6d8113 --- /dev/null +++ b/Environments/ContainerApp/core/gateway/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {0} + + + PUT + GET + POST + DELETE + PATCH + + +
*
+
+ +
*
+
+
+ + + + + + + Call to the @(context.Api.Name) + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + We're Sorry. An unexpected error has occurred. If this continues please contact Tech Support. + + +
\ No newline at end of file diff --git a/Environments/ContainerApp/core/gateway/apim.bicep b/Environments/ContainerApp/core/gateway/apim.bicep new file mode 100644 index 00000000..76c5ae7b --- /dev/null +++ b/Environments/ContainerApp/core/gateway/apim.bicep @@ -0,0 +1,61 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/ContainerApp/core/host/appservice.bicep b/Environments/ContainerApp/core/host/appservice.bicep new file mode 100644 index 00000000..04c2c491 --- /dev/null +++ b/Environments/ContainerApp/core/host/appservice.bicep @@ -0,0 +1,98 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configAppSettings 'config' = { + name: 'appsettings' + properties: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [ + configAppSettings + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/ContainerApp/core/host/appserviceplan.bicep b/Environments/ContainerApp/core/host/appserviceplan.bicep new file mode 100644 index 00000000..69c35d78 --- /dev/null +++ b/Environments/ContainerApp/core/host/appserviceplan.bicep @@ -0,0 +1,20 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id diff --git a/Environments/ContainerApp/core/host/container-app.bicep b/Environments/ContainerApp/core/host/container-app.bicep new file mode 100644 index 00000000..dde1bab7 --- /dev/null +++ b/Environments/ContainerApp/core/host/container-app.bicep @@ -0,0 +1,77 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string = '' +param containerName string = 'main' +param containerRegistryName string = '' +param env array = [] +param external bool = true +param imageName string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param targetPort int = 80 + +@description('CPU cores allocated to a single container instance, e.g. 0.5') +param containerCpuCoreCount string = '0.5' + +@description('Memory allocated to a single container instance, e.g. 1Gi') +param containerMemory string = '1.0Gi' + +resource app 'Microsoft.App/containerApps@2022-03-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: 'single' + ingress: { + external: external + targetPort: targetPort + transport: 'auto' + } + secrets: [ + { + name: 'registry-password' + value: containerRegistry.listCredentials().passwords[0].value + } + ] + registries: [ + { + server: '${containerRegistry.name}.azurecr.io' + username: containerRegistry.name + passwordSecretRef: 'registry-password' + } + ] + } + template: { + containers: [ + { + image: imageName + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { + name: containerAppsEnvironmentName +} + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} + +output identityPrincipalId string = managedIdentity ? app.identity.principalId : '' +output imageName string = imageName +output name string = app.name +output uri string = 'https://${app.properties.configuration.ingress.fqdn}' diff --git a/Environments/ContainerApp/core/host/container-apps-environment.bicep b/Environments/ContainerApp/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..2dd858cb --- /dev/null +++ b/Environments/ContainerApp/core/host/container-apps-environment.bicep @@ -0,0 +1,26 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +output name string = containerAppsEnvironment.name diff --git a/Environments/ContainerApp/core/host/container-apps.bicep b/Environments/ContainerApp/core/host/container-apps.bicep new file mode 100644 index 00000000..395af705 --- /dev/null +++ b/Environments/ContainerApp/core/host/container-apps.bicep @@ -0,0 +1,30 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string = '' +param containerRegistryName string = '' +param logAnalyticsWorkspaceName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + } +} + +output environmentName string = containerAppsEnvironment.outputs.name +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/ContainerApp/core/host/container-registry.bicep b/Environments/ContainerApp/core/host/container-registry.bicep new file mode 100644 index 00000000..01c32139 --- /dev/null +++ b/Environments/ContainerApp/core/host/container-registry.bicep @@ -0,0 +1,36 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param adminUserEnabled bool = true +param anonymousPullEnabled bool = false +param dataEndpointEnabled bool = false +param encryption object = { + status: 'disabled' +} +param networkRuleBypassOptions string = 'AzureServices' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'Basic' +} +param zoneRedundancy string = 'Disabled' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/ContainerApp/core/host/functions.bicep b/Environments/ContainerApp/core/host/functions.bicep new file mode 100644 index 00000000..28a581bd --- /dev/null +++ b/Environments/ContainerApp/core/host/functions.bicep @@ -0,0 +1,82 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/ContainerApp/core/host/staticwebapp.bicep b/Environments/ContainerApp/core/host/staticwebapp.bicep new file mode 100644 index 00000000..91c2d0db --- /dev/null +++ b/Environments/ContainerApp/core/host/staticwebapp.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/ContainerApp/core/monitor/applicationinsights-dashboard.bicep b/Environments/ContainerApp/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..b7af2c1a --- /dev/null +++ b/Environments/ContainerApp/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1235 @@ +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/ContainerApp/core/monitor/applicationinsights.bicep b/Environments/ContainerApp/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..f76b292b --- /dev/null +++ b/Environments/ContainerApp/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/ContainerApp/core/monitor/loganalytics.bicep b/Environments/ContainerApp/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..770544cc --- /dev/null +++ b/Environments/ContainerApp/core/monitor/loganalytics.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/ContainerApp/core/monitor/monitoring.bicep b/Environments/ContainerApp/core/monitor/monitoring.bicep new file mode 100644 index 00000000..96ba11e5 --- /dev/null +++ b/Environments/ContainerApp/core/monitor/monitoring.bicep @@ -0,0 +1,31 @@ +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/ContainerApp/core/security/keyvault-access.bicep b/Environments/ContainerApp/core/security/keyvault-access.bicep new file mode 100644 index 00000000..96c9cf73 --- /dev/null +++ b/Environments/ContainerApp/core/security/keyvault-access.bicep @@ -0,0 +1,21 @@ +param name string = 'add' + +param keyVaultName string = '' +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/ContainerApp/core/security/keyvault-secret.bicep b/Environments/ContainerApp/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..5f786ce5 --- /dev/null +++ b/Environments/ContainerApp/core/security/keyvault-secret.bicep @@ -0,0 +1,30 @@ +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/ContainerApp/core/security/keyvault.bicep b/Environments/ContainerApp/core/security/keyvault.bicep new file mode 100644 index 00000000..0eb4a86d --- /dev/null +++ b/Environments/ContainerApp/core/security/keyvault.bicep @@ -0,0 +1,25 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/ContainerApp/core/storage/storage-account.bicep b/Environments/ContainerApp/core/storage/storage-account.bicep new file mode 100644 index 00000000..a41972ce --- /dev/null +++ b/Environments/ContainerApp/core/storage/storage-account.bicep @@ -0,0 +1,38 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param allowBlobPublicAccess bool = false +param containers array = [] +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + minimumTlsVersion: minimumTlsVersion + allowBlobPublicAccess: allowBlobPublicAccess + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Allow' + } + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/ContainerApp/main.bicep b/Environments/ContainerApp/main.bicep new file mode 100644 index 00000000..5708192a --- /dev/null +++ b/Environments/ContainerApp/main.bicep @@ -0,0 +1,76 @@ +@minLength(1) +@maxLength(64) +@description('Name which is used to generate a short unique hash for each resource') +param environmentName string = 'test' + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +@description('The image name for the api service') +param apiImageName string = '' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) +var tags = { 'azd-env-name': environmentName } + +var prefix = '${environmentName}-${resourceToken}' + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: 'kv-${take(replace(prefix, '-', ''), 17)}' + location: location + tags: tags + principalId: principalId + } +} + +// Container apps host (including container registry) +module containerApps 'core/host/container-apps.bicep' = { + name: 'container-apps' + params: { + name: 'app' + location: location + containerAppsEnvironmentName: 'cae-${prefix}' + containerRegistryName: 'cr${replace(prefix, '-', '')}' + logAnalyticsWorkspaceName: logAnalyticsWorkspace.outputs.name + } +} + +// API app +module api 'api.bicep' = { + name: 'api' + params: { + name: 'ca-${take(prefix,19)}' + location: location + imageName: apiImageName + containerAppsEnvironmentName: containerApps.outputs.environmentName + containerRegistryName: containerApps.outputs.registryName + keyVaultName: keyVault.outputs.name + } +} + + +module logAnalyticsWorkspace 'core/monitor/loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: 'log-${prefix}' + location: location + tags: tags + } +} + +output AZURE_LOCATION string = location +output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName +output AZURE_CONTAINER_REGISTRY_NAME string = containerApps.outputs.registryName +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerApps.outputs.registryLoginServer +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID +output SERVICE_API_NAME string = api.outputs.SERVICE_API_NAME +output SERVICE_API_URI string = api.outputs.SERVICE_API_URI +output SERVICE_API_IMAGE_NAME string = api.outputs.SERVICE_API_IMAGE_NAME +output SERVICE_API_ENDPOINTS array = ['${api.outputs.SERVICE_API_URI}/generate_name'] +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name diff --git a/Environments/ContainerApp/manifest.yaml b/Environments/ContainerApp/manifest.yaml new file mode 100644 index 00000000..e2c73f37 --- /dev/null +++ b/Environments/ContainerApp/manifest.yaml @@ -0,0 +1,14 @@ +name: simple-flask-api-container +version: 1.0.0 +summary: simple flask app with Container Apps +description: Deploy flask app to Container Apps +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: false + default: 'test' From 6a511361dfd1d5434b9ff3961ab8d66d35a91a17 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 10 May 2023 09:23:18 +0800 Subject: [PATCH 007/112] add Spring template --- Environments/Spring/README.md | 75 + Environments/Spring/abbreviations.json | 135 + Environments/Spring/app/app.bicep | 47 + Environments/Spring/azuredeploy.json | 2695 +++++++++++++++++ .../Spring/core/database/mysql/mysql-db.bicep | 50 + .../core/database/mysql/mysql-server.bicep | 121 + .../Spring/core/host/appservice.bicep | 97 + .../Spring/core/host/appserviceplan.bicep | 20 + .../applicationinsights-dashboard.bicep | 1235 ++++++++ .../core/monitor/applicationinsights.bicep | 30 + .../Spring/core/monitor/loganalytics.bicep | 21 + .../Spring/core/monitor/monitoring.bicep | 31 + .../core/security/keyvault-access.bicep | 21 + .../Spring/core/security/keyvault.bicep | 25 + Environments/Spring/main.bicep | 118 + Environments/Spring/manifest.yaml | 21 + 16 files changed, 4742 insertions(+) create mode 100644 Environments/Spring/README.md create mode 100644 Environments/Spring/abbreviations.json create mode 100644 Environments/Spring/app/app.bicep create mode 100644 Environments/Spring/azuredeploy.json create mode 100644 Environments/Spring/core/database/mysql/mysql-db.bicep create mode 100644 Environments/Spring/core/database/mysql/mysql-server.bicep create mode 100644 Environments/Spring/core/host/appservice.bicep create mode 100644 Environments/Spring/core/host/appserviceplan.bicep create mode 100644 Environments/Spring/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Spring/core/monitor/applicationinsights.bicep create mode 100644 Environments/Spring/core/monitor/loganalytics.bicep create mode 100644 Environments/Spring/core/monitor/monitoring.bicep create mode 100644 Environments/Spring/core/security/keyvault-access.bicep create mode 100644 Environments/Spring/core/security/keyvault.bicep create mode 100644 Environments/Spring/main.bicep create mode 100644 Environments/Spring/manifest.yaml diff --git a/Environments/Spring/README.md b/Environments/Spring/README.md new file mode 100644 index 00000000..861e65cc --- /dev/null +++ b/Environments/Spring/README.md @@ -0,0 +1,75 @@ +# Spring Petclinic +This template helps the developer quickly set up the infrastructure about Spring Petclinic for Java. + +This repo is just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/spring-petclinic-java-mysql.git) + +## Prerequisites +* Java 17 or later +* Maven +* [VSCode](https://code.visualstudio.com/) and its extension "Azure Tools" +* Azure Deployment Environment has provisioned the environment + +## QuickStart +1, Clone this [repo](https://github.com/luxu-ms/spring-petclinic-java-mysql.git) +2, Use VS Code to open the cloned folder +3, Right click the blank space in the VS Code Explorer and select "Deploy to Web App..." +4, Select subscription and App Service provisioned + +## How to verify +Go to the App service overview in Azure Portal, click the Default domain, there will be petclinic page. + +## Run application locally + +> NOTE: Azure Database for MySQL flexible servers don't allow connections from local machines by default for security. +> You must add current IP address of your local machine to the firewall rules in [Azure Portal](https://ms.portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.DBforMySQL%2Fservers) before running the application locally. + +![add firewall rule to allow local connections](readme.assests/add_mysql_firewall_rule.png) + +### VSCode +You can just launch the predefined `Debug PetClinic` configuration to run the application locally if you are using VSCode. + +![run application locally in VSCode](readme.assests/run_locally_vscode.png) + +### IntelliJ IDEA +You need to pass the environment variables below to the application first. Values of these +environment variables are all available in the `.azure/${Environment-Name}/.env` file if `azd provision` or `azd up ...` completes successfully. + +![setup environment variables in IntelliJ IDEA](readme.assests/run_locally_intellij.png) + +```properties +# activate `azure` and `mysql` spring profiles +SPRING_PROFILES_ACTIVE=azure,mysql +# Azure Application Insights connection string, for monitoring and logging +APPLICATIONINSIGHTS_CONNECTION_STRING=... +# Azure Key Vault endpoint, where the MySQL user password (${MYSQL_PASS}) is stored +AZURE_KEY_VAULT_ENDPOINT=... +# Azure Database for MySQL server jdbc url +MYSQL_URL=... +# Azure Database for MySQL server user name +MYSQL_USER=... +``` + +## Security + +### Roles + +This template creates a [managed identity](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) +for your app inside your Azure Active Directory tenant, and it is used to authenticate your app with Azure and other services +that support Azure AD authentication like Key Vault via access policies. You will see principalId referenced in the infrastructure +as code files, that refers to the id of the currently logged in Azure CLI user, which will be granted access policies and permissions +to run the application locally. To view your managed identity in the Azure Portal, follow these +[steps](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-view-managed-identity-service-principal-portal). + +### Key Vault + +This template uses [Azure Key Vault](https://docs.microsoft.com/azure/key-vault/general/overview) to securely store user password +for the provisioned Azure Database for MySQL flexible server. Key Vault is a cloud service for securely storing and accessing secrets +(API keys, passwords, certificates, cryptographic keys) and makes it simple to give other Azure services access to them. As you +continue developing your solution, you may add as many secrets to your Key Vault as you require. + + +## Credits + +This Spring microservices sample is forked from +[Azure-Samples/spring-petclinic-java-mysql](https://github.com/Azure-Samples/spring-petclinic-java-mysql). + diff --git a/Environments/Spring/abbreviations.json b/Environments/Spring/abbreviations.json new file mode 100644 index 00000000..a4fc9dfe --- /dev/null +++ b/Environments/Spring/abbreviations.json @@ -0,0 +1,135 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Spring/app/app.bicep b/Environments/Spring/app/app.bicep new file mode 100644 index 00000000..1ae114e4 --- /dev/null +++ b/Environments/Spring/app/app.bicep @@ -0,0 +1,47 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param allowedOrigins array = [] +param appCommandLine string = '' +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param keyVaultName string +param serviceName string = 'petclinic' + +@description('JVM runtime options. Use this instead of defining JAVA_OPTS manually on appSettings.') +param javaRuntimeOptions array = [] + +// applicationinsights-runtime-attach (and other plugins) that uses runtime attach +// require allowAttachSelf to be enabled on App Service. Otherwise, plugins will fail to attach +// on App Service. +var defaultJavaRuntimeOptions = [ '-Djdk.attach.allowAttachSelf=true' ] + +module app '../core/host/appservice.bicep' = { + name: '${name}-app-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + allowedOrigins: allowedOrigins + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + JAVA_OPTS: join( + concat( + javaRuntimeOptions, + defaultJavaRuntimeOptions), + ' ') + }) + keyVaultName: keyVaultName + runtimeName: 'java' + runtimeVersion: '17-java17' + scmDoBuildDuringDeployment: true + } +} + +output APP_IDENTITY_PRINCIPAL_ID string = app.outputs.identityPrincipalId +output APP_NAME string = app.outputs.name +output APP_URI string = app.outputs.uri diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json new file mode 100644 index 00000000..9c83940c --- /dev/null +++ b/Environments/Spring/azuredeploy.json @@ -0,0 +1,2695 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "1154694905938494061" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "defaultValue": "test", + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "appName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanName": { + "type": "string", + "defaultValue": "" + }, + "mySqlServerName": { + "type": "string", + "defaultValue": "" + }, + "mySqlServerAdminName": { + "type": "string", + "defaultValue": "petclinic" + }, + "mySqlServerAdminPassword": { + "type": "securestring" + }, + "mySqlDatabaseName": { + "type": "string", + "defaultValue": "petclinic" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "appserviceplan", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('appServicePlanName'))), createObject('value', parameters('appServicePlanName')), createObject('value', format('{0}{1}', variables('abbrs').webServerFarms, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "sku": { + "value": { + "name": "B1" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "1639156783194607123" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "kind": { + "type": "string", + "defaultValue": "" + }, + "reserved": { + "type": "bool", + "defaultValue": true + }, + "sku": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "kind": "[parameters('kind')]", + "properties": { + "reserved": "[parameters('reserved')]" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Web/serverfarms', parameters('name'))]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "6189478021468152767" + } + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "10960702046792783086" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "4618248294119261512" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "11251127820136102381" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "10766203656109589284" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "mysql-db", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "serverName": "[if(not(empty(parameters('mySqlServerName'))), createObject('value', parameters('mySqlServerName')), createObject('value', format('{0}{1}', variables('abbrs').dBforMySQLServers, variables('resourceToken'))))]", + "serverAdminName": { + "value": "[parameters('mySqlServerAdminName')]" + }, + "serverAdminPassword": { + "value": "[parameters('mySqlServerAdminPassword')]" + }, + "databaseName": "[if(not(empty(parameters('mySqlDatabaseName'))), createObject('value', parameters('mySqlDatabaseName')), createObject('value', 'petclinic'))]", + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "12732925979307758465" + } + }, + "parameters": { + "serverName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "serverAdminName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Database administrator login name" + } + }, + "serverAdminPasswordKey": { + "type": "string", + "defaultValue": "MYSQL-PASS" + }, + "serverAdminPassword": { + "type": "securestring", + "minLength": 8, + "metadata": { + "description": "Database administrator password" + } + } + }, + "resources": [ + { + "type": "Microsoft.DBforMySQL/flexibleServers/databases", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('databaseName'))]", + "properties": { + "charset": "utf8", + "collation": "utf8_general_ci" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'mysql-server')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "mysql-server", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('serverName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "adminName": { + "value": "[parameters('serverAdminName')]" + }, + "adminPassword": { + "value": "[parameters('serverAdminPassword')]" + }, + "adminPasswordKey": { + "value": "[parameters('serverAdminPasswordKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "6228396417467428730" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Server Name for Azure database for MySQL" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "adminName": { + "type": "string", + "defaultValue": "mySqlAdmin", + "minLength": 1, + "metadata": { + "description": "Database administrator login name" + } + }, + "adminPasswordKey": { + "type": "string", + "defaultValue": "MYSQL-PASS" + }, + "adminPassword": { + "type": "securestring", + "minLength": 8, + "metadata": { + "description": "Database administrator password" + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard_B1s", + "metadata": { + "description": "Azure database for MySQL sku name " + } + }, + "autoGrow": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Enable Storage Auto Grow or not" + }, + "allowedValues": [ + "Enabled", + "Disabled" + ] + }, + "storageSizeGB": { + "type": "int", + "defaultValue": 20, + "metadata": { + "description": "Azure database for MySQL storage Size " + } + }, + "storageIops": { + "type": "int", + "defaultValue": 360, + "metadata": { + "description": "Azure database for MySQL storage Iops" + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Burstable", + "allowedValues": [ + "GeneralPurpose", + "MemoryOptimized", + "Burstable" + ], + "metadata": { + "description": "Azure database for MySQL pricing tier" + } + }, + "version": { + "type": "string", + "defaultValue": "8.0.21", + "allowedValues": [ + "5.7", + "8.0.21" + ], + "metadata": { + "description": "MySQL version" + } + }, + "backupRetentionDays": { + "type": "int", + "defaultValue": 7, + "metadata": { + "description": "MySQL Server backup retention days" + } + }, + "geoRedundantBackup": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Geo-Redundant Backup setting" + } + }, + "highAvailabilityMode": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "ZoneRedundant", + "SameZone" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DBforMySQL/flexibleServers", + "apiVersion": "2021-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "properties": { + "administratorLogin": "[parameters('adminName')]", + "administratorLoginPassword": "[parameters('adminPassword')]", + "storage": { + "autoGrow": "[parameters('autoGrow')]", + "iops": "[parameters('storageIops')]", + "storageSizeGB": "[parameters('storageSizeGB')]" + }, + "createMode": "Default", + "version": "[parameters('version')]", + "backup": { + "backupRetentionDays": "[parameters('backupRetentionDays')]", + "geoRedundantBackup": "[parameters('geoRedundantBackup')]" + }, + "highAvailability": { + "mode": "[parameters('highAvailabilityMode')]" + } + } + }, + { + "type": "Microsoft.DBforMySQL/flexibleServers/firewallRules", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', parameters('name'), 'AllowAzureIPs')]", + "properties": { + "startIpAddress": "0.0.0.0", + "endIpAddress": "0.0.0.0" + }, + "dependsOn": [ + "[resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name'))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('adminPasswordKey'))]", + "properties": { + "value": "[parameters('adminPassword')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "adminName": { + "type": "string", + "value": "[parameters('adminName')]" + }, + "adminPasswordKey": { + "type": "string", + "value": "[parameters('adminPasswordKey')]" + }, + "fullyQualifiedDomainName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name')), '2021-05-01').fullyQualifiedDomainName]" + }, + "endpoint": { + "type": "string", + "value": "[format('jdbc:mysql://{0}:3306/?useSSL=true&requireSSL=false', reference(resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name')), '2021-05-01').fullyQualifiedDomainName)]" + } + } + } + } + } + ], + "outputs": { + "serverAdminPasswordKey": { + "type": "string", + "value": "[parameters('serverAdminPasswordKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[format('jdbc:mysql://{0}:3306/{1}?useSSL=true&requireSSL=false', reference(resourceId('Microsoft.Resources/deployments', 'mysql-server'), '2022-09-01').outputs.fullyQualifiedDomainName.value, parameters('databaseName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "app", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('appName'))), createObject('value', parameters('appName')), createObject('value', format('{0}petclinic-{1}', variables('abbrs').webSitesAppService, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "appServicePlanId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'appserviceplan'), '2022-09-01').outputs.id.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "appSettings": { + "value": { + "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]", + "AZURE_KEY_VAULT_ENDPOINT": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]", + "SPRING_PROFILES_ACTIVE": "azure,mysql", + "MYSQL_URL": "[reference(resourceId('Microsoft.Resources/deployments', 'mysql-db'), '2022-09-01').outputs.endpoint.value]", + "MYSQL_USER": "[parameters('mySqlServerAdminName')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "2283778516050253977" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "appCommandLine": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "petclinic" + }, + "javaRuntimeOptions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "JVM runtime options. Use this instead of defining JAVA_OPTS manually on appSettings." + } + } + }, + "variables": { + "defaultJavaRuntimeOptions": [ + "-Djdk.attach.allowAttachSelf=true" + ] + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-app-module', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "allowedOrigins": { + "value": "[parameters('allowedOrigins')]" + }, + "appCommandLine": { + "value": "[parameters('appCommandLine')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + }, + "appServicePlanId": { + "value": "[parameters('appServicePlanId')]" + }, + "appSettings": { + "value": "[union(parameters('appSettings'), createObject('JAVA_OPTS', join(concat(parameters('javaRuntimeOptions'), variables('defaultJavaRuntimeOptions')), ' ')))]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "runtimeName": { + "value": "java" + }, + "runtimeVersion": { + "value": "17-java17" + }, + "scmDoBuildDuringDeployment": { + "value": true + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "4560700392891195594" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "managedIdentity": { + "type": "bool", + "defaultValue": "[not(empty(parameters('keyVaultName')))]" + }, + "runtimeName": { + "type": "string", + "allowedValues": [ + "dotnet", + "dotnetcore", + "dotnet-isolated", + "node", + "python", + "java", + "powershell", + "custom" + ] + }, + "runtimeNameAndVersion": { + "type": "string", + "defaultValue": "[format('{0}|{1}', parameters('runtimeName'), parameters('runtimeVersion'))]" + }, + "runtimeVersion": { + "type": "string" + }, + "kind": { + "type": "string", + "defaultValue": "app,linux" + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "alwaysOn": { + "type": "bool", + "defaultValue": true + }, + "appCommandLine": { + "type": "string", + "defaultValue": "" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": false + }, + "enableOryxBuild": { + "type": "bool", + "defaultValue": "[contains(parameters('kind'), 'linux')]" + }, + "functionAppScaleLimit": { + "type": "int", + "defaultValue": -1 + }, + "linuxFxVersion": { + "type": "string", + "defaultValue": "[parameters('runtimeNameAndVersion')]" + }, + "minimumElasticInstanceCount": { + "type": "int", + "defaultValue": -1 + }, + "numberOfWorkers": { + "type": "int", + "defaultValue": -1 + }, + "scmDoBuildDuringDeployment": { + "type": "bool", + "defaultValue": false + }, + "use32BitWorkerProcess": { + "type": "bool", + "defaultValue": false + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'appsettings')]", + "properties": "[union(parameters('appSettings'), createObject('SCM_DO_BUILD_DURING_DEPLOYMENT', string(parameters('scmDoBuildDuringDeployment')), 'ENABLE_ORYX_BUILD', string(parameters('enableOryxBuild'))), if(not(empty(parameters('applicationInsightsName'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString), createObject()), if(not(empty(parameters('keyVaultName'))), createObject('AZURE_KEY_VAULT_ENDPOINT', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri), createObject()))]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'logs')]", + "properties": { + "applicationLogs": { + "fileSystem": { + "level": "Verbose" + } + }, + "detailedErrorMessages": { + "enabled": true + }, + "failedRequestsTracing": { + "enabled": true + }, + "httpLogs": { + "fileSystem": { + "enabled": true, + "retentionInDays": 1, + "retentionInMb": 35 + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]", + "[resourceId('Microsoft.Web/sites/config', parameters('name'), 'appsettings')]" + ] + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "serverFarmId": "[parameters('appServicePlanId')]", + "siteConfig": { + "linuxFxVersion": "[parameters('linuxFxVersion')]", + "alwaysOn": "[parameters('alwaysOn')]", + "ftpsState": "FtpsOnly", + "appCommandLine": "[parameters('appCommandLine')]", + "numberOfWorkers": "[if(not(equals(parameters('numberOfWorkers'), -1)), parameters('numberOfWorkers'), null())]", + "minimumElasticInstanceCount": "[if(not(equals(parameters('minimumElasticInstanceCount'), -1)), parameters('minimumElasticInstanceCount'), null())]", + "use32BitWorkerProcess": "[parameters('use32BitWorkerProcess')]", + "functionAppScaleLimit": "[if(not(equals(parameters('functionAppScaleLimit'), -1)), parameters('functionAppScaleLimit'), null())]", + "cors": { + "allowedOrigins": "[union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins'))]" + } + }, + "clientAffinityEnabled": "[parameters('clientAffinityEnabled')]", + "httpsOnly": true + }, + "identity": { + "type": "[if(parameters('managedIdentity'), 'SystemAssigned', 'None')]" + } + } + ], + "outputs": { + "identityPrincipalId": { + "type": "string", + "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01', 'full').identity.principalId, '')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "value": "[format('https://{0}', reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01').defaultHostName)]" + } + } + } + } + } + ], + "outputs": { + "APP_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-app-module', parameters('name'))), '2022-09-01').outputs.identityPrincipalId.value]" + }, + "APP_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-app-module', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "APP_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-app-module', parameters('name'))), '2022-09-01').outputs.uri.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'appserviceplan')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]", + "[resourceId('Microsoft.Resources/deployments', 'mysql-db')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "app-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app'), '2022-09-01').outputs.APP_IDENTITY_PRINCIPAL_ID.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "1928598198302569198" + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'app')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + } + ], + "outputs": { + "MYSQL_URL": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'mysql-db'), '2022-09-01').outputs.endpoint.value]" + }, + "MYSQL_USER": { + "type": "string", + "value": "[parameters('mySqlServerAdminName')]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "SPRING_PROFILES_ACTIVE": { + "type": "string", + "value": "azure,mysql" + } + } +} \ No newline at end of file diff --git a/Environments/Spring/core/database/mysql/mysql-db.bicep b/Environments/Spring/core/database/mysql/mysql-db.bicep new file mode 100644 index 00000000..a95cd8ad --- /dev/null +++ b/Environments/Spring/core/database/mysql/mysql-db.bicep @@ -0,0 +1,50 @@ +param serverName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +@description('Database administrator login name') +@minLength(1) +param serverAdminName string + +// this is not the password, but the key used to load password from Key Vault +#disable-next-line secure-secrets-in-params +param serverAdminPasswordKey string = 'MYSQL-PASS' + +@description('Database administrator password') +@minLength(8) +@secure() +param serverAdminPassword string + +// The database server +module server 'mysql-server.bicep' = { + name: 'mysql-server' + params: { + name: serverName + location: location + tags: tags + adminName: serverAdminName + adminPassword: serverAdminPassword + adminPasswordKey: serverAdminPasswordKey + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DBforMySQL/flexibleServers/databases@2021-05-01' = { + name: '${serverName}/${databaseName}' + properties: { + charset: 'utf8' + collation: 'utf8_general_ci' + } + + dependsOn: [ + server + ] +} + +// this is not the password, but the key used to load password from Key Vault +#disable-next-line outputs-should-not-contain-secrets +output serverAdminPasswordKey string = serverAdminPasswordKey +output databaseName string = databaseName +output endpoint string = 'jdbc:mysql://${server.outputs.fullyQualifiedDomainName}:3306/${databaseName}?useSSL=true&requireSSL=false' diff --git a/Environments/Spring/core/database/mysql/mysql-server.bicep b/Environments/Spring/core/database/mysql/mysql-server.bicep new file mode 100644 index 00000000..5cf26493 --- /dev/null +++ b/Environments/Spring/core/database/mysql/mysql-server.bicep @@ -0,0 +1,121 @@ +@description('Server Name for Azure database for MySQL') +param name string +@description('Location for all resources.') +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +@description('Database administrator login name') +@minLength(1) +param adminName string = 'mySqlAdmin' + +// this is not the password, but the key used to load password from Key Vault +#disable-next-line secure-secrets-in-params +param adminPasswordKey string = 'MYSQL-PASS' + +@description('Database administrator password') +@minLength(8) +@secure() +param adminPassword string + +@description('Azure database for MySQL sku name ') +param skuName string = 'Standard_B1s' + +@allowed([ + 'Enabled' + 'Disabled' +]) +@description('Enable Storage Auto Grow or not') +param autoGrow string = 'Enabled' + +@description('Azure database for MySQL storage Size ') +param storageSizeGB int = 20 + +@description('Azure database for MySQL storage Iops') +param storageIops int = 360 + +@description('Azure database for MySQL pricing tier') +@allowed([ + 'GeneralPurpose' + 'MemoryOptimized' + 'Burstable' +]) +param skuTier string = 'Burstable' + +@description('MySQL version') +@allowed([ + '5.7' + '8.0.21' +]) +param version string = '8.0.21' + +@description('MySQL Server backup retention days') +param backupRetentionDays int = 7 + +@description('Geo-Redundant Backup setting') +param geoRedundantBackup string = 'Disabled' + +@allowed([ + 'Disabled' + 'ZoneRedundant' + 'SameZone' +]) +param highAvailabilityMode string = 'Disabled' + +resource server 'Microsoft.DBforMySQL/flexibleServers@2021-05-01' = { + name: name + location: location + tags: tags + sku: { + name: skuName + tier: skuTier + } + properties: { + administratorLogin: adminName + administratorLoginPassword: adminPassword + storage: { + autoGrow: autoGrow + iops: storageIops + storageSizeGB: storageSizeGB + } + createMode: 'Default' + version: version + backup: { + backupRetentionDays: backupRetentionDays + geoRedundantBackup: geoRedundantBackup + } + highAvailability: { + mode: highAvailabilityMode + } + } +} + +resource firewallRuleAllowAllAzureIps 'Microsoft.DBforMySQL/flexibleServers/firewallRules@2021-05-01' = { + parent: server + name: 'AllowAzureIPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } +} + +resource mySqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: adminPasswordKey + properties: { + value: adminPassword + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output name string = server.name +output adminName string = adminName +// this is not the password, but the key used to load password from Key Vault +#disable-next-line outputs-should-not-contain-secrets +output adminPasswordKey string = adminPasswordKey +output fullyQualifiedDomainName string = server.properties.fullyQualifiedDomainName +output endpoint string = 'jdbc:mysql://${server.properties.fullyQualifiedDomainName}:3306/?useSSL=true&requireSSL=false' diff --git a/Environments/Spring/core/host/appservice.bicep b/Environments/Spring/core/host/appservice.bicep new file mode 100644 index 00000000..62e34a65 --- /dev/null +++ b/Environments/Spring/core/host/appservice.bicep @@ -0,0 +1,97 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: 'FtpsOnly' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configAppSettings 'config' = { + name: 'appsettings' + properties: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [ + configAppSettings + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Spring/core/host/appserviceplan.bicep b/Environments/Spring/core/host/appserviceplan.bicep new file mode 100644 index 00000000..69c35d78 --- /dev/null +++ b/Environments/Spring/core/host/appserviceplan.bicep @@ -0,0 +1,20 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id diff --git a/Environments/Spring/core/monitor/applicationinsights-dashboard.bicep b/Environments/Spring/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..b7af2c1a --- /dev/null +++ b/Environments/Spring/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1235 @@ +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Spring/core/monitor/applicationinsights.bicep b/Environments/Spring/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..f76b292b --- /dev/null +++ b/Environments/Spring/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Spring/core/monitor/loganalytics.bicep b/Environments/Spring/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..770544cc --- /dev/null +++ b/Environments/Spring/core/monitor/loganalytics.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Spring/core/monitor/monitoring.bicep b/Environments/Spring/core/monitor/monitoring.bicep new file mode 100644 index 00000000..96ba11e5 --- /dev/null +++ b/Environments/Spring/core/monitor/monitoring.bicep @@ -0,0 +1,31 @@ +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Spring/core/security/keyvault-access.bicep b/Environments/Spring/core/security/keyvault-access.bicep new file mode 100644 index 00000000..96c9cf73 --- /dev/null +++ b/Environments/Spring/core/security/keyvault-access.bicep @@ -0,0 +1,21 @@ +param name string = 'add' + +param keyVaultName string = '' +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Spring/core/security/keyvault.bicep b/Environments/Spring/core/security/keyvault.bicep new file mode 100644 index 00000000..0eb4a86d --- /dev/null +++ b/Environments/Spring/core/security/keyvault.bicep @@ -0,0 +1,25 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Spring/main.bicep b/Environments/Spring/main.bicep new file mode 100644 index 00000000..445798b6 --- /dev/null +++ b/Environments/Spring/main.bicep @@ -0,0 +1,118 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string = 'test' + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +param appName string = '' +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param appServicePlanName string = '' +param mySqlServerName string = '' +param mySqlServerAdminName string = 'petclinic' +@secure() +param mySqlServerAdminPassword string +param mySqlDatabaseName string = 'petclinic' +param keyVaultName string = '' +param logAnalyticsName string = '' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) +var tags = { 'azd-env-name': environmentName } + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan './core/host/appserviceplan.bicep' = { + name: 'appserviceplan' + params: { + name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' + location: location + tags: tags + sku: { + name: 'B1' + } + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// The application database +module mySql './core/database/mysql/mysql-db.bicep' = { + name: 'mysql-db' + params: { + location: location + tags: tags + serverName: !empty(mySqlServerName) ? mySqlServerName : '${abbrs.dBforMySQLServers}${resourceToken}' + serverAdminName: mySqlServerAdminName + serverAdminPassword: mySqlServerAdminPassword + databaseName: !empty(mySqlDatabaseName) ? mySqlDatabaseName : 'petclinic' + keyVaultName: keyVault.outputs.name + } +} + +// The application backend +module app './app/app.bicep' = { + name: 'app' + params: { + name: !empty(appName) ? appName : '${abbrs.webSitesAppService}petclinic-${resourceToken}' + location: location + tags: tags + applicationInsightsName: monitoring.outputs.applicationInsightsName + appServicePlanId: appServicePlan.outputs.id + keyVaultName: keyVault.outputs.name + appSettings: { + APPLICATIONINSIGHTS_CONNECTION_STRING: monitoring.outputs.applicationInsightsConnectionString + AZURE_KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint + SPRING_PROFILES_ACTIVE: 'azure,mysql' + MYSQL_URL: mySql.outputs.endpoint + MYSQL_USER: mySqlServerAdminName + } + } +} + +// Give the API access to KeyVault +module appKeyVaultAccess './core/security/keyvault-access.bicep' = { + name: 'app-keyvault-access' + params: { + keyVaultName: keyVault.outputs.name + principalId: app.outputs.APP_IDENTITY_PRINCIPAL_ID + } +} + +// Data outputs +output MYSQL_URL string = mySql.outputs.endpoint +output MYSQL_USER string = mySqlServerAdminName + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output SPRING_PROFILES_ACTIVE string = 'azure,mysql' diff --git a/Environments/Spring/manifest.yaml b/Environments/Spring/manifest.yaml new file mode 100644 index 00000000..05e4d884 --- /dev/null +++ b/Environments/Spring/manifest.yaml @@ -0,0 +1,21 @@ +name: spring-petclinic-java-mysql +version: 1.0.0 +summary: Spring Petclinic +description: Deploy Spring Petlinic Java app with MySQL +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: false + default: 'test' + + - id: mySqlServerAdminPassword + name: mySqlServerAdminPassword + description: 'MySQL Admin Password' + type: string + required: true + From f4d0262689741e2e9c51d0bee7fc8c6f975bda39 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 10 May 2023 09:26:13 +0800 Subject: [PATCH 008/112] add AKS template --- Environments/AKS/README.md | 4 + Environments/AKS/abbreviations.json | 135 + Environments/AKS/app/db.bicep | 40 + Environments/AKS/azuredeploy.json | 3136 +++++++++++++++++ .../AKS/core/ai/cognitiveservices.bicep | 38 + .../core/database/cosmos/cosmos-account.bicep | 48 + .../cosmos/mongo/cosmos-mongo-account.bicep | 22 + .../cosmos/mongo/cosmos-mongo-db.bicep | 46 + .../cosmos/sql/cosmos-sql-account.bicep | 21 + .../database/cosmos/sql/cosmos-sql-db.bicep | 73 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 18 + .../cosmos/sql/cosmos-sql-role-def.bicep | 29 + .../database/postgresql/flexibleserver.bicep | 64 + .../core/database/sqlserver/sqlserver.bicep | 129 + Environments/AKS/core/gateway/apim.bicep | 78 + .../AKS/core/host/aks-agent-pool.bicep | 17 + .../AKS/core/host/aks-managed-cluster.bicep | 139 + Environments/AKS/core/host/aks.bicep | 213 ++ .../core/host/appservice-appsettings.bicep | 15 + Environments/AKS/core/host/appservice.bicep | 101 + .../AKS/core/host/appserviceplan.bicep | 21 + .../AKS/core/host/container-app.bicep | 78 + .../host/container-apps-environment.bicep | 27 + .../AKS/core/host/container-apps.bicep | 31 + .../AKS/core/host/container-registry.bicep | 67 + Environments/AKS/core/host/functions.bicep | 82 + Environments/AKS/core/host/staticwebapp.bicep | 21 + .../applicationinsights-dashboard.bicep | 1235 +++++++ .../core/monitor/applicationinsights.bicep | 30 + .../AKS/core/monitor/loganalytics.bicep | 21 + .../AKS/core/monitor/monitoring.bicep | 31 + .../AKS/core/networking/cdn-endpoint.bicep | 51 + .../AKS/core/networking/cdn-profile.bicep | 33 + Environments/AKS/core/networking/cdn.bicep | 42 + .../AKS/core/search/search-services.bicep | 62 + .../AKS/core/security/keyvault-access.bicep | 21 + .../AKS/core/security/keyvault-secret.bicep | 30 + Environments/AKS/core/security/keyvault.bicep | 25 + .../AKS/core/security/registry-access.bicep | 18 + Environments/AKS/core/security/role.bicep | 20 + .../AKS/core/storage/storage-account.bicep | 61 + Environments/AKS/main.bicep | 91 + Environments/AKS/manifest.yaml | 21 + 43 files changed, 6485 insertions(+) create mode 100644 Environments/AKS/README.md create mode 100644 Environments/AKS/abbreviations.json create mode 100644 Environments/AKS/app/db.bicep create mode 100644 Environments/AKS/azuredeploy.json create mode 100644 Environments/AKS/core/ai/cognitiveservices.bicep create mode 100644 Environments/AKS/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/AKS/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/AKS/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/AKS/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/AKS/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/AKS/core/gateway/apim.bicep create mode 100644 Environments/AKS/core/host/aks-agent-pool.bicep create mode 100644 Environments/AKS/core/host/aks-managed-cluster.bicep create mode 100644 Environments/AKS/core/host/aks.bicep create mode 100644 Environments/AKS/core/host/appservice-appsettings.bicep create mode 100644 Environments/AKS/core/host/appservice.bicep create mode 100644 Environments/AKS/core/host/appserviceplan.bicep create mode 100644 Environments/AKS/core/host/container-app.bicep create mode 100644 Environments/AKS/core/host/container-apps-environment.bicep create mode 100644 Environments/AKS/core/host/container-apps.bicep create mode 100644 Environments/AKS/core/host/container-registry.bicep create mode 100644 Environments/AKS/core/host/functions.bicep create mode 100644 Environments/AKS/core/host/staticwebapp.bicep create mode 100644 Environments/AKS/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/AKS/core/monitor/applicationinsights.bicep create mode 100644 Environments/AKS/core/monitor/loganalytics.bicep create mode 100644 Environments/AKS/core/monitor/monitoring.bicep create mode 100644 Environments/AKS/core/networking/cdn-endpoint.bicep create mode 100644 Environments/AKS/core/networking/cdn-profile.bicep create mode 100644 Environments/AKS/core/networking/cdn.bicep create mode 100644 Environments/AKS/core/search/search-services.bicep create mode 100644 Environments/AKS/core/security/keyvault-access.bicep create mode 100644 Environments/AKS/core/security/keyvault-secret.bicep create mode 100644 Environments/AKS/core/security/keyvault.bicep create mode 100644 Environments/AKS/core/security/registry-access.bicep create mode 100644 Environments/AKS/core/security/role.bicep create mode 100644 Environments/AKS/core/storage/storage-account.bicep create mode 100644 Environments/AKS/main.bicep create mode 100644 Environments/AKS/manifest.yaml diff --git a/Environments/AKS/README.md b/Environments/AKS/README.md new file mode 100644 index 00000000..184975c1 --- /dev/null +++ b/Environments/AKS/README.md @@ -0,0 +1,4 @@ +# AKS +This template helps the developer quickly set up the infrastructure about AKS. + +This repo is just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/todo-nodejs-mongo-aks.git) \ No newline at end of file diff --git a/Environments/AKS/abbreviations.json b/Environments/AKS/abbreviations.json new file mode 100644 index 00000000..a4fc9dfe --- /dev/null +++ b/Environments/AKS/abbreviations.json @@ -0,0 +1,135 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/AKS/app/db.bicep b/Environments/AKS/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/AKS/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json new file mode 100644 index 00000000..c6449186 --- /dev/null +++ b/Environments/AKS/azuredeploy.json @@ -0,0 +1,3136 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5597674447914299578" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().id]", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the AKS cluster" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the Container Registry (ACR)" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "2584495159523716195" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name for the Azure container registry (ACR)" + } + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the connected log analytics workspace" + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the keyvault to grant access" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "addOns": { + "type": "object", + "defaultValue": { + "azurePolicy": { + "enabled": true, + "config": { + "version": "v2" + } + }, + "keyVault": { + "enabled": true, + "config": { + "enableSecretRotation": "true", + "rotationPollInterval": "2m" + } + }, + "openServiceMesh": { + "enabled": false, + "config": {} + }, + "omsAgent": { + "enabled": true, + "config": {} + }, + "applicationGateway": { + "enabled": false, + "config": {} + } + }, + "metadata": { + "description": "AKS add-ons configuration" + } + }, + "systemPoolType": { + "type": "string", + "defaultValue": "CostOptimised", + "metadata": { + "description": "The System Pool Preset sizing" + }, + "allowedValues": [ + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ] + }, + "agentPoolType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The User Pool Preset sizing" + }, + "allowedValues": [ + "", + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ] + }, + "systemPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of system node pool" + } + }, + "agentPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of user node pool" + } + } + }, + "variables": { + "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", + "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", + "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", + "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", + "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", + "nodePoolBase": { + "osType": "Linux", + "maxPods": 30, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "33%" + } + }, + "nodePoolPresets": { + "CostOptimised": { + "vmSize": "Standard_B4ms", + "count": 1, + "minCount": 1, + "maxCount": 3, + "enableAutoScaling": true, + "availabilityZones": [] + }, + "Standard": { + "vmSize": "Standard_DS2_v2", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + }, + "HighSpec": { + "vmSize": "Standard_D4s_v3", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "managed-cluster", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" + }, + "addOns": { + "value": "[variables('addOnsConfig')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "12976609032349838131" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.25.5", + "metadata": { + "description": "Kubernetes Version" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-02-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPolicy": "[parameters('networkPolicy')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webAppRoutingAddon')]" + } + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-02-01').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-02-01').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-02-01').identityProfile.kubeletidentity.resourceId]" + } + } + } + } + } + }, + { + "condition": "[variables('hasAgentPool')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks-node-pool", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "name": { + "value": "npuserpool" + }, + "config": { + "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "7351126758431238881" + } + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "name": { + "type": "string", + "metadata": { + "description": "The agent pool name" + } + }, + "config": { + "type": "object", + "metadata": { + "description": "The agent pool configuration" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2023-01-02-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": "[parameters('config')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "13010360132212368737" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": true + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices" + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled" + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled" + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-container-registry-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "13019144734616357723" + } + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(parameters('principalId'), 'Acr', variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5134893917065565335" + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The resource name of the ACR" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The login server for the container registry" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "6363297596017193433" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "8513104519886724643" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "16707878505366027072" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "6129206474152081358" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.0'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "10766203656109589284" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "6189478021468152767" + } + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "10960702046792783086" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "4618248294119261512" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "11251127820136102381" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_AKS_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/AKS/core/ai/cognitiveservices.bicep b/Environments/AKS/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..e0afb877 --- /dev/null +++ b/Environments/AKS/core/ai/cognitiveservices.bicep @@ -0,0 +1,38 @@ +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2022-10-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + scaleSettings: deployment.scaleSettings + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/AKS/core/database/cosmos/cosmos-account.bicep b/Environments/AKS/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6bc1f2eb --- /dev/null +++ b/Environments/AKS/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,48 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..bd2a2b5f --- /dev/null +++ b/Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,22 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2c9688e0 --- /dev/null +++ b/Environments/AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,46 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/AKS/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..e8b030f6 --- /dev/null +++ b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/AKS/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..5a4de209 --- /dev/null +++ b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,73 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefintion 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefintion.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefintion.outputs.id diff --git a/Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..6855edfe --- /dev/null +++ b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,18 @@ +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..cfb40335 --- /dev/null +++ b/Environments/AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,29 @@ +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/AKS/core/database/postgresql/flexibleserver.bicep b/Environments/AKS/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..effda63d --- /dev/null +++ b/Environments/AKS/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,64 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/AKS/core/database/sqlserver/sqlserver.bicep b/Environments/AKS/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..64477a74 --- /dev/null +++ b/Environments/AKS/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,129 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/AKS/core/gateway/apim.bicep b/Environments/AKS/core/gateway/apim.bicep new file mode 100644 index 00000000..64c958cd --- /dev/null +++ b/Environments/AKS/core/gateway/apim.bicep @@ -0,0 +1,78 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/AKS/core/host/aks-agent-pool.bicep b/Environments/AKS/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..f6ff43a9 --- /dev/null +++ b/Environments/AKS/core/host/aks-agent-pool.bicep @@ -0,0 +1,17 @@ +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-01-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-01-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/AKS/core/host/aks-managed-cluster.bicep b/Environments/AKS/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..b189af8e --- /dev/null +++ b/Environments/AKS/core/host/aks-managed-cluster.bicep @@ -0,0 +1,139 @@ +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.25.5' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = '' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-02-01' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/AKS/core/host/aks.bicep b/Environments/AKS/core/host/aks.bicep new file mode 100644 index 00000000..f2f42066 --- /dev/null +++ b/Environments/AKS/core/host/aks.bicep @@ -0,0 +1,213 @@ +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/AKS/core/host/appservice-appsettings.bicep b/Environments/AKS/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..2286e1f5 --- /dev/null +++ b/Environments/AKS/core/host/appservice-appsettings.bicep @@ -0,0 +1,15 @@ +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/AKS/core/host/appservice.bicep b/Environments/AKS/core/host/appservice.bicep new file mode 100644 index 00000000..ad58765e --- /dev/null +++ b/Environments/AKS/core/host/appservice.bicep @@ -0,0 +1,101 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + } +} + +module config 'appservice-appsettings.bicep' = if (!empty(appSettings)) { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/AKS/core/host/appserviceplan.bicep b/Environments/AKS/core/host/appserviceplan.bicep new file mode 100644 index 00000000..c444f406 --- /dev/null +++ b/Environments/AKS/core/host/appserviceplan.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/AKS/core/host/container-app.bicep b/Environments/AKS/core/host/container-app.bicep new file mode 100644 index 00000000..119fab30 --- /dev/null +++ b/Environments/AKS/core/host/container-app.bicep @@ -0,0 +1,78 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerName string = 'main' +param containerRegistryName string +param env array = [] +param external bool = true +param imageName string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param targetPort int = 80 + +@description('CPU cores allocated to a single container instance, e.g. 0.5') +param containerCpuCoreCount string = '0.5' + +@description('Memory allocated to a single container instance, e.g. 1Gi') +param containerMemory string = '1.0Gi' + +resource app 'Microsoft.App/containerApps@2022-03-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: 'single' + ingress: { + external: external + targetPort: targetPort + transport: 'auto' + } + secrets: [ + { + name: 'registry-password' + value: containerRegistry.listCredentials().passwords[0].value + } + ] + registries: [ + { + server: '${containerRegistry.name}.azurecr.io' + username: containerRegistry.name + passwordSecretRef: 'registry-password' + } + ] + } + template: { + containers: [ + { + image: imageName + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { + name: containerAppsEnvironmentName +} + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = managedIdentity ? app.identity.principalId : '' +output imageName string = imageName +output name string = app.name +output uri string = 'https://${app.properties.configuration.ingress.fqdn}' diff --git a/Environments/AKS/core/host/container-apps-environment.bicep b/Environments/AKS/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..dbe953d2 --- /dev/null +++ b/Environments/AKS/core/host/container-apps-environment.bicep @@ -0,0 +1,27 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output name string = containerAppsEnvironment.name diff --git a/Environments/AKS/core/host/container-apps.bicep b/Environments/AKS/core/host/container-apps.bicep new file mode 100644 index 00000000..245ed7f2 --- /dev/null +++ b/Environments/AKS/core/host/container-apps.bicep @@ -0,0 +1,31 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param logAnalyticsWorkspaceName string + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/AKS/core/host/container-registry.bicep b/Environments/AKS/core/host/container-registry.bicep new file mode 100644 index 00000000..c0ba201b --- /dev/null +++ b/Environments/AKS/core/host/container-registry.bicep @@ -0,0 +1,67 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param adminUserEnabled bool = true +param anonymousPullEnabled bool = false +param dataEndpointEnabled bool = false +param encryption object = { + status: 'disabled' +} +param networkRuleBypassOptions string = 'AzureServices' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'Basic' +} +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/AKS/core/host/functions.bicep b/Environments/AKS/core/host/functions.bicep new file mode 100644 index 00000000..28a581bd --- /dev/null +++ b/Environments/AKS/core/host/functions.bicep @@ -0,0 +1,82 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/AKS/core/host/staticwebapp.bicep b/Environments/AKS/core/host/staticwebapp.bicep new file mode 100644 index 00000000..91c2d0db --- /dev/null +++ b/Environments/AKS/core/host/staticwebapp.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/AKS/core/monitor/applicationinsights-dashboard.bicep b/Environments/AKS/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..b7af2c1a --- /dev/null +++ b/Environments/AKS/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1235 @@ +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/AKS/core/monitor/applicationinsights.bicep b/Environments/AKS/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..f76b292b --- /dev/null +++ b/Environments/AKS/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/AKS/core/monitor/loganalytics.bicep b/Environments/AKS/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..770544cc --- /dev/null +++ b/Environments/AKS/core/monitor/loganalytics.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/AKS/core/monitor/monitoring.bicep b/Environments/AKS/core/monitor/monitoring.bicep new file mode 100644 index 00000000..96ba11e5 --- /dev/null +++ b/Environments/AKS/core/monitor/monitoring.bicep @@ -0,0 +1,31 @@ +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/AKS/core/networking/cdn-endpoint.bicep b/Environments/AKS/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..e92ee893 --- /dev/null +++ b/Environments/AKS/core/networking/cdn-endpoint.bicep @@ -0,0 +1,51 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/AKS/core/networking/cdn-profile.bicep b/Environments/AKS/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..8d70f54c --- /dev/null +++ b/Environments/AKS/core/networking/cdn-profile.bicep @@ -0,0 +1,33 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/AKS/core/networking/cdn.bicep b/Environments/AKS/core/networking/cdn.bicep new file mode 100644 index 00000000..2177c19f --- /dev/null +++ b/Environments/AKS/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +// Module to create a CDN profile with a single endpoint +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/AKS/core/search/search-services.bicep b/Environments/AKS/core/search/search-services.bicep new file mode 100644 index 00000000..399a8f3f --- /dev/null +++ b/Environments/AKS/core/search/search-services.bicep @@ -0,0 +1,62 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name diff --git a/Environments/AKS/core/security/keyvault-access.bicep b/Environments/AKS/core/security/keyvault-access.bicep new file mode 100644 index 00000000..aa989ebd --- /dev/null +++ b/Environments/AKS/core/security/keyvault-access.bicep @@ -0,0 +1,21 @@ +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/AKS/core/security/keyvault-secret.bicep b/Environments/AKS/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..5f786ce5 --- /dev/null +++ b/Environments/AKS/core/security/keyvault-secret.bicep @@ -0,0 +1,30 @@ +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/AKS/core/security/keyvault.bicep b/Environments/AKS/core/security/keyvault.bicep new file mode 100644 index 00000000..0eb4a86d --- /dev/null +++ b/Environments/AKS/core/security/keyvault.bicep @@ -0,0 +1,25 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/AKS/core/security/registry-access.bicep b/Environments/AKS/core/security/registry-access.bicep new file mode 100644 index 00000000..056bd6c3 --- /dev/null +++ b/Environments/AKS/core/security/registry-access.bicep @@ -0,0 +1,18 @@ +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(principalId, 'Acr', acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/AKS/core/security/role.bicep b/Environments/AKS/core/security/role.bicep new file mode 100644 index 00000000..dca01e18 --- /dev/null +++ b/Environments/AKS/core/security/role.bicep @@ -0,0 +1,20 @@ +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/AKS/core/storage/storage-account.bicep b/Environments/AKS/core/storage/storage-account.bicep new file mode 100644 index 00000000..53d449ba --- /dev/null +++ b/Environments/AKS/core/storage/storage-account.bicep @@ -0,0 +1,61 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/AKS/main.bicep b/Environments/AKS/main.bicep new file mode 100644 index 00000000..2da2a04e --- /dev/null +++ b/Environments/AKS/main.bicep @@ -0,0 +1,91 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().id + +@description('The resource name of the AKS cluster') +param clusterName string = '' + +@description('The resource name of the Container Registry (ACR)') +param containerRegistryName string = '' + +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param logAnalyticsName string = '' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) +var tags = { 'azd-env-name': environmentName } + +// The AKS cluster to host applications +module aks './core/host/aks.bicep' = { + name: 'aks' + params: { + location: location + name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName + keyVaultName: keyVault.outputs.name + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName +output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml new file mode 100644 index 00000000..b73c1ac7 --- /dev/null +++ b/Environments/AKS/manifest.yaml @@ -0,0 +1,21 @@ +name: todo-nodejs-mongo-aks +version: 1.0.0 +summary: ToDo application on AKS +description: ToDo Application with a Node.js API and Azure Cosmos DB API for MongoDB on Azure Kubernetes Service (AKS) +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: false + default: 'test' + + - id: clusterName + name: clusterName + description: 'The resource name of the AKS cluster' + type: string + required: false + default: '' \ No newline at end of file From 9f1b1b212d93da5306c4b35905af7831d5278110 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 08:50:38 +0800 Subject: [PATCH 009/112] add ADE APIM --- Environments/APIM/apimanagement.bicep | 150 + Environments/APIM/appinsightsdashboard.bicep | 1050 +++++++ Environments/APIM/azuredeploy.json | 2588 +++++++++++++++++ .../core/database/cosmos/cosmos-account.bicep | 48 + .../cosmos/mongo/cosmos-mongo-account.bicep | 22 + .../cosmos/mongo/cosmos-mongo-db.bicep | 46 + .../cosmos/sql/cosmos-sql-account.bicep | 21 + .../database/cosmos/sql/cosmos-sql-db.bicep | 73 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 18 + .../cosmos/sql/cosmos-sql-role-def.bicep | 29 + .../core/database/sqlserver/sqlserver.bicep | 129 + .../APIM/core/gateway/apim-api-policy.xml | 92 + Environments/APIM/core/gateway/apim.bicep | 61 + Environments/APIM/core/host/appservice.bicep | 97 + .../APIM/core/host/appserviceplan.bicep | 20 + .../APIM/core/host/container-app.bicep | 77 + .../host/container-apps-environment.bicep | 26 + .../APIM/core/host/container-apps.bicep | 30 + .../APIM/core/host/container-registry.bicep | 36 + Environments/APIM/core/host/functions.bicep | 82 + .../APIM/core/host/staticwebapp.bicep | 21 + .../applicationinsights-dashboard.bicep | 1235 ++++++++ .../core/monitor/applicationinsights.bicep | 30 + .../APIM/core/monitor/loganalytics.bicep | 21 + .../APIM/core/monitor/monitoring.bicep | 31 + .../APIM/core/security/keyvault-access.bicep | 21 + .../APIM/core/security/keyvault-secret.bicep | 30 + .../APIM/core/security/keyvault.bicep | 25 + .../APIM/core/storage/storage-account.bicep | 38 + Environments/APIM/main.bicep | 108 + Environments/APIM/manifest.yaml | 26 + 31 files changed, 6281 insertions(+) create mode 100644 Environments/APIM/apimanagement.bicep create mode 100644 Environments/APIM/appinsightsdashboard.bicep create mode 100644 Environments/APIM/azuredeploy.json create mode 100644 Environments/APIM/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/APIM/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/APIM/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/APIM/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/APIM/core/gateway/apim-api-policy.xml create mode 100644 Environments/APIM/core/gateway/apim.bicep create mode 100644 Environments/APIM/core/host/appservice.bicep create mode 100644 Environments/APIM/core/host/appserviceplan.bicep create mode 100644 Environments/APIM/core/host/container-app.bicep create mode 100644 Environments/APIM/core/host/container-apps-environment.bicep create mode 100644 Environments/APIM/core/host/container-apps.bicep create mode 100644 Environments/APIM/core/host/container-registry.bicep create mode 100644 Environments/APIM/core/host/functions.bicep create mode 100644 Environments/APIM/core/host/staticwebapp.bicep create mode 100644 Environments/APIM/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/APIM/core/monitor/applicationinsights.bicep create mode 100644 Environments/APIM/core/monitor/loganalytics.bicep create mode 100644 Environments/APIM/core/monitor/monitoring.bicep create mode 100644 Environments/APIM/core/security/keyvault-access.bicep create mode 100644 Environments/APIM/core/security/keyvault-secret.bicep create mode 100644 Environments/APIM/core/security/keyvault.bicep create mode 100644 Environments/APIM/core/storage/storage-account.bicep create mode 100644 Environments/APIM/main.bicep create mode 100644 Environments/APIM/manifest.yaml diff --git a/Environments/APIM/apimanagement.bicep b/Environments/APIM/apimanagement.bicep new file mode 100644 index 00000000..56cc39b7 --- /dev/null +++ b/Environments/APIM/apimanagement.bicep @@ -0,0 +1,150 @@ +param apimServiceName string +param functionAppName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apimServiceName +} + +resource functionApp 'Microsoft.Web/sites@2022-03-01' existing = { + name: functionAppName +} + +resource apimBackend 'Microsoft.ApiManagement/service/backends@2021-12-01-preview' = { + parent: apimService + name: functionAppName + properties: { + description: functionAppName + url: 'https://${functionApp.properties.hostNames[0]}' + protocol: 'http' + resourceId: '${environment().resourceManager}${functionApp.id}' + credentials: { + header: { + 'x-functions-key': [ + '{{function-app-key}}' + ] + } + } + } + dependsOn: [apimNamedValuesKey] +} + +resource apimNamedValuesKey 'Microsoft.ApiManagement/service/namedValues@2021-12-01-preview' = { + parent: apimService + name: 'function-app-key' + properties: { + displayName: 'function-app-key' + value: listKeys('${functionApp.id}/host/default', '2019-08-01').functionKeys.default + tags: [ + 'key' + 'function' + 'auto' + ] + secret: true + } +} + +resource apimAPI 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + parent: apimService + name: 'simple-fastapi-api' + properties: { + displayName: 'Protected API Calls' + apiRevision: '1' + subscriptionRequired: true + protocols: [ + 'https' + ] + path: 'api' + } +} + +resource apimAPIGet 'Microsoft.ApiManagement/service/apis/operations@2021-12-01-preview' = { + parent: apimAPI + name: 'generate-name' + properties: { + displayName: 'Generate Name' + method: 'GET' + urlTemplate: '/generate_name' + } +} + +resource apimAPIGetPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2021-12-01-preview' = { + parent: apimAPIGet + name: 'policy' + properties: { + format: 'xml' + value: '\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n*\r\n\r\n\r\nGET\r\nPOST\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n' + } + dependsOn: [apimBackend] +} + +resource apimAPIPublic 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + parent: apimService + name: 'public-docs' + properties: { + displayName: 'Public Doc Paths' + apiRevision: '1' + subscriptionRequired: false + protocols: [ + 'https' + ] + path: 'public' + } +} + +resource apimAPIDocsSwagger 'Microsoft.ApiManagement/service/apis/operations@2021-12-01-preview' = { + parent: apimAPIPublic + name: 'swagger-docs' + properties: { + displayName: 'Documentation' + method: 'GET' + urlTemplate: '/docs' + } +} + +resource apimAPIDocsSchema 'Microsoft.ApiManagement/service/apis/operations@2021-12-01-preview' = { + parent: apimAPIPublic + name: 'openapi-schema' + properties: { + displayName: 'OpenAPI Schema' + method: 'GET' + urlTemplate: '/openapi.json' + } +} + +var docsPolicy = '\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n' + +resource apimAPIDocsSwaggerPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2021-12-01-preview' = { + parent: apimAPIDocsSwagger + name: 'policy' + properties: { + format: 'xml' + value: docsPolicy + } + dependsOn: [apimBackend] +} + +resource apimAPIDocsSchemaPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2021-12-01-preview' = { + parent: apimAPIDocsSchema + name: 'policy' + properties: { + format: 'xml' + value: docsPolicy + } + dependsOn: [apimBackend] +} + +resource functionAppProperties 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'web' + kind: 'string' + parent: functionApp + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/simple-fastapi-api' + } + } + dependsOn: [ + apimService + ] +} + +output apimServiceUrl string = apimService.properties.gatewayUrl diff --git a/Environments/APIM/appinsightsdashboard.bicep b/Environments/APIM/appinsightsdashboard.bicep new file mode 100644 index 00000000..c0de9a3f --- /dev/null +++ b/Environments/APIM/appinsightsdashboard.bicep @@ -0,0 +1,1050 @@ +param prefix string +param location string +param tags object +param appInsightsName string + +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: '${prefix}-dashboard' + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: appInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: appInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: appInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: appInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: appInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: appInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: appInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${appInsightsName}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json new file mode 100644 index 00000000..6bd28608 --- /dev/null +++ b/Environments/APIM/azuredeploy.json @@ -0,0 +1,2588 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "6138348945656610072" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "defaultValue": "test", + "metadata": { + "description": "Name which is used to generate a short unique hash for each resource" + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "publisherEmail": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "The email address of the owner of the service" + } + }, + "publisherName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "The name of the owner of the service" + } + } + }, + "variables": { + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "prefix": "[format('{0}-{1}', parameters('environmentName'), variables('resourceToken'))]", + "validStoragePrefix": "[toLower(take(replace(variables('prefix'), '-', ''), 17))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": { + "value": "[format('log-{0}', variables('prefix'))]" + }, + "applicationInsightsName": { + "value": "[format('appi-{0}', variables('prefix'))]" + }, + "applicationInsightsDashboardName": { + "value": "appinsights-dashboard" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "14795379806004450628" + } + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "10301535207560739586" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "11997294327656983299" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "13636254352620323826" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}storage', variables('validStoragePrefix'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "13809511953096171798" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false + }, + "containers": { + "type": "array", + "defaultValue": [] + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2" + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2" + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Standard_LRS" + } + } + }, + "resources": [ + { + "copy": { + "name": "container", + "count": "[length(parameters('containers'))]" + }, + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}/{2}', parameters('name'), 'default', parameters('containers')[copyIndex()].name)]", + "properties": { + "publicAccess": "[if(contains(parameters('containers')[copyIndex()], 'publicAccess'), parameters('containers')[copyIndex()].publicAccess, 'None')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('name'), 'default')]" + ] + }, + { + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "sku": "[parameters('sku')]", + "properties": { + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "Allow" + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "primaryEndpoints": { + "type": "object", + "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-05-01').primaryEndpoints]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "appserviceplan", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('plan-{0}', variables('prefix'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "sku": { + "value": { + "name": "Y1", + "tier": "Dynamic" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "9579788953021113825" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "kind": { + "type": "string", + "defaultValue": "" + }, + "reserved": { + "type": "bool", + "defaultValue": true + }, + "sku": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "kind": "[parameters('kind')]", + "properties": { + "reserved": "[parameters('reserved')]" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Web/serverfarms', parameters('name'))]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "function", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('func-{0}', take(variables('prefix'), 19))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(variables('tags'), createObject('azd-service-name', 'api'))]" + }, + "alwaysOn": { + "value": false + }, + "appSettings": { + "value": { + "PYTHON_ISOLATE_WORKER_DEPENDENCIES": 1 + } + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "appServicePlanId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'appserviceplan'), '2022-09-01').outputs.id.value]" + }, + "runtimeName": { + "value": "python" + }, + "runtimeVersion": { + "value": "3.10" + }, + "storageAccountName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "4377134390306475369" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "managedIdentity": { + "type": "bool", + "defaultValue": "[not(empty(parameters('keyVaultName')))]" + }, + "storageAccountName": { + "type": "string" + }, + "runtimeName": { + "type": "string", + "allowedValues": [ + "dotnet", + "dotnetcore", + "dotnet-isolated", + "node", + "python", + "java", + "powershell", + "custom" + ] + }, + "runtimeNameAndVersion": { + "type": "string", + "defaultValue": "[format('{0}|{1}', parameters('runtimeName'), parameters('runtimeVersion'))]" + }, + "runtimeVersion": { + "type": "string" + }, + "extensionVersion": { + "type": "string", + "defaultValue": "~4", + "allowedValues": [ + "~4", + "~3", + "~2", + "~1" + ] + }, + "kind": { + "type": "string", + "defaultValue": "functionapp,linux" + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "alwaysOn": { + "type": "bool", + "defaultValue": true + }, + "appCommandLine": { + "type": "string", + "defaultValue": "" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": false + }, + "enableOryxBuild": { + "type": "bool", + "defaultValue": "[contains(parameters('kind'), 'linux')]" + }, + "functionAppScaleLimit": { + "type": "int", + "defaultValue": -1 + }, + "linuxFxVersion": { + "type": "string", + "defaultValue": "[parameters('runtimeNameAndVersion')]" + }, + "minimumElasticInstanceCount": { + "type": "int", + "defaultValue": -1 + }, + "numberOfWorkers": { + "type": "int", + "defaultValue": -1 + }, + "scmDoBuildDuringDeployment": { + "type": "bool", + "defaultValue": true + }, + "use32BitWorkerProcess": { + "type": "bool", + "defaultValue": false + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-functions', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "allowedOrigins": { + "value": "[parameters('allowedOrigins')]" + }, + "alwaysOn": { + "value": "[parameters('alwaysOn')]" + }, + "appCommandLine": { + "value": "[parameters('appCommandLine')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + }, + "appServicePlanId": { + "value": "[parameters('appServicePlanId')]" + }, + "appSettings": { + "value": "[union(parameters('appSettings'), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('storageAccountName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-09-01').keys[0].value, environment().suffixes.storage), 'FUNCTIONS_EXTENSION_VERSION', parameters('extensionVersion'), 'FUNCTIONS_WORKER_RUNTIME', parameters('runtimeName')))]" + }, + "clientAffinityEnabled": { + "value": "[parameters('clientAffinityEnabled')]" + }, + "enableOryxBuild": { + "value": "[parameters('enableOryxBuild')]" + }, + "functionAppScaleLimit": { + "value": "[parameters('functionAppScaleLimit')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "linuxFxVersion": { + "value": "[parameters('linuxFxVersion')]" + }, + "managedIdentity": { + "value": "[parameters('managedIdentity')]" + }, + "minimumElasticInstanceCount": { + "value": "[parameters('minimumElasticInstanceCount')]" + }, + "numberOfWorkers": { + "value": "[parameters('numberOfWorkers')]" + }, + "runtimeName": { + "value": "[parameters('runtimeName')]" + }, + "runtimeVersion": { + "value": "[parameters('runtimeVersion')]" + }, + "runtimeNameAndVersion": { + "value": "[parameters('runtimeNameAndVersion')]" + }, + "scmDoBuildDuringDeployment": { + "value": "[parameters('scmDoBuildDuringDeployment')]" + }, + "use32BitWorkerProcess": { + "value": "[parameters('use32BitWorkerProcess')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "14158023622954101240" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "managedIdentity": { + "type": "bool", + "defaultValue": "[not(empty(parameters('keyVaultName')))]" + }, + "runtimeName": { + "type": "string", + "allowedValues": [ + "dotnet", + "dotnetcore", + "dotnet-isolated", + "node", + "python", + "java", + "powershell", + "custom" + ] + }, + "runtimeNameAndVersion": { + "type": "string", + "defaultValue": "[format('{0}|{1}', parameters('runtimeName'), parameters('runtimeVersion'))]" + }, + "runtimeVersion": { + "type": "string" + }, + "kind": { + "type": "string", + "defaultValue": "app,linux" + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "alwaysOn": { + "type": "bool", + "defaultValue": true + }, + "appCommandLine": { + "type": "string", + "defaultValue": "" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": false + }, + "enableOryxBuild": { + "type": "bool", + "defaultValue": "[contains(parameters('kind'), 'linux')]" + }, + "functionAppScaleLimit": { + "type": "int", + "defaultValue": -1 + }, + "linuxFxVersion": { + "type": "string", + "defaultValue": "[parameters('runtimeNameAndVersion')]" + }, + "minimumElasticInstanceCount": { + "type": "int", + "defaultValue": -1 + }, + "numberOfWorkers": { + "type": "int", + "defaultValue": -1 + }, + "scmDoBuildDuringDeployment": { + "type": "bool", + "defaultValue": false + }, + "use32BitWorkerProcess": { + "type": "bool", + "defaultValue": false + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'appsettings')]", + "properties": "[union(parameters('appSettings'), createObject('SCM_DO_BUILD_DURING_DEPLOYMENT', string(parameters('scmDoBuildDuringDeployment')), 'ENABLE_ORYX_BUILD', string(parameters('enableOryxBuild'))), if(not(empty(parameters('applicationInsightsName'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString), createObject()), if(not(empty(parameters('keyVaultName'))), createObject('AZURE_KEY_VAULT_ENDPOINT', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri), createObject()))]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'logs')]", + "properties": { + "applicationLogs": { + "fileSystem": { + "level": "Verbose" + } + }, + "detailedErrorMessages": { + "enabled": true + }, + "failedRequestsTracing": { + "enabled": true + }, + "httpLogs": { + "fileSystem": { + "enabled": true, + "retentionInDays": 1, + "retentionInMb": 35 + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]", + "[resourceId('Microsoft.Web/sites/config', parameters('name'), 'appsettings')]" + ] + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "serverFarmId": "[parameters('appServicePlanId')]", + "siteConfig": { + "linuxFxVersion": "[parameters('linuxFxVersion')]", + "alwaysOn": "[parameters('alwaysOn')]", + "ftpsState": "FtpsOnly", + "appCommandLine": "[parameters('appCommandLine')]", + "numberOfWorkers": "[if(not(equals(parameters('numberOfWorkers'), -1)), parameters('numberOfWorkers'), null())]", + "minimumElasticInstanceCount": "[if(not(equals(parameters('minimumElasticInstanceCount'), -1)), parameters('minimumElasticInstanceCount'), null())]", + "use32BitWorkerProcess": "[parameters('use32BitWorkerProcess')]", + "functionAppScaleLimit": "[if(not(equals(parameters('functionAppScaleLimit'), -1)), parameters('functionAppScaleLimit'), null())]", + "cors": { + "allowedOrigins": "[union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins'))]" + } + }, + "clientAffinityEnabled": "[parameters('clientAffinityEnabled')]", + "httpsOnly": true + }, + "identity": { + "type": "[if(parameters('managedIdentity'), 'SystemAssigned', 'None')]" + } + } + ], + "outputs": { + "identityPrincipalId": { + "type": "string", + "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01', 'full').identity.principalId, '')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "value": "[format('https://{0}', reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01').defaultHostName)]" + } + } + } + } + } + ], + "outputs": { + "identityPrincipalId": { + "type": "string", + "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions', parameters('name'))), '2022-09-01').outputs.identityPrincipalId.value, '')]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions', parameters('name'))), '2022-09-01').outputs.uri.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'appserviceplan')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]", + "[resourceId('Microsoft.Resources/deployments', 'storage')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('apim-{0}', variables('prefix'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "publisherEmail": { + "value": "[parameters('publisherEmail')]" + }, + "publisherName": { + "value": "[parameters('publisherName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "1442451087124190423" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "publisherEmail": { + "type": "string", + "defaultValue": "noreply@microsoft.com", + "minLength": 1, + "metadata": { + "description": "The email address of the owner of the service" + } + }, + "publisherName": { + "type": "string", + "defaultValue": "n/a", + "minLength": 1, + "metadata": { + "description": "The name of the owner of the service" + } + }, + "sku": { + "type": "string", + "defaultValue": "Consumption", + "allowedValues": [ + "Consumption", + "Developer", + "Standard", + "Premium" + ], + "metadata": { + "description": "The pricing tier of this API Management service" + } + }, + "skuCount": { + "type": "int", + "defaultValue": 0, + "allowedValues": [ + 0, + 1, + 2 + ], + "metadata": { + "description": "The instance size of this API Management service." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "Azure Application Insights Name" + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2021-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('azd-service-name', parameters('name')))]", + "sku": { + "name": "[parameters('sku')]", + "capacity": "[if(equals(parameters('sku'), 'Consumption'), 0, if(equals(parameters('sku'), 'Developer'), 1, parameters('skuCount')))]" + }, + "properties": { + "publisherEmail": "[parameters('publisherEmail')]", + "publisherName": "[parameters('publisherName')]" + } + }, + { + "condition": "[not(empty(parameters('applicationInsightsName')))]", + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'app-insights-logger')]", + "properties": { + "credentials": { + "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" + }, + "description": "Logger to Azure Application Insights", + "isBuffered": false, + "loggerType": "applicationInsights", + "resourceId": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" + ] + } + ], + "outputs": { + "apimServiceName": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apimanagement-resources", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "apimServiceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'apim-deployment'), '2022-09-01').outputs.apimServiceName.value]" + }, + "functionAppName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'function'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "16472563199209300832" + } + }, + "parameters": { + "apimServiceName": { + "type": "string" + }, + "functionAppName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/backends", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), parameters('functionAppName'))]", + "properties": { + "description": "[parameters('functionAppName')]", + "url": "[format('https://{0}', reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2022-03-01').hostNames[0])]", + "protocol": "http", + "resourceId": "[format('{0}{1}', environment().resourceManager, resourceId('Microsoft.Web/sites', parameters('functionAppName')))]", + "credentials": { + "header": { + "x-functions-key": [ + "{{function-app-key}}" + ] + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/namedValues', parameters('apimServiceName'), 'function-app-key')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/namedValues", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'function-app-key')]", + "properties": { + "displayName": "function-app-key", + "value": "[listKeys(format('{0}/host/default', resourceId('Microsoft.Web/sites', parameters('functionAppName'))), '2019-08-01').functionKeys.default]", + "tags": [ + "key", + "function", + "auto" + ], + "secret": true + } + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'simple-fastapi-api')]", + "properties": { + "displayName": "Protected API Calls", + "apiRevision": "1", + "subscriptionRequired": true, + "protocols": [ + "https" + ], + "path": "api" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('apimServiceName'), 'simple-fastapi-api', 'generate-name')]", + "properties": { + "displayName": "Generate Name", + "method": "GET", + "urlTemplate": "/generate_name" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apimServiceName'), 'simple-fastapi-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}/{3}', parameters('apimServiceName'), 'simple-fastapi-api', 'generate-name', 'policy')]", + "properties": { + "format": "xml", + "value": "[format('\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n*\r\n\r\n\r\nGET\r\nPOST\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n', reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2022-03-01').name)]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis/operations', parameters('apimServiceName'), 'simple-fastapi-api', 'generate-name')]", + "[resourceId('Microsoft.ApiManagement/service/backends', parameters('apimServiceName'), parameters('functionAppName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'public-docs')]", + "properties": { + "displayName": "Public Doc Paths", + "apiRevision": "1", + "subscriptionRequired": false, + "protocols": [ + "https" + ], + "path": "public" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('apimServiceName'), 'public-docs', 'swagger-docs')]", + "properties": { + "displayName": "Documentation", + "method": "GET", + "urlTemplate": "/docs" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apimServiceName'), 'public-docs')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('apimServiceName'), 'public-docs', 'openapi-schema')]", + "properties": { + "displayName": "OpenAPI Schema", + "method": "GET", + "urlTemplate": "/openapi.json" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apimServiceName'), 'public-docs')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}/{3}', parameters('apimServiceName'), 'public-docs', 'swagger-docs', 'policy')]", + "properties": { + "format": "xml", + "value": "[format('\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n', reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2022-03-01').name)]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis/operations', parameters('apimServiceName'), 'public-docs', 'swagger-docs')]", + "[resourceId('Microsoft.ApiManagement/service/backends', parameters('apimServiceName'), parameters('functionAppName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}/{3}', parameters('apimServiceName'), 'public-docs', 'openapi-schema', 'policy')]", + "properties": { + "format": "xml", + "value": "[format('\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n', reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2022-03-01').name)]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis/operations', parameters('apimServiceName'), 'public-docs', 'openapi-schema')]", + "[resourceId('Microsoft.ApiManagement/service/backends', parameters('apimServiceName'), parameters('functionAppName'))]" + ] + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('functionAppName'), 'web')]", + "kind": "string", + "properties": { + "apiManagementConfig": { + "id": "[format('{0}/apis/simple-fastapi-api', resourceId('Microsoft.ApiManagement/service', parameters('apimServiceName')))]" + } + } + } + ], + "outputs": { + "apimServiceUrl": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ApiManagement/service', parameters('apimServiceName')), '2021-08-01').gatewayUrl]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'apim-deployment')]", + "[resourceId('Microsoft.Resources/deployments', 'function')]" + ] + } + ], + "outputs": { + "SERVICE_API_ENDPOINTS": { + "type": "array", + "value": [ + "[format('{0}/public/docs', reference(resourceId('Microsoft.Resources/deployments', 'apimanagement-resources'), '2022-09-01').outputs.apimServiceUrl.value)]" + ] + } + } +} \ No newline at end of file diff --git a/Environments/APIM/core/database/cosmos/cosmos-account.bicep b/Environments/APIM/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6bc1f2eb --- /dev/null +++ b/Environments/APIM/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,48 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..bd2a2b5f --- /dev/null +++ b/Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,22 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2c9688e0 --- /dev/null +++ b/Environments/APIM/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,46 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/APIM/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..e8b030f6 --- /dev/null +++ b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/APIM/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..5a4de209 --- /dev/null +++ b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,73 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefintion 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefintion.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefintion.outputs.id diff --git a/Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..6855edfe --- /dev/null +++ b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,18 @@ +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..cfb40335 --- /dev/null +++ b/Environments/APIM/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,29 @@ +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/APIM/core/database/sqlserver/sqlserver.bicep b/Environments/APIM/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..64477a74 --- /dev/null +++ b/Environments/APIM/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,129 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/APIM/core/gateway/apim-api-policy.xml b/Environments/APIM/core/gateway/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/APIM/core/gateway/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
*
+
+ +
*
+
+
+ + + + + + + Call to the @(context.Api.Name) + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
diff --git a/Environments/APIM/core/gateway/apim.bicep b/Environments/APIM/core/gateway/apim.bicep new file mode 100644 index 00000000..76c5ae7b --- /dev/null +++ b/Environments/APIM/core/gateway/apim.bicep @@ -0,0 +1,61 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/APIM/core/host/appservice.bicep b/Environments/APIM/core/host/appservice.bicep new file mode 100644 index 00000000..62e34a65 --- /dev/null +++ b/Environments/APIM/core/host/appservice.bicep @@ -0,0 +1,97 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: 'FtpsOnly' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configAppSettings 'config' = { + name: 'appsettings' + properties: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [ + configAppSettings + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/APIM/core/host/appserviceplan.bicep b/Environments/APIM/core/host/appserviceplan.bicep new file mode 100644 index 00000000..69c35d78 --- /dev/null +++ b/Environments/APIM/core/host/appserviceplan.bicep @@ -0,0 +1,20 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id diff --git a/Environments/APIM/core/host/container-app.bicep b/Environments/APIM/core/host/container-app.bicep new file mode 100644 index 00000000..dde1bab7 --- /dev/null +++ b/Environments/APIM/core/host/container-app.bicep @@ -0,0 +1,77 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string = '' +param containerName string = 'main' +param containerRegistryName string = '' +param env array = [] +param external bool = true +param imageName string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param targetPort int = 80 + +@description('CPU cores allocated to a single container instance, e.g. 0.5') +param containerCpuCoreCount string = '0.5' + +@description('Memory allocated to a single container instance, e.g. 1Gi') +param containerMemory string = '1.0Gi' + +resource app 'Microsoft.App/containerApps@2022-03-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: 'single' + ingress: { + external: external + targetPort: targetPort + transport: 'auto' + } + secrets: [ + { + name: 'registry-password' + value: containerRegistry.listCredentials().passwords[0].value + } + ] + registries: [ + { + server: '${containerRegistry.name}.azurecr.io' + username: containerRegistry.name + passwordSecretRef: 'registry-password' + } + ] + } + template: { + containers: [ + { + image: imageName + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { + name: containerAppsEnvironmentName +} + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} + +output identityPrincipalId string = managedIdentity ? app.identity.principalId : '' +output imageName string = imageName +output name string = app.name +output uri string = 'https://${app.properties.configuration.ingress.fqdn}' diff --git a/Environments/APIM/core/host/container-apps-environment.bicep b/Environments/APIM/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..2dd858cb --- /dev/null +++ b/Environments/APIM/core/host/container-apps-environment.bicep @@ -0,0 +1,26 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +output name string = containerAppsEnvironment.name diff --git a/Environments/APIM/core/host/container-apps.bicep b/Environments/APIM/core/host/container-apps.bicep new file mode 100644 index 00000000..395af705 --- /dev/null +++ b/Environments/APIM/core/host/container-apps.bicep @@ -0,0 +1,30 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string = '' +param containerRegistryName string = '' +param logAnalyticsWorkspaceName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + } +} + +output environmentName string = containerAppsEnvironment.outputs.name +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/APIM/core/host/container-registry.bicep b/Environments/APIM/core/host/container-registry.bicep new file mode 100644 index 00000000..01c32139 --- /dev/null +++ b/Environments/APIM/core/host/container-registry.bicep @@ -0,0 +1,36 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param adminUserEnabled bool = true +param anonymousPullEnabled bool = false +param dataEndpointEnabled bool = false +param encryption object = { + status: 'disabled' +} +param networkRuleBypassOptions string = 'AzureServices' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'Basic' +} +param zoneRedundancy string = 'Disabled' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/APIM/core/host/functions.bicep b/Environments/APIM/core/host/functions.bicep new file mode 100644 index 00000000..28a581bd --- /dev/null +++ b/Environments/APIM/core/host/functions.bicep @@ -0,0 +1,82 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/APIM/core/host/staticwebapp.bicep b/Environments/APIM/core/host/staticwebapp.bicep new file mode 100644 index 00000000..91c2d0db --- /dev/null +++ b/Environments/APIM/core/host/staticwebapp.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/APIM/core/monitor/applicationinsights-dashboard.bicep b/Environments/APIM/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..b7af2c1a --- /dev/null +++ b/Environments/APIM/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1235 @@ +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/APIM/core/monitor/applicationinsights.bicep b/Environments/APIM/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..f76b292b --- /dev/null +++ b/Environments/APIM/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/APIM/core/monitor/loganalytics.bicep b/Environments/APIM/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..770544cc --- /dev/null +++ b/Environments/APIM/core/monitor/loganalytics.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/APIM/core/monitor/monitoring.bicep b/Environments/APIM/core/monitor/monitoring.bicep new file mode 100644 index 00000000..96ba11e5 --- /dev/null +++ b/Environments/APIM/core/monitor/monitoring.bicep @@ -0,0 +1,31 @@ +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/APIM/core/security/keyvault-access.bicep b/Environments/APIM/core/security/keyvault-access.bicep new file mode 100644 index 00000000..96c9cf73 --- /dev/null +++ b/Environments/APIM/core/security/keyvault-access.bicep @@ -0,0 +1,21 @@ +param name string = 'add' + +param keyVaultName string = '' +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/APIM/core/security/keyvault-secret.bicep b/Environments/APIM/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..5f786ce5 --- /dev/null +++ b/Environments/APIM/core/security/keyvault-secret.bicep @@ -0,0 +1,30 @@ +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/APIM/core/security/keyvault.bicep b/Environments/APIM/core/security/keyvault.bicep new file mode 100644 index 00000000..0eb4a86d --- /dev/null +++ b/Environments/APIM/core/security/keyvault.bicep @@ -0,0 +1,25 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/APIM/core/storage/storage-account.bicep b/Environments/APIM/core/storage/storage-account.bicep new file mode 100644 index 00000000..a41972ce --- /dev/null +++ b/Environments/APIM/core/storage/storage-account.bicep @@ -0,0 +1,38 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param allowBlobPublicAccess bool = false +param containers array = [] +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + minimumTlsVersion: minimumTlsVersion + allowBlobPublicAccess: allowBlobPublicAccess + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Allow' + } + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/APIM/main.bicep b/Environments/APIM/main.bicep new file mode 100644 index 00000000..443c9ed9 --- /dev/null +++ b/Environments/APIM/main.bicep @@ -0,0 +1,108 @@ +@minLength(1) +@maxLength(64) +@description('Name which is used to generate a short unique hash for each resource') +param environmentName string = 'test' + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string + +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) +var tags = { 'azd-env-name': environmentName } + +var prefix = '${environmentName}-${resourceToken}' + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: 'log-${prefix}' + applicationInsightsName: 'appi-${prefix}' + applicationInsightsDashboardName: 'appinsights-dashboard' + } +} + +// Backing storage for Azure functions backend API +var validStoragePrefix = toLower(take(replace(prefix, '-', ''), 17)) +module storageAccount 'core/storage/storage-account.bicep' = { + name: 'storage' + params: { + name: '${validStoragePrefix}storage' + location: location + tags: tags + } +} + + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan './core/host/appserviceplan.bicep' = { + name: 'appserviceplan' + params: { + name: 'plan-${prefix}' + location: location + tags: tags + sku: { + name: 'Y1' + tier: 'Dynamic' + } + } +} + +module functionApp 'core/host/functions.bicep' = { + name: 'function' + params: { + // Truncating to 32 due to https://github.com/Azure/azure-functions-host/issues/2015 + name: 'func-${take(prefix, 19)}' + location: location + tags: union(tags, { 'azd-service-name': 'api' }) + alwaysOn: false + appSettings: { + PYTHON_ISOLATE_WORKER_DEPENDENCIES: 1 + } + applicationInsightsName: monitoring.outputs.applicationInsightsName + appServicePlanId: appServicePlan.outputs.id + runtimeName: 'python' + runtimeVersion: '3.10' + storageAccountName: storageAccount.outputs.name + } +} + + +// Creates Azure API Management (APIM) service to mediate the requests between the frontend and the backend API +module apim './core/gateway/apim.bicep' = { + name: 'apim-deployment' + params: { + name: 'apim-${prefix}' + location: location + tags: tags + applicationInsightsName: monitoring.outputs.applicationInsightsName + publisherEmail: publisherEmail + publisherName: publisherName + } +} + +// Configures the API in the Azure API Management (APIM) service +module apimAPI 'apimanagement.bicep' = { + name: 'apimanagement-resources' + params: { + apimServiceName: apim.outputs.apimServiceName + functionAppName: functionApp.outputs.name + } + dependsOn: [ + functionApp + ] +} + + + +output SERVICE_API_ENDPOINTS array = ['${apimAPI.outputs.apimServiceUrl}/public/docs'] diff --git a/Environments/APIM/manifest.yaml b/Environments/APIM/manifest.yaml new file mode 100644 index 00000000..7074e2af --- /dev/null +++ b/Environments/APIM/manifest.yaml @@ -0,0 +1,26 @@ +name: fastapi-azure-function-apim +version: 1.0.0 +summary: API Management +description: Deploy a secured Azure Function with an API Management service in front +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: false + default: 'test' + + - id: publisherEmail + name: publisherEmail + description: 'Eamil of the publisher' + type: string + required: true + + - id: publisherName + name: publisherName + description: 'Name of the publisher' + type: string + required: true \ No newline at end of file From 72ba5fef342939c1f186257c755d9a7fc139f3a0 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 00:51:42 +0000 Subject: [PATCH 010/112] Rebuild ARM templates --- Environments/AKS/azuredeploy.json | 64 +++++++++++----------- Environments/ContainerApp/azuredeploy.json | 32 +++++------ Environments/FunctionApp/azuredeploy.json | 4 +- Environments/OpenAISearch/azuredeploy.json | 64 +++++++++++----------- Environments/Sandbox/azuredeploy.json | 4 +- Environments/Spring/azuredeploy.json | 48 ++++++++-------- Environments/WebApp/azuredeploy.json | 4 +- 7 files changed, 110 insertions(+), 110 deletions(-) diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index c6449186..ed582901 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5597674447914299578" + "version": "0.17.1.54307", + "templateHash": "15381851435357335500" } }, "parameters": { @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "2584495159523716195" + "version": "0.17.1.54307", + "templateHash": "5858231189521761249" } }, "parameters": { @@ -444,8 +444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "12976609032349838131" + "version": "0.17.1.54307", + "templateHash": "12367137306066387435" } }, "parameters": { @@ -724,8 +724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "7351126758431238881" + "version": "0.17.1.54307", + "templateHash": "1069227136590891661" } }, "parameters": { @@ -786,8 +786,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13010360132212368737" + "version": "0.17.1.54307", + "templateHash": "14794375381139164570" } }, "parameters": { @@ -931,8 +931,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13019144734616357723" + "version": "0.17.1.54307", + "templateHash": "5378345674008955659" } }, "parameters": { @@ -989,8 +989,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5134893917065565335" + "version": "0.17.1.54307", + "templateHash": "18301876310392488097" } }, "parameters": { @@ -1104,8 +1104,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6363297596017193433" + "version": "0.17.1.54307", + "templateHash": "10658647303664219792" } }, "parameters": { @@ -1185,8 +1185,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "8513104519886724643" + "version": "0.17.1.54307", + "templateHash": "6962395895547155086" } }, "parameters": { @@ -1292,8 +1292,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "16707878505366027072" + "version": "0.17.1.54307", + "templateHash": "6947634037528798693" } }, "parameters": { @@ -1352,8 +1352,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6129206474152081358" + "version": "0.17.1.54307", + "templateHash": "13620265882151180505" } }, "parameters": { @@ -1531,8 +1531,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10766203656109589284" + "version": "0.17.1.54307", + "templateHash": "15428045515587502988" } }, "parameters": { @@ -1608,8 +1608,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6189478021468152767" + "version": "0.17.1.54307", + "templateHash": "14795379806004450628" } }, "parameters": { @@ -1658,8 +1658,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10960702046792783086" + "version": "0.17.1.54307", + "templateHash": "10301535207560739586" } }, "parameters": { @@ -1738,8 +1738,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "4618248294119261512" + "version": "0.17.1.54307", + "templateHash": "11997294327656983299" } }, "parameters": { @@ -1800,8 +1800,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "11251127820136102381" + "version": "0.17.1.54307", + "templateHash": "13636254352620323826" } }, "parameters": { diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json index d5bf6760..b78bd934 100644 --- a/Environments/ContainerApp/azuredeploy.json +++ b/Environments/ContainerApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6767977252020051216" + "version": "0.17.1.54307", + "templateHash": "4272651749285762279" } }, "parameters": { @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10766203656109589284" + "version": "0.17.1.54307", + "templateHash": "15428045515587502988" } }, "parameters": { @@ -161,8 +161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "12550809022825447853" + "version": "0.17.1.54307", + "templateHash": "7437297476919271337" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "242147059711260467" + "version": "0.17.1.54307", + "templateHash": "4782162412977538651" } }, "parameters": { @@ -293,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "14762338059257613188" + "version": "0.17.1.54307", + "templateHash": "10225668587554043828" } }, "parameters": { @@ -434,8 +434,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "16445015047461323116" + "version": "0.17.1.54307", + "templateHash": "17254398984980713782" } }, "parameters": { @@ -508,8 +508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "7411744424728408137" + "version": "0.17.1.54307", + "templateHash": "17927222532671817166" } }, "parameters": { @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10960702046792783086" + "version": "0.17.1.54307", + "templateHash": "10301535207560739586" } }, "parameters": { diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index 139c8777..e09d3c9e 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "14714597139081137006" + "version": "0.17.1.54307", + "templateHash": "12123717580285782629" } }, "parameters": { diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 6d67f1f7..5569613e 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "2447162074270477957" + "version": "0.17.1.54307", + "templateHash": "12584815192835029184" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "1837336361730027989" + "version": "0.17.1.54307", + "templateHash": "8694592921587637446" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "935190995104945414" + "version": "0.17.1.54307", + "templateHash": "3317056714714509123" } }, "parameters": { @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "15292359723056804408" + "version": "0.17.1.54307", + "templateHash": "11076527127870690555" } }, "parameters": { @@ -771,8 +771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "15292359723056804408" + "version": "0.17.1.54307", + "templateHash": "11076527127870690555" } }, "parameters": { @@ -900,8 +900,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "11912420961517980450" + "version": "0.17.1.54307", + "templateHash": "2612000844202476215" } }, "parameters": { @@ -1024,8 +1024,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "14234446244824387754" + "version": "0.17.1.54307", + "templateHash": "17856846537035111526" } }, "parameters": { @@ -1196,8 +1196,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1260,8 +1260,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1324,8 +1324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1388,8 +1388,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1452,8 +1452,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1516,8 +1516,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1580,8 +1580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1647,8 +1647,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1714,8 +1714,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index a2039127..4add3976 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "8154980294172562107" + "version": "0.17.1.54307", + "templateHash": "11429704302890700233" } }, "resources": [] diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index 9c83940c..ad5ac488 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "1154694905938494061" + "version": "0.17.1.54307", + "templateHash": "7122962929441782527" } }, "parameters": { @@ -245,8 +245,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "1639156783194607123" + "version": "0.17.1.54307", + "templateHash": "9579788953021113825" } }, "parameters": { @@ -322,8 +322,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6189478021468152767" + "version": "0.17.1.54307", + "templateHash": "14795379806004450628" } }, "parameters": { @@ -372,8 +372,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10960702046792783086" + "version": "0.17.1.54307", + "templateHash": "10301535207560739586" } }, "parameters": { @@ -452,8 +452,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "4618248294119261512" + "version": "0.17.1.54307", + "templateHash": "11997294327656983299" } }, "parameters": { @@ -514,8 +514,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "11251127820136102381" + "version": "0.17.1.54307", + "templateHash": "13636254352620323826" } }, "parameters": { @@ -1823,8 +1823,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10766203656109589284" + "version": "0.17.1.54307", + "templateHash": "15428045515587502988" } }, "parameters": { @@ -1908,8 +1908,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "12732925979307758465" + "version": "0.17.1.54307", + "templateHash": "15254487284233766693" } }, "parameters": { @@ -2000,8 +2000,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6228396417467428730" + "version": "0.17.1.54307", + "templateHash": "2841349531746939468" } }, "parameters": { @@ -2262,8 +2262,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "2283778516050253977" + "version": "0.17.1.54307", + "templateHash": "16501122015582133594" } }, "parameters": { @@ -2371,8 +2371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "4560700392891195594" + "version": "0.17.1.54307", + "templateHash": "14158023622954101240" } }, "parameters": { @@ -2608,8 +2608,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "1928598198302569198" + "version": "0.17.1.54307", + "templateHash": "17512594306716167359" } }, "parameters": { diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 4d6743d8..932a2654 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "3666499352609317658" + "version": "0.17.1.54307", + "templateHash": "11616939137952571479" } }, "parameters": { From d1e15aba849d8dd22142e2c9b08b5333db8e2bf6 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 08:55:51 +0800 Subject: [PATCH 011/112] add ADE app vnet --- Environments/AppVNet/appinsights.bicep | 1244 ++++++++++++++++++ Environments/AppVNet/azuredeploy.json | 1655 ++++++++++++++++++++++++ Environments/AppVNet/main.bicep | 35 + Environments/AppVNet/manifest.yaml | 26 + Environments/AppVNet/resources.bicep | 233 ++++ 5 files changed, 3193 insertions(+) create mode 100644 Environments/AppVNet/appinsights.bicep create mode 100644 Environments/AppVNet/azuredeploy.json create mode 100644 Environments/AppVNet/main.bicep create mode 100644 Environments/AppVNet/manifest.yaml create mode 100644 Environments/AppVNet/resources.bicep diff --git a/Environments/AppVNet/appinsights.bicep b/Environments/AppVNet/appinsights.bicep new file mode 100644 index 00000000..83cfe17a --- /dev/null +++ b/Environments/AppVNet/appinsights.bicep @@ -0,0 +1,1244 @@ +param prefix string +param location string +param tags object +param workspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: '${prefix}-appinsights' + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: workspaceId + } +} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: '${prefix}-dashboard' + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.properties.ConnectionString diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json new file mode 100644 index 00000000..3ead99b1 --- /dev/null +++ b/Environments/AppVNet/azuredeploy.json @@ -0,0 +1,1655 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "110809187980919240" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "defaultValue": "test", + "metadata": { + "description": "Name which is used to generate a short unique hash for each resource" + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "databasePassword": { + "type": "securestring", + "metadata": { + "description": "PostGreSQL Server administrator password" + } + }, + "secretKey": { + "type": "securestring", + "metadata": { + "description": "Django SECRET_KEY for securing signed data" + } + } + }, + "variables": { + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "resources", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('environmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "resourceToken": { + "value": "[variables('resourceToken')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "databasePassword": { + "value": "[parameters('databasePassword')]" + }, + "secretKey": { + "value": "[parameters('secretKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "949995683768579485" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "resourceToken": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "databasePassword": { + "type": "securestring" + }, + "secretKey": { + "type": "securestring" + } + }, + "variables": { + "prefix": "[format('{0}-{1}', parameters('name'), parameters('resourceToken'))]", + "pgServerName": "[format('{0}-postgres-server', variables('prefix'))]", + "databaseSubnetName": "database-subnet", + "webappSubnetName": "webapp-subnet" + }, + "resources": [ + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', format('app-{0}', variables('prefix')), 'appsettings')]", + "properties": { + "AZURE_POSTGRESQL_CONNECTIONSTRING": "[format('dbname={0} host={1}.postgres.database.azure.com port=5432 sslmode=require user={2} password={3}', 'django', variables('pgServerName'), reference(resourceId('Microsoft.DBforPostgreSQL/flexibleServers', variables('pgServerName')), '2022-01-20-preview').administratorLogin, parameters('databasePassword'))]", + "SCM_DO_BUILD_DURING_DEPLOYMENT": "true", + "SECRET_KEY": "[parameters('secretKey')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers/databases', variables('pgServerName'), 'django')]", + "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', variables('pgServerName'))]", + "[resourceId('Microsoft.Web/sites', format('app-{0}', variables('prefix')))]" + ] + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', format('app-{0}', variables('prefix')), 'logs')]", + "properties": { + "applicationLogs": { + "fileSystem": { + "level": "Verbose" + } + }, + "detailedErrorMessages": { + "enabled": true + }, + "failedRequestsTracing": { + "enabled": true + }, + "httpLogs": { + "fileSystem": { + "enabled": true, + "retentionInDays": 1, + "retentionInMb": 35 + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', format('app-{0}', variables('prefix')))]" + ] + }, + { + "type": "Microsoft.Web/sites/networkConfig", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', format('app-{0}', variables('prefix')), 'virtualNetwork')]", + "properties": { + "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', format('vnet-{0}', variables('prefix')), variables('webappSubnetName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/virtualNetworks', format('vnet-{0}', variables('prefix')))]", + "[resourceId('Microsoft.Web/sites', format('app-{0}', variables('prefix')))]" + ] + }, + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2019-11-01", + "name": "[format('vnet-{0}', variables('prefix'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "10.0.0.0/16" + ] + }, + "subnets": [ + { + "name": "[variables('databaseSubnetName')]", + "properties": { + "addressPrefix": "10.0.0.0/24", + "delegations": [ + { + "name": "[format('{0}-subnet-delegation', variables('prefix'))]", + "properties": { + "serviceName": "Microsoft.DBforPostgreSQL/flexibleServers" + } + } + ] + } + }, + { + "name": "[variables('webappSubnetName')]", + "properties": { + "addressPrefix": "10.0.1.0/24", + "delegations": [ + { + "name": "[format('{0}-subnet-delegation-web', variables('prefix'))]", + "properties": { + "serviceName": "Microsoft.Web/serverFarms" + } + } + ] + } + } + ] + } + }, + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[format('{0}.private.postgres.database.azure.com', variables('pgServerName'))]", + "location": "global", + "tags": "[parameters('tags')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/virtualNetworks', format('vnet-{0}', variables('prefix')))]" + ] + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', format('{0}.private.postgres.database.azure.com', variables('pgServerName')), format('link-{0}', variables('pgServerName')))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[resourceId('Microsoft.Network/virtualNetworks', format('vnet-{0}', variables('prefix')))]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', format('{0}.private.postgres.database.azure.com', variables('pgServerName')))]", + "[resourceId('Microsoft.Network/virtualNetworks', format('vnet-{0}', variables('prefix')))]" + ] + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2022-03-01", + "name": "[format('app-{0}', variables('prefix'))]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('azd-service-name', 'web'))]", + "kind": "app,linux", + "properties": { + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', format('plan-{0}', variables('prefix')))]", + "siteConfig": { + "alwaysOn": true, + "linuxFxVersion": "PYTHON|3.10", + "ftpsState": "Disabled", + "appCommandLine": "startup.sh" + }, + "httpsOnly": true + }, + "identity": { + "type": "SystemAssigned" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', format('plan-{0}', variables('prefix')))]", + "[resourceId('Microsoft.Network/virtualNetworks', format('vnet-{0}', variables('prefix')))]" + ] + }, + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2021-03-01", + "name": "[format('plan-{0}', variables('prefix'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "B1" + }, + "properties": { + "reserved": true + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2020-03-01-preview", + "name": "[format('log-{0}', variables('prefix'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + }, + { + "type": "Microsoft.DBforPostgreSQL/flexibleServers", + "apiVersion": "2022-01-20-preview", + "name": "[variables('pgServerName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "Standard_B1ms", + "tier": "Burstable" + }, + "properties": { + "version": "12", + "administratorLogin": "django", + "administratorLoginPassword": "[parameters('databasePassword')]", + "storage": { + "storageSizeGB": 128 + }, + "backup": { + "backupRetentionDays": 7, + "geoRedundantBackup": "Disabled" + }, + "network": { + "delegatedSubnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', format('vnet-{0}', variables('prefix')), variables('databaseSubnetName'))]", + "privateDnsZoneArmResourceId": "[resourceId('Microsoft.Network/privateDnsZones', format('{0}.private.postgres.database.azure.com', variables('pgServerName')))]" + }, + "highAvailability": { + "mode": "Disabled" + }, + "maintenanceWindow": { + "customWindow": "Disabled", + "dayOfWeek": 0, + "startHour": 0, + "startMinute": 0 + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', format('{0}.private.postgres.database.azure.com', variables('pgServerName')))]", + "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', format('{0}.private.postgres.database.azure.com', variables('pgServerName')), format('link-{0}', variables('pgServerName')))]", + "[resourceId('Microsoft.Network/virtualNetworks', format('vnet-{0}', variables('prefix')))]" + ] + }, + { + "type": "Microsoft.DBforPostgreSQL/flexibleServers/databases", + "apiVersion": "2022-01-20-preview", + "name": "[format('{0}/{1}', variables('pgServerName'), 'django')]", + "dependsOn": [ + "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', variables('pgServerName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights-resources", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "prefix": { + "value": "[variables('prefix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": { + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', format('log-{0}', variables('prefix')))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "10803209143093915866" + } + }, + "parameters": { + "prefix": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "workspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[format('{0}-appinsights', parameters('prefix'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('workspaceId')]" + } + }, + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[format('{0}-dashboard', parameters('prefix'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[format('{0}-appinsights', parameters('prefix'))]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[format('{0}-appinsights', parameters('prefix'))]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[format('{0}-appinsights', parameters('prefix'))]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[format('{0}-appinsights', parameters('prefix'))]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[format('{0}-appinsights', parameters('prefix'))]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[format('{0}-appinsights', parameters('prefix'))]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[format('{0}-appinsights', parameters('prefix'))]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, format('{0}-appinsights', parameters('prefix')))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', format('{0}-appinsights', parameters('prefix')))]" + ] + } + ], + "outputs": { + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', format('{0}-appinsights', parameters('prefix'))), '2020-02-02').ConnectionString]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.OperationalInsights/workspaces', format('log-{0}', variables('prefix')))]" + ] + } + ], + "outputs": { + "WEB_URI": { + "type": "string", + "value": "[format('https://{0}', reference(resourceId('Microsoft.Web/sites', format('app-{0}', variables('prefix'))), '2022-03-01').defaultHostName)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights-resources'), '2022-09-01').outputs.APPLICATIONINSIGHTS_CONNECTION_STRING.value]" + } + } + } + } + } + ], + "outputs": { + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.APPLICATIONINSIGHTS_CONNECTION_STRING.value]" + }, + "WEB_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.WEB_URI.value]" + } + } +} \ No newline at end of file diff --git a/Environments/AppVNet/main.bicep b/Environments/AppVNet/main.bicep new file mode 100644 index 00000000..f4e7c7a1 --- /dev/null +++ b/Environments/AppVNet/main.bicep @@ -0,0 +1,35 @@ +@minLength(1) +@maxLength(64) +@description('Name which is used to generate a short unique hash for each resource') +param environmentName string = 'test' + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +@secure() +@description('PostGreSQL Server administrator password') +param databasePassword string + +@secure() +@description('Django SECRET_KEY for securing signed data') +param secretKey string + +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) +var tags = { 'azd-env-name': environmentName } + +module resources 'resources.bicep' = { + name: 'resources' + params: { + name: environmentName + location: location + resourceToken: resourceToken + tags: tags + databasePassword: databasePassword + secretKey: secretKey + } +} + +output AZURE_LOCATION string = location +output APPLICATIONINSIGHTS_CONNECTION_STRING string = resources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING +output WEB_URI string = resources.outputs.WEB_URI diff --git a/Environments/AppVNet/manifest.yaml b/Environments/AppVNet/manifest.yaml new file mode 100644 index 00000000..2558bf93 --- /dev/null +++ b/Environments/AppVNet/manifest.yaml @@ -0,0 +1,26 @@ +name: msdocs-django-postgresql-sample-app +version: 1.0.0 +summary: App Service + VNet +description: Deploy App service in VNet +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: false + default: 'test' + + - id: databasePassword + name: databasePassword + description: 'Password of PostgreSQL' + type: string + required: true + + - id: secretKey + name: secretKey + description: 'Django SECRET_KEY for securing signed data' + type: string + required: true \ No newline at end of file diff --git a/Environments/AppVNet/resources.bicep b/Environments/AppVNet/resources.bicep new file mode 100644 index 00000000..8b5419e6 --- /dev/null +++ b/Environments/AppVNet/resources.bicep @@ -0,0 +1,233 @@ +param name string +param location string +param resourceToken string +param tags object +@secure() +param databasePassword string +@secure() +param secretKey string + +var prefix = '${name}-${resourceToken}' + +var pgServerName = '${prefix}-postgres-server' +var databaseSubnetName = 'database-subnet' +var webappSubnetName = 'webapp-subnet' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { + name: 'vnet-${prefix}' + location: location + tags: tags + properties: { + addressSpace: { + addressPrefixes: [ + '10.0.0.0/16' + ] + } + subnets: [ + { + name: databaseSubnetName + properties: { + addressPrefix: '10.0.0.0/24' + delegations: [ + { + name: '${prefix}-subnet-delegation' + properties: { + serviceName: 'Microsoft.DBforPostgreSQL/flexibleServers' + } + } + ] + } + } + { + name: webappSubnetName + properties: { + addressPrefix: '10.0.1.0/24' + delegations: [ + { + name: '${prefix}-subnet-delegation-web' + properties: { + serviceName: 'Microsoft.Web/serverFarms' + } + } + ] + } + } + ] + } + resource databaseSubnet 'subnets' existing = { + name: databaseSubnetName + } + resource webappSubnet 'subnets' existing = { + name: webappSubnetName + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: '${pgServerName}.private.postgres.database.azure.com' + location: 'global' + tags: tags + dependsOn: [ + virtualNetwork + ] +} + +resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + parent: privateDnsZone + name: 'link-${pgServerName}' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetwork.id + } + } +} + +resource web 'Microsoft.Web/sites@2022-03-01' = { + name: 'app-${prefix}' + location: location + tags: union(tags, { 'azd-service-name': 'web' }) + kind: 'app,linux' + properties: { + serverFarmId: appServicePlan.id + siteConfig: { + alwaysOn: true + linuxFxVersion: 'PYTHON|3.10' + ftpsState: 'Disabled' + appCommandLine: 'startup.sh' + } + httpsOnly: true + } + identity: { + type: 'SystemAssigned' + } + + resource appSettings 'config' = { + name: 'appsettings' + properties: { + AZURE_POSTGRESQL_CONNECTIONSTRING: 'dbname=${djangoDatabase.name} host=${postgresServer.name}.postgres.database.azure.com port=5432 sslmode=require user=${postgresServer.properties.administratorLogin} password=${databasePassword}' + SCM_DO_BUILD_DURING_DEPLOYMENT: 'true' + SECRET_KEY: secretKey + } + } + + resource logs 'config' = { + name: 'logs' + properties: { + applicationLogs: { + fileSystem: { + level: 'Verbose' + } + } + detailedErrorMessages: { + enabled: true + } + failedRequestsTracing: { + enabled: true + } + httpLogs: { + fileSystem: { + enabled: true + retentionInDays: 1 + retentionInMb: 35 + } + } + } + } + + resource webappVnetConfig 'networkConfig' = { + name: 'virtualNetwork' + properties: { + subnetResourceId: virtualNetwork::webappSubnet.id + } + } + + dependsOn: [virtualNetwork] + +} + +resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = { + name: 'plan-${prefix}' + location: location + tags: tags + sku: { + name: 'B1' + } + properties: { + reserved: true + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-03-01-preview' = { + name: 'log-${prefix}' + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +module applicationInsightsResources 'appinsights.bicep' = { + name: 'applicationinsights-resources' + params: { + prefix: prefix + location: location + tags: tags + workspaceId: logAnalyticsWorkspace.id + } +} + + +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' = { + location: location + tags: tags + name: pgServerName + sku: { + name: 'Standard_B1ms' + tier: 'Burstable' + } + properties: { + version: '12' + administratorLogin: 'django' + administratorLoginPassword: databasePassword + storage: { + storageSizeGB: 128 + } + backup: { + backupRetentionDays: 7 + geoRedundantBackup: 'Disabled' + } + network: { + delegatedSubnetResourceId: virtualNetwork::databaseSubnet.id + privateDnsZoneArmResourceId: privateDnsZone.id + } + highAvailability: { + mode: 'Disabled' + } + maintenanceWindow: { + customWindow: 'Disabled' + dayOfWeek: 0 + startHour: 0 + startMinute: 0 + } + } + + dependsOn: [ + privateDnsZoneLink + ] +} + + +resource djangoDatabase 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2022-01-20-preview' = { + parent: postgresServer + name: 'django' +} + +output WEB_URI string = 'https://${web.properties.defaultHostName}' +output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsightsResources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING From f90e496640881d517dc700086b83396e575d0adf Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 00:57:32 +0000 Subject: [PATCH 012/112] Rebuild ARM templates --- Environments/AppVNet/azuredeploy.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json index 3ead99b1..650c72af 100644 --- a/Environments/AppVNet/azuredeploy.json +++ b/Environments/AppVNet/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "110809187980919240" + "version": "0.17.1.54307", + "templateHash": "17914070543937939267" } }, "parameters": { @@ -81,8 +81,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "949995683768579485" + "version": "0.17.1.54307", + "templateHash": "17810419246017990523" } }, "parameters": { @@ -369,8 +369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10803209143093915866" + "version": "0.17.1.54307", + "templateHash": "17765872346499755481" } }, "parameters": { From abb7548b6d8100a89b5584bb38f4a199a98b6724 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 08:58:22 +0800 Subject: [PATCH 013/112] add StaticWeb template --- Environments/StaticWeb/abbreviations.json | 135 + Environments/StaticWeb/app/api.bicep | 33 + .../StaticWeb/app/apim-api-policy.xml | 92 + Environments/StaticWeb/app/apim-api.bicep | 120 + Environments/StaticWeb/app/db.bicep | 40 + Environments/StaticWeb/app/web.bicep | 17 + Environments/StaticWeb/azuredeploy.json | 3739 +++++++++++++++++ .../StaticWeb/core/ai/cognitiveservices.bicep | 38 + .../core/database/cosmos/cosmos-account.bicep | 48 + .../cosmos/mongo/cosmos-mongo-account.bicep | 22 + .../cosmos/mongo/cosmos-mongo-db.bicep | 46 + .../cosmos/sql/cosmos-sql-account.bicep | 21 + .../database/cosmos/sql/cosmos-sql-db.bicep | 73 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 18 + .../cosmos/sql/cosmos-sql-role-def.bicep | 29 + .../database/postgresql/flexibleserver.bicep | 64 + .../core/database/sqlserver/sqlserver.bicep | 129 + .../StaticWeb/core/gateway/apim.bicep | 78 + .../StaticWeb/core/host/aks-agent-pool.bicep | 17 + .../core/host/aks-managed-cluster.bicep | 139 + Environments/StaticWeb/core/host/aks.bicep | 213 + .../core/host/appservice-appsettings.bicep | 15 + .../StaticWeb/core/host/appservice.bicep | 101 + .../StaticWeb/core/host/appserviceplan.bicep | 21 + .../StaticWeb/core/host/container-app.bicep | 78 + .../host/container-apps-environment.bicep | 27 + .../StaticWeb/core/host/container-apps.bicep | 31 + .../core/host/container-registry.bicep | 67 + .../StaticWeb/core/host/functions.bicep | 82 + .../StaticWeb/core/host/staticwebapp.bicep | 21 + .../applicationinsights-dashboard.bicep | 1235 ++++++ .../core/monitor/applicationinsights.bicep | 30 + .../StaticWeb/core/monitor/loganalytics.bicep | 21 + .../StaticWeb/core/monitor/monitoring.bicep | 31 + .../core/networking/cdn-endpoint.bicep | 51 + .../core/networking/cdn-profile.bicep | 33 + .../StaticWeb/core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 62 + .../core/security/keyvault-access.bicep | 21 + .../core/security/keyvault-secret.bicep | 30 + .../StaticWeb/core/security/keyvault.bicep | 25 + .../core/security/registry-access.bicep | 18 + .../StaticWeb/core/security/role.bicep | 20 + .../core/storage/storage-account.bicep | 61 + Environments/StaticWeb/main.bicep | 170 + Environments/StaticWeb/manifest.yaml | 14 + 46 files changed, 7418 insertions(+) create mode 100644 Environments/StaticWeb/abbreviations.json create mode 100644 Environments/StaticWeb/app/api.bicep create mode 100644 Environments/StaticWeb/app/apim-api-policy.xml create mode 100644 Environments/StaticWeb/app/apim-api.bicep create mode 100644 Environments/StaticWeb/app/db.bicep create mode 100644 Environments/StaticWeb/app/web.bicep create mode 100644 Environments/StaticWeb/azuredeploy.json create mode 100644 Environments/StaticWeb/core/ai/cognitiveservices.bicep create mode 100644 Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/StaticWeb/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/StaticWeb/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/StaticWeb/core/gateway/apim.bicep create mode 100644 Environments/StaticWeb/core/host/aks-agent-pool.bicep create mode 100644 Environments/StaticWeb/core/host/aks-managed-cluster.bicep create mode 100644 Environments/StaticWeb/core/host/aks.bicep create mode 100644 Environments/StaticWeb/core/host/appservice-appsettings.bicep create mode 100644 Environments/StaticWeb/core/host/appservice.bicep create mode 100644 Environments/StaticWeb/core/host/appserviceplan.bicep create mode 100644 Environments/StaticWeb/core/host/container-app.bicep create mode 100644 Environments/StaticWeb/core/host/container-apps-environment.bicep create mode 100644 Environments/StaticWeb/core/host/container-apps.bicep create mode 100644 Environments/StaticWeb/core/host/container-registry.bicep create mode 100644 Environments/StaticWeb/core/host/functions.bicep create mode 100644 Environments/StaticWeb/core/host/staticwebapp.bicep create mode 100644 Environments/StaticWeb/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/StaticWeb/core/monitor/applicationinsights.bicep create mode 100644 Environments/StaticWeb/core/monitor/loganalytics.bicep create mode 100644 Environments/StaticWeb/core/monitor/monitoring.bicep create mode 100644 Environments/StaticWeb/core/networking/cdn-endpoint.bicep create mode 100644 Environments/StaticWeb/core/networking/cdn-profile.bicep create mode 100644 Environments/StaticWeb/core/networking/cdn.bicep create mode 100644 Environments/StaticWeb/core/search/search-services.bicep create mode 100644 Environments/StaticWeb/core/security/keyvault-access.bicep create mode 100644 Environments/StaticWeb/core/security/keyvault-secret.bicep create mode 100644 Environments/StaticWeb/core/security/keyvault.bicep create mode 100644 Environments/StaticWeb/core/security/registry-access.bicep create mode 100644 Environments/StaticWeb/core/security/role.bicep create mode 100644 Environments/StaticWeb/core/storage/storage-account.bicep create mode 100644 Environments/StaticWeb/main.bicep create mode 100644 Environments/StaticWeb/manifest.yaml diff --git a/Environments/StaticWeb/abbreviations.json b/Environments/StaticWeb/abbreviations.json new file mode 100644 index 00000000..a4fc9dfe --- /dev/null +++ b/Environments/StaticWeb/abbreviations.json @@ -0,0 +1,135 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/StaticWeb/app/api.bicep b/Environments/StaticWeb/app/api.bicep new file mode 100644 index 00000000..2888ae4b --- /dev/null +++ b/Environments/StaticWeb/app/api.bicep @@ -0,0 +1,33 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param allowedOrigins array = [] +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param keyVaultName string +param serviceName string = 'api' +param storageAccountName string + +module api '../core/host/functions.bicep' = { + name: '${serviceName}-functions-node-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + allowedOrigins: allowedOrigins + alwaysOn: false + appSettings: appSettings + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + keyVaultName: keyVaultName + runtimeName: 'node' + runtimeVersion: '16' + storageAccountName: storageAccountName + } +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = api.outputs.identityPrincipalId +output SERVICE_API_NAME string = api.outputs.name +output SERVICE_API_URI string = api.outputs.uri diff --git a/Environments/StaticWeb/app/apim-api-policy.xml b/Environments/StaticWeb/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/StaticWeb/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
*
+
+ +
*
+
+
+ + + + + + + Call to the @(context.Api.Name) + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
diff --git a/Environments/StaticWeb/app/apim-api.bicep b/Environments/StaticWeb/app/apim-api.bicep new file mode 100644 index 00000000..051e4b09 --- /dev/null +++ b/Environments/StaticWeb/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('../../src/api/openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/StaticWeb/app/db.bicep b/Environments/StaticWeb/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/StaticWeb/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/StaticWeb/app/web.bicep b/Environments/StaticWeb/app/web.bicep new file mode 100644 index 00000000..e43e7370 --- /dev/null +++ b/Environments/StaticWeb/app/web.bicep @@ -0,0 +1,17 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param serviceName string = 'web' + +module web '../core/host/staticwebapp.bicep' = { + name: '${serviceName}-staticwebapp-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + } +} + +output SERVICE_WEB_NAME string = web.outputs.name +output SERVICE_WEB_URI string = web.outputs.uri diff --git a/Environments/StaticWeb/azuredeploy.json b/Environments/StaticWeb/azuredeploy.json new file mode 100644 index 00000000..f53ba56b --- /dev/null +++ b/Environments/StaticWeb/azuredeploy.json @@ -0,0 +1,3739 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "1765628537118726753" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "defaultValue": "test", + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "apiServiceName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "storageAccountName": { + "type": "string", + "defaultValue": "" + }, + "webServiceName": { + "type": "string", + "defaultValue": "" + }, + "apimServiceName": { + "type": "string", + "defaultValue": "" + }, + "useAPIM": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "web", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('webServiceName'))), createObject('value', parameters('webServiceName')), createObject('value', format('{0}web-{1}', variables('abbrs').webStaticSites, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "501613055779684124" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "serviceName": { + "type": "string", + "defaultValue": "web" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-staticwebapp-module', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "8622701495145799571" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Free", + "tier": "Free" + } + } + }, + "resources": [ + { + "type": "Microsoft.Web/staticSites", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "provider": "Custom" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "value": "[format('https://{0}', reference(resourceId('Microsoft.Web/staticSites', parameters('name')), '2022-03-01').defaultHostname)]" + } + } + } + } + } + ], + "outputs": { + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-staticwebapp-module', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_WEB_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-staticwebapp-module', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apiServiceName'))), createObject('value', parameters('apiServiceName')), createObject('value', format('{0}api-{1}', variables('abbrs').webSitesFunctions, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "appServicePlanId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'appserviceplan'), '2022-09-01').outputs.id.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "storageAccountName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" + }, + "allowedOrigins": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + ] + }, + "appSettings": { + "value": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]", + "AZURE_COSMOS_DATABASE_NAME": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]", + "AZURE_COSMOS_ENDPOINT": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.endpoint.value]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "798773494490796368" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "api" + }, + "storageAccountName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-functions-node-module', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "allowedOrigins": { + "value": "[parameters('allowedOrigins')]" + }, + "alwaysOn": { + "value": false + }, + "appSettings": { + "value": "[parameters('appSettings')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + }, + "appServicePlanId": { + "value": "[parameters('appServicePlanId')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "runtimeName": { + "value": "node" + }, + "runtimeVersion": { + "value": "16" + }, + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "188971145560630009" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "managedIdentity": { + "type": "bool", + "defaultValue": "[not(empty(parameters('keyVaultName')))]" + }, + "storageAccountName": { + "type": "string" + }, + "runtimeName": { + "type": "string", + "allowedValues": [ + "dotnet", + "dotnetcore", + "dotnet-isolated", + "node", + "python", + "java", + "powershell", + "custom" + ] + }, + "runtimeNameAndVersion": { + "type": "string", + "defaultValue": "[format('{0}|{1}', parameters('runtimeName'), parameters('runtimeVersion'))]" + }, + "runtimeVersion": { + "type": "string" + }, + "extensionVersion": { + "type": "string", + "defaultValue": "~4", + "allowedValues": [ + "~4", + "~3", + "~2", + "~1" + ] + }, + "kind": { + "type": "string", + "defaultValue": "functionapp,linux" + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "alwaysOn": { + "type": "bool", + "defaultValue": true + }, + "appCommandLine": { + "type": "string", + "defaultValue": "" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": false + }, + "enableOryxBuild": { + "type": "bool", + "defaultValue": "[contains(parameters('kind'), 'linux')]" + }, + "functionAppScaleLimit": { + "type": "int", + "defaultValue": -1 + }, + "linuxFxVersion": { + "type": "string", + "defaultValue": "[parameters('runtimeNameAndVersion')]" + }, + "minimumElasticInstanceCount": { + "type": "int", + "defaultValue": -1 + }, + "numberOfWorkers": { + "type": "int", + "defaultValue": -1 + }, + "scmDoBuildDuringDeployment": { + "type": "bool", + "defaultValue": true + }, + "use32BitWorkerProcess": { + "type": "bool", + "defaultValue": false + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-functions', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "allowedOrigins": { + "value": "[parameters('allowedOrigins')]" + }, + "alwaysOn": { + "value": "[parameters('alwaysOn')]" + }, + "appCommandLine": { + "value": "[parameters('appCommandLine')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + }, + "appServicePlanId": { + "value": "[parameters('appServicePlanId')]" + }, + "appSettings": { + "value": "[union(parameters('appSettings'), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('storageAccountName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-09-01').keys[0].value, environment().suffixes.storage), 'FUNCTIONS_EXTENSION_VERSION', parameters('extensionVersion'), 'FUNCTIONS_WORKER_RUNTIME', parameters('runtimeName')))]" + }, + "clientAffinityEnabled": { + "value": "[parameters('clientAffinityEnabled')]" + }, + "enableOryxBuild": { + "value": "[parameters('enableOryxBuild')]" + }, + "functionAppScaleLimit": { + "value": "[parameters('functionAppScaleLimit')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "linuxFxVersion": { + "value": "[parameters('linuxFxVersion')]" + }, + "managedIdentity": { + "value": "[parameters('managedIdentity')]" + }, + "minimumElasticInstanceCount": { + "value": "[parameters('minimumElasticInstanceCount')]" + }, + "numberOfWorkers": { + "value": "[parameters('numberOfWorkers')]" + }, + "runtimeName": { + "value": "[parameters('runtimeName')]" + }, + "runtimeVersion": { + "value": "[parameters('runtimeVersion')]" + }, + "runtimeNameAndVersion": { + "value": "[parameters('runtimeNameAndVersion')]" + }, + "scmDoBuildDuringDeployment": { + "value": "[parameters('scmDoBuildDuringDeployment')]" + }, + "use32BitWorkerProcess": { + "value": "[parameters('use32BitWorkerProcess')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "17727319403230913323" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanId": { + "type": "string" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "managedIdentity": { + "type": "bool", + "defaultValue": "[not(empty(parameters('keyVaultName')))]" + }, + "runtimeName": { + "type": "string", + "allowedValues": [ + "dotnet", + "dotnetcore", + "dotnet-isolated", + "node", + "python", + "java", + "powershell", + "custom" + ] + }, + "runtimeNameAndVersion": { + "type": "string", + "defaultValue": "[format('{0}|{1}', parameters('runtimeName'), parameters('runtimeVersion'))]" + }, + "runtimeVersion": { + "type": "string" + }, + "kind": { + "type": "string", + "defaultValue": "app,linux" + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [] + }, + "alwaysOn": { + "type": "bool", + "defaultValue": true + }, + "appCommandLine": { + "type": "string", + "defaultValue": "" + }, + "appSettings": { + "type": "object", + "defaultValue": {} + }, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": false + }, + "enableOryxBuild": { + "type": "bool", + "defaultValue": "[contains(parameters('kind'), 'linux')]" + }, + "functionAppScaleLimit": { + "type": "int", + "defaultValue": -1 + }, + "linuxFxVersion": { + "type": "string", + "defaultValue": "[parameters('runtimeNameAndVersion')]" + }, + "minimumElasticInstanceCount": { + "type": "int", + "defaultValue": -1 + }, + "numberOfWorkers": { + "type": "int", + "defaultValue": -1 + }, + "scmDoBuildDuringDeployment": { + "type": "bool", + "defaultValue": false + }, + "use32BitWorkerProcess": { + "type": "bool", + "defaultValue": false + }, + "ftpsState": { + "type": "string", + "defaultValue": "FtpsOnly" + }, + "healthCheckPath": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'logs')]", + "properties": { + "applicationLogs": { + "fileSystem": { + "level": "Verbose" + } + }, + "detailedErrorMessages": { + "enabled": true + }, + "failedRequestsTracing": { + "enabled": true + }, + "httpLogs": { + "fileSystem": { + "enabled": true, + "retentionInDays": 1, + "retentionInMb": 35 + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "serverFarmId": "[parameters('appServicePlanId')]", + "siteConfig": { + "linuxFxVersion": "[parameters('linuxFxVersion')]", + "alwaysOn": "[parameters('alwaysOn')]", + "ftpsState": "[parameters('ftpsState')]", + "minTlsVersion": "1.2", + "appCommandLine": "[parameters('appCommandLine')]", + "numberOfWorkers": "[if(not(equals(parameters('numberOfWorkers'), -1)), parameters('numberOfWorkers'), null())]", + "minimumElasticInstanceCount": "[if(not(equals(parameters('minimumElasticInstanceCount'), -1)), parameters('minimumElasticInstanceCount'), null())]", + "use32BitWorkerProcess": "[parameters('use32BitWorkerProcess')]", + "functionAppScaleLimit": "[if(not(equals(parameters('functionAppScaleLimit'), -1)), parameters('functionAppScaleLimit'), null())]", + "healthCheckPath": "[parameters('healthCheckPath')]", + "cors": { + "allowedOrigins": "[union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins'))]" + } + }, + "clientAffinityEnabled": "[parameters('clientAffinityEnabled')]", + "httpsOnly": true + }, + "identity": { + "type": "[if(parameters('managedIdentity'), 'SystemAssigned', 'None')]" + } + }, + { + "condition": "[not(empty(parameters('appSettings')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-appSettings', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "appSettings": { + "value": "[union(parameters('appSettings'), createObject('SCM_DO_BUILD_DURING_DEPLOYMENT', string(parameters('scmDoBuildDuringDeployment')), 'ENABLE_ORYX_BUILD', string(parameters('enableOryxBuild'))), if(not(empty(parameters('applicationInsightsName'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString), createObject()), if(not(empty(parameters('keyVaultName'))), createObject('AZURE_KEY_VAULT_ENDPOINT', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri), createObject()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "6698200352571171521" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the app service resource within the current resource group scope" + } + }, + "appSettings": { + "type": "object", + "metadata": { + "description": "The app settings to be applied to the app service" + } + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'appsettings')]", + "properties": "[parameters('appSettings')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('name'))]" + ] + } + ], + "outputs": { + "identityPrincipalId": { + "type": "string", + "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01', 'full').identity.principalId, '')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "value": "[format('https://{0}', reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01').defaultHostName)]" + } + } + } + } + } + ], + "outputs": { + "identityPrincipalId": { + "type": "string", + "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions', parameters('name'))), '2022-09-01').outputs.identityPrincipalId.value, '')]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions', parameters('name'))), '2022-09-01').outputs.uri.value]" + } + } + } + } + } + ], + "outputs": { + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions-node-module', parameters('serviceName'))), '2022-09-01').outputs.identityPrincipalId.value]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions-node-module', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_API_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-functions-node-module', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'appserviceplan')]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]", + "[resourceId('Microsoft.Resources/deployments', 'storage')]", + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "18301876310392488097" + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "10658647303664219792" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "6962395895547155086" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "6947634037528798693" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "13620265882151180505" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.0'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "appserviceplan", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('appServicePlanName'))), createObject('value', parameters('appServicePlanName')), createObject('value', format('{0}{1}', variables('abbrs').webServerFarms, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "sku": { + "value": { + "name": "Y1", + "tier": "Dynamic" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "8694592921587637446" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "kind": { + "type": "string", + "defaultValue": "" + }, + "reserved": { + "type": "bool", + "defaultValue": true + }, + "sku": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2022-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "kind": "[parameters('kind')]", + "properties": { + "reserved": "[parameters('reserved')]" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Web/serverfarms', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('storageAccountName'))), createObject('value', parameters('storageAccountName')), createObject('value', format('{0}{1}', variables('abbrs').storageStorageAccounts, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "12921139788046631420" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Cool", + "Hot", + "Premium" + ] + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": true + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": true + }, + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true + }, + "containers": { + "type": "array", + "defaultValue": [] + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false + }, + "deleteRetentionPolicy": { + "type": "object", + "defaultValue": {} + }, + "dnsEndpointType": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "AzureDnsZone", + "Standard" + ] + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2" + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2" + }, + "networkAcls": { + "type": "object", + "defaultValue": { + "bypass": "AzureServices", + "defaultAction": "Allow" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ] + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Standard_LRS" + } + } + }, + "resources": [ + { + "copy": { + "name": "container", + "count": "[length(parameters('containers'))]" + }, + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}/{2}', parameters('name'), 'default', parameters('containers')[copyIndex()].name)]", + "properties": { + "publicAccess": "[if(contains(parameters('containers')[copyIndex()], 'publicAccess'), parameters('containers')[copyIndex()].publicAccess, 'None')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('name'), 'default')]" + ] + }, + { + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "properties": { + "deleteRetentionPolicy": "[parameters('deleteRetentionPolicy')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "sku": "[parameters('sku')]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "dnsEndpointType": "[parameters('dnsEndpointType')]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": "[parameters('networkAcls')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "primaryEndpoints": { + "type": "object", + "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-05-01').primaryEndpoints]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "15428045515587502988" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "14795379806004450628" + } + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "10301535207560739586" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "11997294327656983299" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "13636254352620323826" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apimServiceName'))), createObject('value', parameters('apimServiceName')), createObject('value', format('{0}{1}', variables('abbrs').apiManagementService, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "5614285907031652965" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "publisherEmail": { + "type": "string", + "defaultValue": "noreply@microsoft.com", + "minLength": 1, + "metadata": { + "description": "The email address of the owner of the service" + } + }, + "publisherName": { + "type": "string", + "defaultValue": "n/a", + "minLength": 1, + "metadata": { + "description": "The name of the owner of the service" + } + }, + "sku": { + "type": "string", + "defaultValue": "Consumption", + "allowedValues": [ + "Consumption", + "Developer", + "Standard", + "Premium" + ], + "metadata": { + "description": "The pricing tier of this API Management service" + } + }, + "skuCount": { + "type": "int", + "defaultValue": 0, + "allowedValues": [ + 0, + 1, + 2 + ], + "metadata": { + "description": "The instance size of this API Management service." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "Azure Application Insights Name" + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2021-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('azd-service-name', parameters('name')))]", + "sku": { + "name": "[parameters('sku')]", + "capacity": "[if(equals(parameters('sku'), 'Consumption'), 0, if(equals(parameters('sku'), 'Developer'), 1, parameters('skuCount')))]" + }, + "properties": { + "publisherEmail": "[parameters('publisherEmail')]", + "publisherName": "[parameters('publisherName')]", + "customProperties": "[if(equals(parameters('sku'), 'Consumption'), createObject(), createObject('Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30', 'false'))]" + } + }, + { + "condition": "[not(empty(parameters('applicationInsightsName')))]", + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'app-insights-logger')]", + "properties": { + "credentials": { + "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" + }, + "description": "Logger to Azure Application Insights", + "isBuffered": false, + "loggerType": "applicationInsights", + "resourceId": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" + ] + } + ], + "outputs": { + "apimServiceName": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-api-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(parameters('useAPIM'), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'apim-deployment'), '2022-09-01').outputs.apimServiceName.value), createObject('value', ''))]", + "apiName": { + "value": "todo-api" + }, + "apiDisplayName": { + "value": "Simple Todo API" + }, + "apiDescription": { + "value": "This is a simple Todo API" + }, + "apiPath": { + "value": "todo" + }, + "webFrontendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "apiBackendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value]" + }, + "apiAppName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_NAME.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.17.1.54307", + "templateHash": "17100463825882340380" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "apiName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Resource name to uniquely identify this API within the API Management service instance" + } + }, + "apiDisplayName": { + "type": "string", + "maxLength": 300, + "minLength": 1, + "metadata": { + "description": "The Display Name of the API" + } + }, + "apiDescription": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Description of the API. May include HTML formatting tags." + } + }, + "apiPath": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + } + }, + "webFrontendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the web frontend" + } + }, + "apiBackendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the backend service implementing this API." + } + }, + "apiAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Resource name for backend Web App or Function App" + } + } + }, + "variables": { + "$fxv#0": "\n\n \n \n \n \n \n {origin}\n \n \n PUT\n GET\n POST\n DELETE\n PATCH\n \n \n
*
\n
\n \n
*
\n
\n
\n \n \n \n \n \n \n Call to the @(context.Api.Name)\n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n = 200 && context.Response.StatusCode < 300)\">\n \n \n \n \n \n \n \n = 400 && context.Response.StatusCode < 600)\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Failed to process the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n An unexpected error has occurred.\n \n \n
\n", + "$fxv#1": "openapi: 3.0.0\ninfo:\n description: Simple Todo API\n version: 3.0.0\n title: Simple Todo API\n contact:\n email: azdevteam@microsoft.com\n\ncomponents:\n schemas:\n TodoItem:\n type: object\n required:\n - listId\n - name\n - description\n description: A task that needs to be completed\n properties:\n id:\n type: string\n listId:\n type: string\n name:\n type: string\n description:\n type: string\n state:\n $ref: \"#/components/schemas/TodoState\"\n dueDate:\n type: string\n format: date-time\n completedDate:\n type: string\n format: date-time\n TodoList:\n type: object\n required:\n - name\n properties:\n id:\n type: string\n name:\n type: string\n description:\n type: string\n description: \" A list of related Todo items\"\n TodoState:\n type: string\n enum:\n - todo\n - inprogress\n - done\n parameters:\n listId:\n in: path\n required: true\n name: listId\n description: The Todo list unique identifier\n schema:\n type: string\n itemId:\n in: path\n required: true\n name: itemId\n description: The Todo item unique identifier\n schema:\n type: string\n state:\n in: path\n required: true\n name: state\n description: The Todo item state\n schema:\n $ref: \"#/components/schemas/TodoState\"\n top:\n in: query\n required: false\n name: top\n description: The max number of items to returns in a result\n schema:\n type: number\n default: 20\n skip:\n in: query\n required: false\n name: skip\n description: The number of items to skip within the results\n schema:\n type: number\n default: 0\n\n requestBodies:\n TodoList:\n description: The Todo List\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: The Todo Item\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n\n responses:\n TodoList:\n description: A Todo list result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoListArray:\n description: An array of Todo lists\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: A Todo item result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n TodoItemArray:\n description: An array of Todo items\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoItem\"\n\npaths:\n /lists:\n get:\n operationId: GetLists\n summary: Gets an array of Todo lists\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoListArray\"\n post:\n operationId: CreateList\n summary: Creates a new Todo list\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoList\"\n 400:\n description: Invalid request schema\n /lists/{listId}:\n get:\n operationId: GetListById\n summary: Gets a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n put:\n operationId: UpdateListById\n summary: Updates a Todo list by unique identifier\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n 400:\n description: Todo list is invalid\n delete:\n operationId: DeleteListById\n summary: Deletes a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 204:\n description: Todo list deleted successfully\n 404:\n description: Todo list not found\n /lists/{listId}/items:\n post:\n operationId: CreateItem\n summary: Creates a new Todo item within a list\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list not found\n get:\n operationId: GetItemsByListId\n summary: Gets Todo items within the specified list\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list not found\n /lists/{listId}/items/{itemId}:\n get:\n operationId: GetItemById\n summary: Gets a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemById\n summary: Updates a Todo item by unique identifier\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 400:\n description: Todo item is invalid\n 404:\n description: Todo list or item not found\n delete:\n operationId: DeleteItemById\n summary: Deletes a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 204:\n description: Todo item deleted successfully\n 404:\n description: Todo list or item not found\n /lists/{listId}/items/state/{state}:\n get:\n operationId: GetItemsByListIdAndState\n summary: Gets a list of Todo items of a specific state\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemsStateByListId\n summary: Changes the state of the specified list items\n tags:\n - Items\n requestBody:\n description: unique identifiers of the Todo items to update\n content:\n application/json:\n schema:\n type: array\n items:\n description: The Todo item unique identifier\n type: string\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n responses:\n 204:\n description: Todo items updated\n 400:\n description: Update request is invalid\n", + "apiPolicyContent": "[replace(variables('$fxv#0'), '{origin}', parameters('webFrontendUrl'))]", + "appNameForBicep": "[if(not(empty(parameters('apiAppName'))), parameters('apiAppName'), 'placeholderName')]" + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), parameters('apiName'))]", + "properties": { + "description": "[parameters('apiDescription')]", + "displayName": "[parameters('apiDisplayName')]", + "path": "[parameters('apiPath')]", + "protocols": [ + "https" + ], + "subscriptionRequired": false, + "type": "http", + "format": "openapi", + "serviceUrl": "[parameters('apiBackendUrl')]", + "value": "[variables('$fxv#1')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'policy')]", + "properties": { + "format": "rawxml", + "value": "[variables('apiPolicyContent')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'applicationinsights')]", + "properties": { + "alwaysLog": "allErrors", + "backend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "frontend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "httpCorrelationProtocol": "W3C", + "logClientIp": true, + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('name'), 'app-insights-logger')]", + "metrics": true, + "sampling": { + "percentage": 100, + "samplingType": "fixed" + }, + "verbosity": "verbose" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "condition": "[not(empty(parameters('apiAppName')))]", + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/web', variables('appNameForBicep'))]", + "kind": "string", + "properties": { + "apiManagementConfig": { + "id": "[format('{0}/apis/{1}', resourceId('Microsoft.ApiManagement/service', parameters('name')), parameters('apiName'))]" + } + } + } + ], + "outputs": { + "SERVICE_API_URI": { + "type": "string", + "value": "[format('{0}/{1}', reference(resourceId('Microsoft.ApiManagement/service', parameters('name')), '2021-08-01').gatewayUrl, parameters('apiPath'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'apim-deployment')]", + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_API_BASE_URL": { + "type": "string", + "value": "[if(parameters('useAPIM'), reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "REACT_APP_WEB_BASE_URL": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "USE_APIM": { + "type": "bool", + "value": "[parameters('useAPIM')]" + }, + "SERVICE_API_ENDPOINTS": { + "type": "array", + "value": "[if(parameters('useAPIM'), createArray(reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value), createArray())]" + } + } +} \ No newline at end of file diff --git a/Environments/StaticWeb/core/ai/cognitiveservices.bicep b/Environments/StaticWeb/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..e0afb877 --- /dev/null +++ b/Environments/StaticWeb/core/ai/cognitiveservices.bicep @@ -0,0 +1,38 @@ +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2022-10-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + scaleSettings: deployment.scaleSettings + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep b/Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6bc1f2eb --- /dev/null +++ b/Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,48 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..bd2a2b5f --- /dev/null +++ b/Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,22 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2c9688e0 --- /dev/null +++ b/Environments/StaticWeb/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,46 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..e8b030f6 --- /dev/null +++ b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..5a4de209 --- /dev/null +++ b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,73 @@ +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefintion 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefintion.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefintion.outputs.id diff --git a/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..6855edfe --- /dev/null +++ b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,18 @@ +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..cfb40335 --- /dev/null +++ b/Environments/StaticWeb/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,29 @@ +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/StaticWeb/core/database/postgresql/flexibleserver.bicep b/Environments/StaticWeb/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..effda63d --- /dev/null +++ b/Environments/StaticWeb/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,64 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/StaticWeb/core/database/sqlserver/sqlserver.bicep b/Environments/StaticWeb/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..64477a74 --- /dev/null +++ b/Environments/StaticWeb/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,129 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/StaticWeb/core/gateway/apim.bicep b/Environments/StaticWeb/core/gateway/apim.bicep new file mode 100644 index 00000000..64c958cd --- /dev/null +++ b/Environments/StaticWeb/core/gateway/apim.bicep @@ -0,0 +1,78 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/StaticWeb/core/host/aks-agent-pool.bicep b/Environments/StaticWeb/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..f6ff43a9 --- /dev/null +++ b/Environments/StaticWeb/core/host/aks-agent-pool.bicep @@ -0,0 +1,17 @@ +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-01-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-01-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/StaticWeb/core/host/aks-managed-cluster.bicep b/Environments/StaticWeb/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..b189af8e --- /dev/null +++ b/Environments/StaticWeb/core/host/aks-managed-cluster.bicep @@ -0,0 +1,139 @@ +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.25.5' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = '' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-02-01' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/StaticWeb/core/host/aks.bicep b/Environments/StaticWeb/core/host/aks.bicep new file mode 100644 index 00000000..f2f42066 --- /dev/null +++ b/Environments/StaticWeb/core/host/aks.bicep @@ -0,0 +1,213 @@ +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/StaticWeb/core/host/appservice-appsettings.bicep b/Environments/StaticWeb/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..2286e1f5 --- /dev/null +++ b/Environments/StaticWeb/core/host/appservice-appsettings.bicep @@ -0,0 +1,15 @@ +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/StaticWeb/core/host/appservice.bicep b/Environments/StaticWeb/core/host/appservice.bicep new file mode 100644 index 00000000..ad58765e --- /dev/null +++ b/Environments/StaticWeb/core/host/appservice.bicep @@ -0,0 +1,101 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + } +} + +module config 'appservice-appsettings.bicep' = if (!empty(appSettings)) { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/StaticWeb/core/host/appserviceplan.bicep b/Environments/StaticWeb/core/host/appserviceplan.bicep new file mode 100644 index 00000000..c444f406 --- /dev/null +++ b/Environments/StaticWeb/core/host/appserviceplan.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/StaticWeb/core/host/container-app.bicep b/Environments/StaticWeb/core/host/container-app.bicep new file mode 100644 index 00000000..119fab30 --- /dev/null +++ b/Environments/StaticWeb/core/host/container-app.bicep @@ -0,0 +1,78 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerName string = 'main' +param containerRegistryName string +param env array = [] +param external bool = true +param imageName string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param targetPort int = 80 + +@description('CPU cores allocated to a single container instance, e.g. 0.5') +param containerCpuCoreCount string = '0.5' + +@description('Memory allocated to a single container instance, e.g. 1Gi') +param containerMemory string = '1.0Gi' + +resource app 'Microsoft.App/containerApps@2022-03-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: 'single' + ingress: { + external: external + targetPort: targetPort + transport: 'auto' + } + secrets: [ + { + name: 'registry-password' + value: containerRegistry.listCredentials().passwords[0].value + } + ] + registries: [ + { + server: '${containerRegistry.name}.azurecr.io' + username: containerRegistry.name + passwordSecretRef: 'registry-password' + } + ] + } + template: { + containers: [ + { + image: imageName + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { + name: containerAppsEnvironmentName +} + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = managedIdentity ? app.identity.principalId : '' +output imageName string = imageName +output name string = app.name +output uri string = 'https://${app.properties.configuration.ingress.fqdn}' diff --git a/Environments/StaticWeb/core/host/container-apps-environment.bicep b/Environments/StaticWeb/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..dbe953d2 --- /dev/null +++ b/Environments/StaticWeb/core/host/container-apps-environment.bicep @@ -0,0 +1,27 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output name string = containerAppsEnvironment.name diff --git a/Environments/StaticWeb/core/host/container-apps.bicep b/Environments/StaticWeb/core/host/container-apps.bicep new file mode 100644 index 00000000..245ed7f2 --- /dev/null +++ b/Environments/StaticWeb/core/host/container-apps.bicep @@ -0,0 +1,31 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param logAnalyticsWorkspaceName string + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/StaticWeb/core/host/container-registry.bicep b/Environments/StaticWeb/core/host/container-registry.bicep new file mode 100644 index 00000000..c0ba201b --- /dev/null +++ b/Environments/StaticWeb/core/host/container-registry.bicep @@ -0,0 +1,67 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param adminUserEnabled bool = true +param anonymousPullEnabled bool = false +param dataEndpointEnabled bool = false +param encryption object = { + status: 'disabled' +} +param networkRuleBypassOptions string = 'AzureServices' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'Basic' +} +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/StaticWeb/core/host/functions.bicep b/Environments/StaticWeb/core/host/functions.bicep new file mode 100644 index 00000000..28a581bd --- /dev/null +++ b/Environments/StaticWeb/core/host/functions.bicep @@ -0,0 +1,82 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/StaticWeb/core/host/staticwebapp.bicep b/Environments/StaticWeb/core/host/staticwebapp.bicep new file mode 100644 index 00000000..91c2d0db --- /dev/null +++ b/Environments/StaticWeb/core/host/staticwebapp.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/StaticWeb/core/monitor/applicationinsights-dashboard.bicep b/Environments/StaticWeb/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..b7af2c1a --- /dev/null +++ b/Environments/StaticWeb/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1235 @@ +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/StaticWeb/core/monitor/applicationinsights.bicep b/Environments/StaticWeb/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..f76b292b --- /dev/null +++ b/Environments/StaticWeb/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/StaticWeb/core/monitor/loganalytics.bicep b/Environments/StaticWeb/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..770544cc --- /dev/null +++ b/Environments/StaticWeb/core/monitor/loganalytics.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/StaticWeb/core/monitor/monitoring.bicep b/Environments/StaticWeb/core/monitor/monitoring.bicep new file mode 100644 index 00000000..96ba11e5 --- /dev/null +++ b/Environments/StaticWeb/core/monitor/monitoring.bicep @@ -0,0 +1,31 @@ +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/StaticWeb/core/networking/cdn-endpoint.bicep b/Environments/StaticWeb/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..e92ee893 --- /dev/null +++ b/Environments/StaticWeb/core/networking/cdn-endpoint.bicep @@ -0,0 +1,51 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/StaticWeb/core/networking/cdn-profile.bicep b/Environments/StaticWeb/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..8d70f54c --- /dev/null +++ b/Environments/StaticWeb/core/networking/cdn-profile.bicep @@ -0,0 +1,33 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/StaticWeb/core/networking/cdn.bicep b/Environments/StaticWeb/core/networking/cdn.bicep new file mode 100644 index 00000000..2177c19f --- /dev/null +++ b/Environments/StaticWeb/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +// Module to create a CDN profile with a single endpoint +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/StaticWeb/core/search/search-services.bicep b/Environments/StaticWeb/core/search/search-services.bicep new file mode 100644 index 00000000..399a8f3f --- /dev/null +++ b/Environments/StaticWeb/core/search/search-services.bicep @@ -0,0 +1,62 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name diff --git a/Environments/StaticWeb/core/security/keyvault-access.bicep b/Environments/StaticWeb/core/security/keyvault-access.bicep new file mode 100644 index 00000000..aa989ebd --- /dev/null +++ b/Environments/StaticWeb/core/security/keyvault-access.bicep @@ -0,0 +1,21 @@ +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/StaticWeb/core/security/keyvault-secret.bicep b/Environments/StaticWeb/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..5f786ce5 --- /dev/null +++ b/Environments/StaticWeb/core/security/keyvault-secret.bicep @@ -0,0 +1,30 @@ +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/StaticWeb/core/security/keyvault.bicep b/Environments/StaticWeb/core/security/keyvault.bicep new file mode 100644 index 00000000..0eb4a86d --- /dev/null +++ b/Environments/StaticWeb/core/security/keyvault.bicep @@ -0,0 +1,25 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/StaticWeb/core/security/registry-access.bicep b/Environments/StaticWeb/core/security/registry-access.bicep new file mode 100644 index 00000000..056bd6c3 --- /dev/null +++ b/Environments/StaticWeb/core/security/registry-access.bicep @@ -0,0 +1,18 @@ +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(principalId, 'Acr', acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/StaticWeb/core/security/role.bicep b/Environments/StaticWeb/core/security/role.bicep new file mode 100644 index 00000000..dca01e18 --- /dev/null +++ b/Environments/StaticWeb/core/security/role.bicep @@ -0,0 +1,20 @@ +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/StaticWeb/core/storage/storage-account.bicep b/Environments/StaticWeb/core/storage/storage-account.bicep new file mode 100644 index 00000000..53d449ba --- /dev/null +++ b/Environments/StaticWeb/core/storage/storage-account.bicep @@ -0,0 +1,61 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/StaticWeb/main.bicep b/Environments/StaticWeb/main.bicep new file mode 100644 index 00000000..571252a1 --- /dev/null +++ b/Environments/StaticWeb/main.bicep @@ -0,0 +1,170 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string = 'test' + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +param apiServiceName string = '' +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param appServicePlanName string = '' +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param logAnalyticsName string = '' +param storageAccountName string = '' +param webServiceName string = '' +param apimServiceName string = '' + +@description('Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API') +param useAPIM bool = false + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) +var tags = { 'azd-env-name': environmentName } + +// The application frontend +module web './app/web.bicep' = { + name: 'web' + params: { + name: !empty(webServiceName) ? webServiceName : '${abbrs.webStaticSites}web-${resourceToken}' + location: location + tags: tags + } +} + +// The application backend +module api './app/api.bicep' = { + name: 'api' + params: { + name: !empty(apiServiceName) ? apiServiceName : '${abbrs.webSitesFunctions}api-${resourceToken}' + location: location + tags: tags + applicationInsightsName: monitoring.outputs.applicationInsightsName + appServicePlanId: appServicePlan.outputs.id + keyVaultName: keyVault.outputs.name + storageAccountName: storage.outputs.name + allowedOrigins: [ web.outputs.SERVICE_WEB_URI ] + appSettings: { + AZURE_COSMOS_CONNECTION_STRING_KEY: cosmos.outputs.connectionStringKey + AZURE_COSMOS_DATABASE_NAME: cosmos.outputs.databaseName + AZURE_COSMOS_ENDPOINT: cosmos.outputs.endpoint + } + } +} + +// Give the API access to KeyVault +module apiKeyVaultAccess './core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVault.outputs.name + principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan './core/host/appserviceplan.bicep' = { + name: 'appserviceplan' + params: { + name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' + location: location + tags: tags + sku: { + name: 'Y1' + tier: 'Dynamic' + } + } +} + +// Backing storage for Azure functions backend API +module storage './core/storage/storage-account.bicep' = { + name: 'storage' + params: { + name: !empty(storageAccountName) ? storageAccountName : '${abbrs.storageStorageAccounts}${resourceToken}' + location: location + tags: tags + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Creates Azure API Management (APIM) service to mediate the requests between the frontend and the backend API +module apim './core/gateway/apim.bicep' = if (useAPIM) { + name: 'apim-deployment' + params: { + name: !empty(apimServiceName) ? apimServiceName : '${abbrs.apiManagementService}${resourceToken}' + location: location + tags: tags + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Configures the API in the Azure API Management (APIM) service +module apimApi './app/apim-api.bicep' = if (useAPIM) { + name: 'apim-api-deployment' + params: { + name: useAPIM ? apim.outputs.apimServiceName : '' + apiName: 'todo-api' + apiDisplayName: 'Simple Todo API' + apiDescription: 'This is a simple Todo API' + apiPath: 'todo' + webFrontendUrl: web.outputs.SERVICE_WEB_URI + apiBackendUrl: api.outputs.SERVICE_API_URI + apiAppName: api.outputs.SERVICE_API_NAME + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output REACT_APP_API_BASE_URL string = useAPIM ? apimApi.outputs.SERVICE_API_URI : api.outputs.SERVICE_API_URI +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output REACT_APP_WEB_BASE_URL string = web.outputs.SERVICE_WEB_URI +output USE_APIM bool = useAPIM +output SERVICE_API_ENDPOINTS array = useAPIM ? [ apimApi.outputs.SERVICE_API_URI, api.outputs.SERVICE_API_URI ]: [] diff --git a/Environments/StaticWeb/manifest.yaml b/Environments/StaticWeb/manifest.yaml new file mode 100644 index 00000000..ed4f2356 --- /dev/null +++ b/Environments/StaticWeb/manifest.yaml @@ -0,0 +1,14 @@ +name: todo-nodejs-mongo-swa-func +version: 1.0.0 +summary: Static Web +description: Deploy static web application +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: false + default: 'test' \ No newline at end of file From f2fbbed472562b6799936658358ea2b7d4ffb862 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 09:08:51 +0800 Subject: [PATCH 014/112] refine parameters and readme --- Environments/OpenAISearch/README.md | 57 +------------------ Environments/OpenAISearch/azuredeploy.json | 65 +++++++++++----------- Environments/OpenAISearch/main.bicep | 2 +- Environments/OpenAISearch/manifest.yaml | 3 +- 4 files changed, 37 insertions(+), 90 deletions(-) diff --git a/Environments/OpenAISearch/README.md b/Environments/OpenAISearch/README.md index f7712ec9..7330aec3 100644 --- a/Environments/OpenAISearch/README.md +++ b/Environments/OpenAISearch/README.md @@ -1,59 +1,4 @@ # OpenAI Search This template helps the developer quickly set up the infrastructure about OpenAI Search with enterprise own data for searching. -This repo is the just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/azure-search-openai-demo) - -## Prerequisites -Ensure that your machine has installed the following items -- [Python 3+](https://www.python.org/downloads/) - - **Important**: Python and the pip package manager must be in the path in Windows for the setup scripts to work. - - **Important**: Ensure you can run `python --version` from console. On Ubuntu, you might need to run `sudo apt install python-is-python3` to link `python` to `python3`. -- [Node.js](https://nodejs.org/en/download/) -- [Git](https://git-scm.com/downloads) -- [Powershell 7+ (pwsh)](https://github.com/powershell/powershell) - For Windows users only. - - **Important**: Ensure you can run `pwsh.exe` from a PowerShell command. If this fails, you likely need to upgrade PowerShell. - ->NOTE: Your Azure Account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator) or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner). - -- [Dev Center](https://learn.microsoft.com/en-us/azure/deployment-environments/quickstart-create-and-configure-devcenter), [Project](https://learn.microsoft.com/en-us/azure/deployment-environments/quickstart-create-and-configure-projects) and [Catalog](https://learn.microsoft.com/en-us/azure/deployment-environments/how-to-configure-catalog) - - **Important**: Ensure the user is assigned role "Deployment Environments User" to the project -- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) and [Azure CLI extension for Dev Center](https://learn.microsoft.com/en-us/azure/deployment-environments/how-to-install-devcenter-cli-extension) -- Clone the application and data [repo](https://github.com/luxu-ms/azure-search-openai-demo) - -## How to setup -Typically, there are two ways to quickly setup your own OpenAI and Cognitive search application. -1. Leverage the script "deploy.ps1" in the root folder to automatically deploy. -2. Manually deploy the infra, application and data. - -### Leverage the script "deploy.ps1" in the root folder to automatically deploy -1. Use "az login" to login -2. Run the script "deploy.ps1" in the PowerShell by the command below: -``` -.\deploy.ps1 -``` ->NOTE: "principal id" is the user id that you use in step 1. If you do not know the user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find the "Object ID" - -### Manually deploy the infra, application and data -1. Use [Dev Portal](https://devportal.microsoft.com/) to deploy the infra -* Click "+New" -> "New environment" -* Give a name for the environment name (e.g. dev1) -* Select the environment type (e.g. Dev/Test) -* Select catalog item (e.g. azure-search-openai-demo) -* Click "Next" -* Input the required parameters (e.g. "environmentName" is dev1, "principalId" is your user's Object ID which can be found in the Azure Active Directory's users) -* Click "Create" - -2. Deploy the application to App service -* Go to the folder "app" in root folder, run "start.ps1" to build backend and frontend -* In VS Code, right click "backend" folder and select "Deploy to Web App.." (Note: If no such option, please install "Azure Tools" extension in VS Code) -* Follow the guide to deploy to your app service which is created in step 1 - -3. Upload the test data to storage account -In step 1, find the created resource: storage account name, search service name and form recognizer service name. -Execute the commands below to upload the test data. -``` -$storageAccountName='' -$searchServiceName='' -$formRecognizerServiceName='' -.\scripts\prepdocs.ps1 $storageAccountName $searchServiceName $formRecognizerServiceName -``` +>NOTE: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find "Object ID". \ No newline at end of file diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 5569613e..6e07d05a 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,13 +4,14 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "12584815192835029184" + "version": "0.16.2.56959", + "templateHash": "5616284125706811061" } }, "parameters": { "environmentName": { "type": "string", + "defaultValue": "test", "metadata": { "description": "Name of the the environment which is used to generate a short unique hash used in all resources." }, @@ -284,8 +285,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "8694592921587637446" + "version": "0.16.2.56959", + "templateHash": "1837336361730027989" } }, "parameters": { @@ -389,8 +390,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3317056714714509123" + "version": "0.16.2.56959", + "templateHash": "935190995104945414" } }, "parameters": { @@ -649,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11076527127870690555" + "version": "0.16.2.56959", + "templateHash": "15292359723056804408" } }, "parameters": { @@ -771,8 +772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11076527127870690555" + "version": "0.16.2.56959", + "templateHash": "15292359723056804408" } }, "parameters": { @@ -900,8 +901,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2612000844202476215" + "version": "0.16.2.56959", + "templateHash": "11912420961517980450" } }, "parameters": { @@ -1024,8 +1025,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17856846537035111526" + "version": "0.16.2.56959", + "templateHash": "14234446244824387754" } }, "parameters": { @@ -1196,8 +1197,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1260,8 +1261,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1324,8 +1325,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1388,8 +1389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1452,8 +1453,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1516,8 +1517,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1580,8 +1581,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1647,8 +1648,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1714,8 +1715,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep index e9f046e7..37a430ca 100644 --- a/Environments/OpenAISearch/main.bicep +++ b/Environments/OpenAISearch/main.bicep @@ -1,7 +1,7 @@ @minLength(1) @maxLength(64) @description('Name of the the environment which is used to generate a short unique hash used in all resources.') -param environmentName string +param environmentName string = 'test' @description('Primary location for all resources') param location string = resourceGroup().location diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index 020af909..144d06d3 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -10,7 +10,8 @@ parameters: name: environmentName description: 'Name of the Environment' type: string - required: true + required: false + default: 'test' - id: principalId name: principalId From af6822b3be3c7961ecf3ef83370e6a98d1bae56d Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 01:09:55 +0000 Subject: [PATCH 015/112] Rebuild ARM templates --- Environments/OpenAISearch/azuredeploy.json | 64 +++++++++++----------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 6e07d05a..fad08eb4 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5616284125706811061" + "version": "0.17.1.54307", + "templateHash": "17837155476675764237" } }, "parameters": { @@ -285,8 +285,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "1837336361730027989" + "version": "0.17.1.54307", + "templateHash": "8694592921587637446" } }, "parameters": { @@ -390,8 +390,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "935190995104945414" + "version": "0.17.1.54307", + "templateHash": "3317056714714509123" } }, "parameters": { @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "15292359723056804408" + "version": "0.17.1.54307", + "templateHash": "11076527127870690555" } }, "parameters": { @@ -772,8 +772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "15292359723056804408" + "version": "0.17.1.54307", + "templateHash": "11076527127870690555" } }, "parameters": { @@ -901,8 +901,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "11912420961517980450" + "version": "0.17.1.54307", + "templateHash": "2612000844202476215" } }, "parameters": { @@ -1025,8 +1025,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "14234446244824387754" + "version": "0.17.1.54307", + "templateHash": "17856846537035111526" } }, "parameters": { @@ -1197,8 +1197,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1261,8 +1261,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1325,8 +1325,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1389,8 +1389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1453,8 +1453,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1517,8 +1517,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1581,8 +1581,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1648,8 +1648,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1715,8 +1715,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { From 3d714c085adcaf01b9056694a21a063237d55c34 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 10:50:46 +0800 Subject: [PATCH 016/112] add OpenAISummarization --- Environments/OpenAISummarization/README.md | 7 + .../OpenAISummarization/abbreviations.json | 135 +++ .../assets/SummarizationArchitecture.png | Bin 0 -> 140629 bytes .../OpenAISummarization/azuredeploy.json | 883 ++++++++++++++++++ .../core/ai/openai-account.bicep | 32 + .../core/search/search-services.bicep | 35 + .../core/security/role.bicep | 12 + .../core/storage/storage-account.bicep | 54 ++ Environments/OpenAISummarization/main.bicep | 141 +++ .../OpenAISummarization/manifest.yaml | 41 + 10 files changed, 1340 insertions(+) create mode 100644 Environments/OpenAISummarization/README.md create mode 100644 Environments/OpenAISummarization/abbreviations.json create mode 100644 Environments/OpenAISummarization/assets/SummarizationArchitecture.png create mode 100644 Environments/OpenAISummarization/azuredeploy.json create mode 100644 Environments/OpenAISummarization/core/ai/openai-account.bicep create mode 100644 Environments/OpenAISummarization/core/search/search-services.bicep create mode 100644 Environments/OpenAISummarization/core/security/role.bicep create mode 100644 Environments/OpenAISummarization/core/storage/storage-account.bicep create mode 100644 Environments/OpenAISummarization/main.bicep create mode 100644 Environments/OpenAISummarization/manifest.yaml diff --git a/Environments/OpenAISummarization/README.md b/Environments/OpenAISummarization/README.md new file mode 100644 index 00000000..5c2ed952 --- /dev/null +++ b/Environments/OpenAISummarization/README.md @@ -0,0 +1,7 @@ +# OpenAI Summarization +This repository contains a Python Notebook that shows you how easy it is to deploy and use Azure OpenAI along with Azure Cognitive Search, Azure Storage and Visual Studio Code to make sense of large amounts of data. + +![Architecture](assets/SummarizationArchitecture.png) + +>NOTE1: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find "Object ID". +>NOTE1: Model "text-davinci-001", "text-search-curie-doc-001" and "text-search-curie-query-001" in this demo used are only located in South Central US and West Europe. Pleasel provision in the two regions or you can modify the models accordingly. \ No newline at end of file diff --git a/Environments/OpenAISummarization/abbreviations.json b/Environments/OpenAISummarization/abbreviations.json new file mode 100644 index 00000000..e0bcbf3c --- /dev/null +++ b/Environments/OpenAISummarization/abbreviations.json @@ -0,0 +1,135 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} diff --git a/Environments/OpenAISummarization/assets/SummarizationArchitecture.png b/Environments/OpenAISummarization/assets/SummarizationArchitecture.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e53339b7b25f450fd181d66a63077627b5652e GIT binary patch literal 140629 zcmeFZcT`jP7d?u-U_}H$0R^H02}tjtND)F25T&EEfHdhHEU0uOU@%mrDM}GSk#3<^ z14=JS?_H4o&czwW?{DV){nlIS$6B*y!a%tB+*9`1d!OrfRax%fe)|0s6ch*LuSly= zQ0yg8Q0#cJcQ<_Jmc98o_}4ajH90AY^oCQT@M0(KlF}s#ip&towOhO3^}ah-wCpJ; z7#<`4YzrBjHKCx`Op=$rbj?M7qI;JE^^KDivtNpn(y!f9>*_9kSNOEAJPk8LZffeA zQtN`($k*i+FW2b?)~N0vsw&Pk?_se$JoY#zXDWePOV!0*+u_gr{&KmPx%2l6_zy-^ z^tL}<-<6}r{_&FH+tG5WKVQ2@|KDH#`#k>tdvu{go4wwB(<*yst9P!g?z;Q->OVnn zF>zADO-W#~P-^e&2TMr@4aT49oZF6w_@n;*+!VgqHIo7g$py8nmt#!u+}~8Bd_!2j z^;&)ydNVjQxu>Pnlwu~(aNqBrXHIi_S&t{p?yn~M9_h1So9)zwcl-EfD22*q`W)#! z2*LLK(jO7_f4_~KN$>1rUT@90I1lFeq581ZrD+`+8E>xsOTf$P(yn{{ywvF=mCv-9&|Z0gBX#qLX~X`#-`b0#S_GGc{s zctUrYV6+GZn0oGtiIbQN?rf6!`K%#1=G$MnUeyRPZWtf#POMx@eJ%o zpM5C(A{Q!2FOTK5`NsOijM5G5jg@h(ao=wf<3f&OU(Rz7_$=E#N0VG;`&zEQ=lwJK z`20~mKdl)}GNmb5AM80SP`(SJIud77{DXkjFZ!7hont&$^As=Qc-wV(Fk&!7a^ndJ z*I4u6!JfgYP?jvm@$2SaUWMk%avN2I6M1pka=Tt=-XHmQFxDra30(z_Sbu6ZLw`#5 z#R=}D{p;?li<5&DK_^>YosK1V&ko$Gd^r4brbkvfRXe9f_km!qhx+b=^u|^P;|K9v zrE6-j-W#qdstJ#B_^UqAV0D>}l~YOY{G+5w)0b08c?gDNX~qvezG2|0fk$CYC1Qna zMxavR_PVN3>^9P!vtzJwJVW)=88vI^a>pC+(xqUM4qHC83w)dY<7KR z`{X0}QU!DQRrPZEa+(swq zRn%_U-2eJUhV@ifPn>0YR&};{^T5paPc6PPnE@!9qS;3a)4jbN^aGz?TpkjLbseH( z)5=QB;lFaFoT~6byiDww%a5*n|M(E4@%kEG%+-!fJNuQxXp@Z7WQR$GPy{wQ0EJQr zztDMBDbkoQo|&<#BDrpyyW>B?=Cz?84U=pTy@>*4L0Ld)X{i?da5=33U*}LAvENoW z8gAG#wK60P3_NBZBH^6u`=Zh;8-1%Y&nB)t`C-71@m3w32kuMV&h^i>?buy4*_oG? z7W~oAy0uR_fCi&T{8SR8mZU)BcH4AlXVj}0ZpX#(FUd?t1e51)-x_>>Z-=R+i;o7n zy~MLncxgPN)S6IZJD8$rNGmfT6-;n)sIzYrKbL7(jz7U|_$v9n}3CZgTPP!_q=G-e2Q?#_*kRhxXT^#TZ# z@&H<;4^jclNX|gg)xc#bg{C&e2fG z^w8v0QIDT@EG(XQZ>|f@%<2|6RCmy`CemSZtvb(+Hop{Hby%dq45#Ymt2s?|nKOno z)Wxr6nbzN~=zu#rUAz>_maw)w9LHl+aiIO3ZGyviOZ`OiwKQ#wh8R&CgWtsJ zj`5e{TlL+Qyw-o*$ak8I%&E*=sDH|FW59cJHI7dG?afQ`nY_gYkovr9u%OuE={ zZ1$0`)0eiBJO`>mO`|wsxRW8@Org4(q^5nq!5vL{c>2b0V_XDe+Lu6bql}BG>4z}C zKO1t8;l48^QF5wivYJnW-3!t^7j231Sk&bRJu*>JDju5;Z~ZASO|LK)68#!{Fq-gV z=ae;d9-uYfzMp92dE9WesnFSGv?IsjZMJ256PhU@lAs6tB%ld8~O+vKoZxle;ss=raUd6QjUjercn2cyPoF~HiDDCQ*5*|FGE;BDU$CV zi6lge&32)pD%5OB)#A;+^ZkC#ag~$*?f~%xn(-w4kpoQH(w{$;Kfm;#?!YOh?B_3b z?ZU*E`5E?>c(#Oo7@atS#Qbn|I9u2$QK!0&W324{C`dV5ogrDJIEn6wyae7bA@5YO zn%_nWDRKmjxkWaVAN!J0zs?|F`7DRuw*lW&>g{dca6{i!rX^L&Xwr7(8k$brE!$(} z!yYP8CrhnDr?mDVzXsFz%CRQtJ%<_jIw3b8jRiZ!*seamOP7F(4m+o!tuveb;$Lxd z_H_UX*BE;c&1{za*~e_<4%0o|m}0jdc=P7uT{PlSMzmN5w$qp=F7f3DG5#Z|XWe?E zUD*kNCj}b%9Ywd zpO>13T>VsJ!|Kw_4U5G}|KnHzs}5r;PoeK@gpn+<(WWHcJUz%>i#HEm8u~SO#1BT` zVqIkkXf4BB*QR`XoW14B!sy_K9hJkK`I>#^qYRz6I8W1xh4EGu>+S**tMcYY|8cQs zf)Lk(Fm?@gi#NF12(Ac#0d3*f?DOBnPRc`M)C)tSr4gchJZ_0nh#-?~xed#ZVymt5 zV4@=j+v7eh&U-e#91k6(7Mi+|!fB|!7dpkdAN%fA$sG8SqCPhiWfMsSNv)Kcb|}^# zB^P`u^qp;gR+l7oT^G*Hn?W!qJIN zOz%9*ihq5bZaPmm(pTtL180yM%4Hzoy0JW*q(w4qx!&k76aaO;i<$;{3@0s@K3}Kv zR2Q9)(c^}!qx3NhDBHfWxi0%gB2g*Ys<%YXZm7<@A%RZJ<&KX(%FTO$d*lS>0b2Ln z=Z<4t09^*#GL8DCr){kE5uyCkU3W-6dP7?mA}1(csrXU4%psL>Dvft{ZjtmN)}f~O zLq~eXZ8QK7LV!1FBuiWXL*TpprRvlha+9Amsr#MnWq_i%8p%wfO2XyGhqV5)I-X?@ zKO$?ekps-4P7`l)A5By;dGjS5iJ_Cm?NwboKo1zO;7gj$4b%Ea)UmUQ7m)r{ zN92{`)O~le;>q6>y{sfk>83#yhQih!S@V>m7XD6s z{#oVd^9iy+JSf^?MmKluaAEI_BFDrBa7eQa&<)m6MiqgkaSdWk#qQ4Pr^%IF+2-o@ zo{r}6aA#|}3!T#v?l)@2qnL_^v^f!AKl z#%$KkES7y_c4Kuq%IK_0tYz+TF8wYKOSy=LMhcKE@mRK%`eDdQw z-Y@d0Dn5XIwo|wl z_1meaV(tyt-YK>T&Clo=l6N^U^WPwF-`#VR|2`;)5uZK(_uIc};@@cTwWi&4k2n%Q z(W1@`18d9kL;X5=)>Y;kNfQJW0wwr)s-ULV z+d5D8a4EoKU$Mu235;8Q+1uM7aUBA&<6mmYxiSJV|?%9 zQ^I!7Ja_Cq@b>@ZtxPfp7AHFq5Tt&@bvVY^82V-l<713Y?z8x67_q0WqpPC?GlbKj zkC;}UqUd?}pRH_!N>FlrA&kuziT4ir0-yk*B=z$=a9HBWr3bsUSQwy{vqA?C<1=sC z8SD<@b`qeuEW`5s7T4~4dUP-hnP}NHUK^V#EW7UVR~z<-TNXk^O05w0RKl`{7ea1EmwX z^W3thsGWYB3+;bjcb>JLdN`DoM{M_eWCG4Z_~4g%+_~Nw9~HFLttMci))qT$?B)jK zd8bx>j3*y43_b-U`96Y!UAoR=8n4;?>7pCfj%R^K0*=yKwX3w?MYY z!;))P_tT)yv#BLsU@C=ST`f(Uf86rJ=)2)q_lCgc)wK?bj9syEa5ieyPtH83i{!5j zEOnWU7mk=(9SBpYo2|y9C{JxdX)SnqmBQvvUiKI5rF4x3JJprX0!{TGTE1wu;$+sX zPrEHLaEm;=z22L}!m_nY+9g*Tu%81NL|Z0W`()wB>jk>KR~M~#SLG@K>FT{Ot|M7I z4cVG=&{+2LWVEE|)DG50)R(|}Rs$Q7ejZWyyxx?eV;XzVbf)Psz z>a5A4=VCi9%c$}Y>F^BAe`FX?H|AucGT)=gr(ck@q_@bTk9(DRid^e|CM!ijN4^1| z3aFMaW*PryRF!~!EUgYARe%Vb$2TiZy#hW>w(@+<_}cD~CP4v=_{S@vU?_nG0)6sICaf}_H$+%R^Q)6IRL>aNECC; z%HbEn*tC%d**3irZK){04&pbYM-*9sr4y*U4r5UV`_Ni}Pl$CKFD{rZQmA=*;8Zi6 zf`b565-Gn3#&|rBi30qMp!OlfMsBDb_FWKcH?4LR?f7?V2#ME>&t|oqA5tPID%BD& zN?KC$K&eG(i8;iPKKr9Y)P1M0F(Nry1r^6)at4-qy#@QTXI|9KwPkC@KMdB)G^~c8 zNK1geb&OeEPjv>e4eC|aAV9Xgdx9`t~bD;LO6@`N|k>7+Z~KbqD7S7exLE^G`yyyZ$L z5OX_4ab{%uZ9P8!zQJ;O=uGnu(h(nACv#dAZKWt#Be)E57ax^s+O7SxwXnFOjW+Mh zO{0r|>To0g)tPHW%H56NW8x+0koB=Q8C&C&1hgn+VA{?c=eW+d3YWCvvS8HRlPG^| z{$&hM9I^$@)0{9WaQ9m}WhPz;WzMo_+L{s94ll&im1m^`Sa6e1paSTBUtVj}PB?QCMwT6Ejsc#c3D%9nq6AT%YCGWUjET)P;V;|=G&mwr-(b0Vo+MrKCO)=TNO|@JUVzX zHSI-|sB=17M$zWRDv#TO8L7UxpQ7kL=pd2rMEznn(@pp3;&bBei`k1i*$hgG+T);5Z!vIR&!ig4!yr(X zY2dY-6v!mm)4^`HG^J<$_4QM>5HlcP_~Ja~{IsMhg<-~zfMQeyZ7-FjtnRiN46^ot zoW;DhebN>xM=)h0>tB=XC~TzurD-6=y(PoIM&VdNS3U6focpL)_Xg64I>Zqaba5Q2 za7Xxe9b`zrb_~=;r~qkclFNaKU;Eh~V!FwZQN)aNT?ptaIk&$mhF;Eto7jbx>964= z0GW@?weE)OLHcGjk@ux=h9lfK4Yi6(+>P4|X5S3a0q4S_C&D9~vBx z{_;@5X*Cz$CXgPk&3rnXWk0N(Z`=Q0o}$`6R{J9aj_z;V0=AxA?$n1{+#nropFx!p=jAQ0Z$m&|;m#Gh57Uk1{#J{47v zW=4*0F?$RkpEwEq=P3;Fk#hmEjr=Ax)XRMVTy-0V7({k*G>tS#SaoEF8du|Y_4Hu5 zidjL^2!}a7s$DM{C$g>Rzs42_tgI@Si3XuVn9@9f5p$<(04-!`rmS1?!()2NIG`I5 zxTD_mnRv<4&5I#{gh!MB>BeI3O)n&``DVQr9gwXkuujH#Z1pq#oUsKc)!hq?%&;m8q2I4fC4zm zS5BojhlA8@i9HRajTgH-azd|U&y3^0yZ2SJpHNJwbi|?`FiC8|tXG~~1jd04qn&+$ z6b=G~bYDe~#o}ONoWUU`2}_23KRN#m(9RR2dtly70=;1l0uiz{2Edt0kLQw}I*%|U z&$lL_yIcwXBFi2bR(~S5U%8&9jZg$n$O&?_fMQHwK7?{b1tA{HzkkN_zpi=P5yt6JLjI9REi22JvUBm9`uw}1GLkj7pH}>zYwJG8_{3n(M7Y7q^l6`;njL{{ zEY4HQI-8N({#OXg=8c%D^0UtyaJjeu8mx`o$N<2c& z14DTC?f?FrEvXW8?;`@9xxas$f*EM?GbISw{4u$a=J)q6vTn(#mos->Y+GpmP5o2& zvTkXSaI$-&{^vtV|1D?V`p=8M|7so(d^M<-iVM@BFDiRVy(OC}`^lgG{YhOH%Be8> zx(b~eeX=edDfGEza5Bhi0{FY28F2(JeE;+qn`hl^Jlqh|(&CGtfi;j=?FOojT2L0< z`}alFCtwI>TFih!`i6&bh$5)Y=h_h?;i(O@6B>mj94HdVC|-=D#%N#Lec;5+pgHmN z)g{4@!Fs{oa7a8Yod;$_*#O=%#{(Z7Vql*xSxpy~gC;mIR3FvCC@uK!(~OeSK|^XP z-?Wu#&N8WeS%Q9RQcDA~q{`dzK6=azWR4U0=v)^gdP8F4r_u6UEtisCRXdF7P#U~K zk#VZdtgjkawk?R=dJ_hxmg_D%{_8K@K}>YTFWB7BIBjxkXOD43zL5ev34Po)D;Fh!7D zEq77UWasY2aUD(^-fMpSnDk-K+!^{G9UC0`_B#{8liZF-?X^`Xx*wD17`NO)LQ(8& zHLp_hiz-d196l_y*EKTi)fL*5j@$7+ED=}4H}Z=u#fW?y3k>#g&G^rF`Hi*Z`ratu zcM)CGl)h`#_m4>B20LZ>%6v3>;Vz?Ag`$(v$X8jsyg-? zylAA+PgsmoXH_maL{)kdH$SxZjL(x`s?w`CzaO6N%La}4zpiT|u6)jM$_fAc>eAie zpje+HQroFAZid{I6jewX>QBXOxqm_q{ZggVHRG{8W_}lO901xP0Mb?=nrJSAuw5Db zaoy>j;z3fQL>R5U%O!o+!Kd$w7CS8F>UbM>a_F2AbNL*NjnF3(FN*mc=OEDW!W6A; z`Bo!UlWfTZ!~60(jd}7>#0gof|N%n;WdHuUvTtm}Jb> zm7xt<#obJpdN0X=M;^m>JEs_Do9T773=GuHnzDh1gC6SZb7udiw@NJnN z(Y1^m2*)vrBLW!d5l?vt({Dl;j#MpDMc}~@pzn$O*+Q9SWM&JCpP)x{i!ox%MqINk zL-{SPs}tT%hjEH0K4ipU3Fy$Dv;9%)ZfU?yQwixO$Q&z9*M9dF?sq*}ZvjBD0=%_i zwRGhekGPZYd2oyjbv0z~j4wYuOy%1X2RQ%A{hj;ncc*)K@w+K;8I)L7Om*g|l>F?Y zB=t%x0Xk3;md`W4SE24In-LVdnU>ds2cQ{cwX)m*!`-(Z@)4XduF;E=2iQA^rRxf{sB5`>$nqsp@C6hLHSx`d>o z-5DkaV#=kN9{0QiV!Ap}KCDgx69G1uwo@k~18BK+(`YWJ#N1+uU+@~6)qdVa!iD53 zuRH^K-(o+H(|4nh&o5ezG8Z~e6M(>oBq}-3(Z?K@j_43}olA7B6WIW!??P+4t1Gi- zk%(wO!`us_1tW1;8FGtg0Snw&YJ`?cA%&yG7KGF>&4iA7AJ}(hV6^W3SHA%x zNU6N0kU1eqKxhegAzFfkBgMz|WY^zPjmuSZGUK>$kS&{V)J|zqklXi>C>SSKM zL`)1bx}Zx5hN}A1V

JNw3Zm%u)0`UY@CE zFgiv$`F2TKk7O$atvbN9AR*F;-P~ALl9$4XBExacIbVY41j=|}O*{6g_N&io!JJ0@ z1%ZJy><(v-VH$)YE{B7*H2-6wy*ZpG8V0!r-R-YOlqd4PY^s`5j4Nn@$t3Bn>wQ}T71#C)fr#vn#Ewv09@4#LQRUtRkYa3ODVchjn64uIros&%qkA9CrY3@zr7+31!5 zBo3Ffq*6JC<~*|urLz76hUTHhf&2aKA1x3=q%#60fsjAr^X@TVV_apUZs?&05j9Kv z8{8)Ng+ce)xOJMHYirq3b~l(<>($gD$@CgGe4mcn(0@o#e-2aVh*NMF@BeE zKd%-jDtBa?E9IBDn$jed9~OT}3<0L)Y z2T0L?oUgV94uyo9;RKI~c4H8bhuePGzl>`H8uD`>c|BAsa>P6V5C^mVFt%+=V%Ck6#|gdkCss`}2QftDEetfd*(Q6ZO1G^euO<8xmFfEZ(9c~yjDifkrD$LJTI3@J=}j! z#t-2D<#xZU9Ywrvfr#ZShyNXNe(QxE?dv#i_lkfSASi_(osN%RY0PE?t$~NPhqJnk zlm%)4tM(nTQO*pNTBbCX0kPz0^~h147tpH#!;eKQ^hU(tsHJ}eH~)FEAUcQxF?c=H z9knvgl(reSJ&uiz_(V=}32#V7IDe8-X>Pa|C%vGxW$wdsD?jL40(tL<{b%aMT02jR zKaro$!{*1yVY8^ZQC=UE)%us@vpo85<$KKscT&C+wC;Xt<*tIAwo_h7r)c^*4T<5|4aVh43nB@j1|(8g8AlAF4A=Ikg_0KUZPs$N4w`#nSz< z{6sNlL7*yltLp055L;$Hu%Cc+JYU5XLS-j5db?mHRCtjd3t{q}0gCx8=#qQSOu;NC zR!o!H!!%>#U6hDv)hVfJzplVfhnl}J9Y%S>+P~C$go{SMzzr)fA;y{0skl#Q(MDdQ z;3c)i1*x-j-KIf+slogTe%E6;<#0Y?~hvfajTJ4Eme~nDE3>& zu>c=UOwW_o3li19T2t3M-jZe!bmwyhS9iJ$BS!T5_fgWrR)pD@LWXXj!eH_`Tu$|E zG;{tvoq4nOi-9zL&64V7eYO)SbmwqZ|rpM`K?cn?hWqJyMGpFeN%xj zu)999I)hMqz;%^HU&f8zN#@l;EYuUtBRp(J!PR8Pu6>@&jgZI0gJs8J+NE(f_nEuW z4WPw<5b48;r+`5k#HxXY?HMnup z(k`JGVyanX{HaY%4`Tw%vfniqI9jN`w?)oSq}kt#abd$_fh^b3^6eu#AzH{*N`Zhj zUmS0h2lrHce~TTSL`loJv*9{Y2}!!49G<#v>s%NXP8kI0-~U%Bzk9BCmiI&R2J#ElDn2CqnoY|Yq>vC^vo)52_m*1I+jmOQpyo$_pV9w^FBLqA=ucHSi-A4|Vk z)w_eXdg}6Usw&V(lfMdj0r;Be<81~?0T-AG9@e(j1VAvt!k7P8bZ`@ zj%+ZEe6#KKm=DV5L^zOT&FM<#UQe89q^&8~7sHQgXCe?I6i8BSoy%N=^X|*cK@z6L z1Ueq$Cm_K_t!t<5OVf;B;4WDidvk9W1BN%Bb2LFF&ZL&GD;Aq!0ent*($tANlqSG%V6OeLz31o(UdJ3TC#RRd~DBD@H%c_ zdK)FmJkuHvj0D>?=T{5#Icmx=K?VGy+bCv~wn#B)C9ndrmafeOxE_-P>8+*ZvsK&M z58+oAPPn%O#LEOkhFtb1Y|BQ!a5#H72As;<8!Ayskzc6yBF;Vq+5#)!V~0-YAabsg zJ2C1BFf3Kn8VDuj;-v&Fq+U(Jt*9aE1u4`gf*_rJBteD>b44ibhI)eRL9GIZ6yoYL6$#g&ab^YpdPF5Fkp_lgS9+%;5#9-~99Qd%}+i zmyq%vth$Jog>(F&N!3I&S+HLM3S~DzD%`rY-PEBv7Q7ZfVvQJmwn#2Nwz^@l?@9)# zBh%wa=?wKouH)7+W`GpH6tm>8e|Mv?t_m=s9B?nRWB=wc8TP zO;5)$aVDGC+w0s~H0kYJFB`;5Xt#6emY92aZRCtwB3zHWc2QKgC7;?OMJ=vYo88{u zX({f$1XB^L8K|nG$x;t!WE{*xA#orsU-DXjG4LvM$#cLZa(8AgJmM~#+Ai@SdbNB7e8D}m#n-i^$g9Hzr$RV4<5AQx zM`dxEv(i|4ARgK!qa(t|<0*02m`i1j0!NZa&w$$Lr52p9NOLIUyqs7&j_9Qw^wPZ# zK98rgsxXlxc8fn=@9~MnhJ4NNdt!%rYCpPoB2d{|AADLNVl@Z0(;JaD%+8OaFhBnpLli~&r2?=u`mO2UJNZUw7zfDg+seC2X@Y zZ=D(+H)#`Nvcrp!k>8$lO5}S9%}T%B${PQ&Gnq4`-7SmlSt9!Dr=r{>$->?O`$m}C z-Zw)XN9H!2{lmA!(+xZvieoAr3qzs^Y+&TE@@LWBu9_aA5x`!M7(xT)$1?^$61@%; z`~5R1RyPlRo(Z9Mb3xbc`XpD1f@9dbWP$6(;%XroM^i1r^n=t*Eo0I%(Atl_@+7Ep#+0gsuE(&{Bwg{daURwEo| z#f1lZr2ZIj=8T&iY4fHfF~^3ipI$E>syU3QBx*@Jx=q7q2TcRJ>8N z#; zV+Y>+2)VXt6~D%R^M#^Lk@_N&xQ#fK^f>^2NBS83QFm=R-3N*mPUK7N|H^Q-_xK1a z9mnERm|&}hy3ZBc2|QUnyk^q)-M-0YjlNpP;%!-dud3a3hVxb{T;T^rRrZ#O?xAp* z`%Cy3kWZ>zKrBU6#U5n~n+=}IQeu(C~ zqW&nAK?{TV#DnVIzikU%g*5veP+hP|D247>*DJJ`+rJaB_=1J zZo!z9?IO6G)+(lzGw7?E@6<^@Se(58R&>r!1o>ot&%sU)21Q|6$sYTWX#V{ach7+h z0qQ9=Hr5!?U|?59p|8wO>7H~#ZI4-x+c?ILK{PiItg2|g9X_&ub60Yi1Jy4|%ilSC zx@0bTCp7%Fm|7EIlr7UP@99q&w@5nqri}~wJT1|jT4QyBq<{fS!w9+5?eec4hQ10` z|6azPHDY;Qgyh$7J03g6iV?4>dqp+8>I7BECgtb2=BR*SKF~KG-O5~ae^--|!MU7H z9;X(Xl*LYIdEM+c#My*Ph2}i=?bgE1vyb>|oOMfGZxB*N%}h~>TCX3MHFRF8Sr?OC zQPE_nR5r+z1ysPgMP`EZ8a5_rv3wO1geSR@o-l230H0x>_tdA{O@7RuMd=KS z&bkbqlv$h_BuFDK6*5mH0y+NVz}J@wujf9odWAAlaj599UH5*r)^2kTt?z!(x~srK z@3`3h-(1PG(`tjKR`KAg?9tNCXu4y07tgWv-0@jpDwRcYImpc}TEtyLwz2EfOf;W9 z70sxj0-vNa@%W5x4#*r!8z?VWH$VATByQ=Rzc^C4%(5l1nN1afl^84hJX^ch$T@?d z6&VjHbeIoyp)o#f!?0S@J~A;~NQ#Pj5@oPUY;i4TGqlBX zsJ5l}Wh#fyD}-{8>H__9MtLRbPUm6_A^lSXwkLA4DsNdtVQTC_;{02aER#mIdD23d zWiOeM=T4tCII$~Z^VJNN6Q;Mr$Mo$gcJ75EzsvQfCi{Du`$-cs=2o_#r1rMq$g+sD zsJw1`Mq-MXEYH_i6x2q0YK0;@V$^wmjSJ*CMI`4PlR`}i1RKbn{iT6VN3l-TF@;4p z8NdDx{vO;WTnIECFhynHq?dYDyl#z;dm|NhT=@4z`TZu9g*_WNxd)s zX%L^%azI|g%NH`~L*2I~&$9jV$=l-*LG4$_x}TD?m#JuMXaDLIWmeOgvcpT?`m;5C zQa=T@g(;jyeCWy|`;LKTORGf1FfxzJoLjs5K()@Hy%aNvTYF}IN!h=T&4tSyKlP^7 zrnbzrGf_I>D3zf1IeFcpki<>f`^HezBVUxM&-R&DE7%{*YSB4w&JhyoBja8JqZ(20 z_)td2IiRH8rZY1FyZ-A6Pp`xjy6Dklh@K7-ZFnNIEOKQIrisTi{U&v(7(3KAoS9Hp z=`UU zDM{1(bBVgeUUDtgqGwB&V$W4$og#c>3a*TvO)&mk=4jVfcie(nSFgkkTT$P`uCXDg zk3T+rc9znSAj^F!Pa$JqD#oR$?yb#ijxhOFmZO$DYKGJ#d7a#dwI279)DF9A>AeHP zrWAMc{xe5hUcxM=@J8PrM!0nRD@`tW1IKuy(Wr_yVl?bmuG$GWp@YJv9&=Nb-Vp?b z?D4|MQdw&rdwFCV{1^Mrm?8eSf%x&x_kvHXri-O0=O35mbf#TRhp40*8g}54`ky4J zz3aKK%skwSU$5MjoJ|5fCEqy?h1y2Z1F^SNDphX*sem}_h20ij{ajtu(Ybf-@jn>S zZz#F_`TepYE;w!n2gl5!oj0kr|Hqr$bKt z-I?F_+YSt%hy{0EhHttCdPL>~8K-oq+}_~U8p`Og33~rYyR`ou-z1;9oyN)Y0FP12 z7KcN>Z(4_Y$~C~saPbv*;0F1x?MC&jr4wWvNme8>!y@%lL|Fpc(IBicxA>l`1@W50 z%gc+$>@(ty3(h(2VmI;+yW|qFy0o4&{OTz_Sy4q^GyAdeYhfk3XHxr;@O$Mlbc`?& zichBZJB3b2E=Ksx=T>`f&i1)T|8vl3_7T=qO$Q$*wlIYaMu|R=NR!x~( z`(V$S*wT5luKUHYY$7uq`vP}s(Y3Hp9;;bSVaF-@UB@Wy(gNP~oj!Xz0JSvT8v&MR zg*VsJKopeVHlY+n)DCL)YgfSOw^CL=Hak@~9a$-4Shm+6<-+_2MdlKbvaL6PM`Mg- z7B2O`*R$B*N405&evkWh?i1bC%85YH+&MGSZtqaHPsU#fEG=JUUHzQ7M~!`q^&ZMa&0R{r(=Pblkq!E+XQ?RN8v;^VJb#D3R* z)p0~_fTo0uGKn0$I(#_B<-Ckurr(UAS1SZS#Un+J#-=iSIdF*jIf1q9szOjnlu#$tw05>p3E;KY!KW z+g*j57Z(|Xxz;N)Ai@qk0q7Cd^>q(%Gj2lUP7D3R5xcnwjZq#;B6TWnS-7VfVeE|6 z9t<9o1J#z@6k?{o4qY!3S(n)oklw!1%?E3`A?W;E7A>jVWRyL)O3n(}JQ5R)qqzoa zD!5=BSY!n{TN#Y0)7Nij>Xmr3N8bn`o3uL)fw?>PS*PfoZ+Eq7Dw9`#EdD5>7taV5 zcA0t3&+H4jAph;~d=>3@WCuK7(rpUacV*euoA}S5-Nz6dUBoKvb?Wr&eg}LY|4VnY z+>^&D1x%ILRI$lKh1yxWji#`#n%?2RZ{XqK4Nq@j`|kyc(S=GS8M7fC#5ZZiuRd26 zIgzX@xh<%RU@wd|Q6@wfpRXplEMTw)(J>d8@??YK@|p9dcNSkbMDaf1@-K3i28-xO z>Tj#!3=G~JD1~(f#J10B-SuwBrQRZ=_^eizaj4h&N`Czv*pK9Te24)Lr-Gem%S5~& z_bSa{lZ92(xuA4d3>=EC@caQ3fd8!9U)gyd_2%Y1y|00qJ=1jZD9Em--70Y7cWC^i%EMsTVs`nY@2wJ_Ct zq?>DPx9`^(6a(n8$uV!0H~(_0AQ984(|3G=HUIU&aBfm=RnnWhfvMs>2no)(>$hYu z_yi=`XUyfbT%~BxQw|I4xMFs@H64SPxY;}rM>4EZu)59@ApBcb{;}+d z1cK)4>uI;G$D36o`5#h?%K?Kn0Pn-sS-L!QGLNSc_JBDM4T8-L5jRbBVVY2cMH;gj z*4v8{?MQ*B9MVu`Iuul9vo)K#)u-FbO3cEjLSrxhn&L4@W5v)1fJk# z1w)Os3ALV=Yyg8BCnl{eyR7NOURL4_7PidA zow^U-+(4dYVw!@i$VS;I!sf@|MEkeeyN6B+)L98o7mCFrd!f2TF655>6E?j};!NKH z7XA^o<9k6|f;rp^pD1mB6s7^=T)Z41TIo7AS(_K-^_@|zp!!O?{m-HwS{KcJC**f$ zgzqm}OT&_-B?o3i_R>h~fI3v0N+d)jP z%a!!rB92R?I{PRtUi;PA#{5ya4=HJDqHIdc4I?nw-(KuGHmoIQ2_ij@QTF+GY}Y$_ zAbH#4fTFj+wq>Wz!D0*SvN#Ta7x{|=dp?|{?KKDfbeU28? z9^Em8OD3Poa`1&Kry+WxJZ?M>TkEjOoe*&$5X~qkRKaWe%Uv-N=o=+qg^v|?k0f4+ z#$5ny$h*#xMIdIG<zwg2_~}vdc-AcEY5no4Z`nr- znG%Ml+cFc7jX6B*Fd)l0{Om57#y@Aix3rwrmV&*Rqik7i5I`?vm>#@=%zd^FUcPW} z3~A|X>-|H1q*@=_;MV}O%uM%MIdtM+gezM5$*UTL>Yke^iRpZ@YJ`ci412gTB`Ej8 zV~PH1RmMwGl2673r>ma(jn}EMX`h$Z&no1b7SIWf-tTsgR)xUPYJ+|G0*g{=*EdZ` zn4WXSAKFBR&PP~3n*%rGVenS`It!uBB3BYTY{sC+b(leY;j^6#2#MQq+rjW{SP^KR zYw?{0q8}dYGxk0a8=(v~uYmXK%`a61E7>q_M|cDasFh(ifz_lYeCR~XHkpVl*C8UV zE*E}6M-J3bw-RKDzAm-zHW&_(hu^qGLA8R@w4Azc zVfE$mKSh~!MZ;3!Iu-3Cn$DtO=Bm$W)wn@?Zp}`4-NY~;I2u>br}=)5_RfbHe@1Qp z@H1Zr5cWO%+bgsPE&pYG)u26lJduCDO2OrGoWxCoWX|ShkAsO^y*j%UHCZAux_Jy( zC@7@=@?Yj^8g>>qz6objub>vlzWIU__6qc*Nc>}KcUV%|9`QLdffQ5pqeW+nk^|@C zz(Do$&B6)91h+aRYM;vK-tQ5uXGq5Ty)>+bM@O(z(xAZ-176DzK#x_?8BoGoTHdOn zKZGg&`ALtb@?N8&89Pt(j^sc;m>&rv&9ciISoxRLw>A`2--BQHYn-_Mb8{(ST+w*Ti+=FNJqd==uilx*AKeJS%x*AT(JjH^v+{})E)V?&l-ozGpoB$!8$Y+lC0F^LVH_N(P#KO4k5Tn?6D53nrZeZCm znfdks7HLa7S8HIGgSsyeMUB1dOU6NgsnP5#%qWYR3cbL~g z9t_1Il!&`@Fn>5U3>4z>j4NEYX9dg7`#x94+e~g4w2bQ^;L^=^o??&Ce^mT zBWxzSxvA>KG1?^8C1Eh|ogW#SP+!cVdt4wx)ir`s1Vh4}? zBGo@-%*bpnNA_q{1Z;XNtsFmhPQg5t=#RP$_SvB>=-GTS$8c^_U}UaAe6Ciu&o%F# zi}`0XlwTkPpy=LgasZ>Vgyc}fQ+)9U9{HtDkfBEr6@H6lQXxUnh>AN5*~Shr_0$+u zfUP`(9ccQh)3WzH37&sZ3)W2eWCc&uB_MCX#Ae=P{^*pjYc>5gii=maBFeA8up0Kr z24HD16jq~af#|OjC_K`XX(Y!>=}X{1%)#*B75-HXL}~?ZD9B=#uvFAztmf{>$WdFZ{re`Ym&L zetbOckao`rVLRh*5}33Bc#6}EkthW9D>`1&ow1j3TI9|K@cJ$P1am$CY??YOpr(A2 z|3guuT0d2eZ@|abQEUMVGsVmSRTQpY!?gOvK+=jrbkQUm!3Q3M_N#x0DIvfBWR)=H zACKhM?#AhaMT~IrZ#+YfJDtjtr3fXh&Ip_Lc;+Uubz)06KFizPILW$RkxsCuwLv~3 zQj5U&17Hhr)Jn+$5<+>dEWjq2Qm$qD*1~ITm5(u4;nYezT$e`@Lbivj!Wvx?ILU^J z(zD-lgRkReUKoqqA)p6-aoZQc@qG5Ba!gk65u%9WYvD~;o8>}adahfpdGlHMD1kKM zEt$r3*zhKzoEEy$d%RYycum0&-$MB2wKgk9GN9zU%hmK}IZD=4dq-kr?~+IO%sS1v zz0EF3iEl*6x)!8#(Q^Eqzrn@*mmeTUjmaH55Nl8J-k3zq8*F5=LTQ46}7vuP%wpT6(363Vf%| zfB}qmm4ZSAL?n3PU;O_=*jdI!xwh>du@;K4P(ctulp&-9L}~zo5QY#?X;2ywk?z4( zIvtvk7Affl3rA`YP&%cC?i>d8dE;8^SCUz@%5wn|6fC6361MW3oUpHH5cIZqT1cirJo_8O1td7)mY!b#C?ZFrw)eoV zczu<2rLRn_D(UR%gG6YvE+BV8X_0PAf;OGt->>BW#+g}+htH!SB=**RrKyV34fx$0 znIYE*0#L7PwQ+qYM%Gzy)tTcQTSCLJHr7_}u-)EfKX=r7AHtBy`#+|=zqOQflpbHy zZSkx1C#lYv?$Xf`1MYnGrxbG8k&<~wE%G78pxRAgE2=^jR{ZB{eO)3-)+LpoJTN6! zwc0Ol*jOK*Gew5+%o29$aInzLd8Z-LFRj{JUcrtmAQ6un3ZSJzEVKXbE+mx1pqncj zmbFEvxH=@Os8R9K#40xLDZ5D{qqz>lv50v!J?8rTS+Mfth>0?wJ2AsvtF#j0FS7M| z>JY9hC{3I0?mu5}7Dq$pc@$H?h5Rtj_#xZ$R&(FX9!G7WLD}5^s`&fR_~7`;)uKo6 zzlJBzDEc{tQOg_(*&1s9)~M&Q^9ku z`{T!{)@-7HVwt4gBjY!#x_m5E{~HbcQd0Z+3iC3%~wJlZ*l6>51~)* zJ;v8%DJ~m9qoik6VS=OM>kgmd8*eOpA5k^$2~IFssJy}w?2eq>++?_%I*#3@ zw6En&??q(i-&48z0hYaU$ld$@tl8kv$~N|idqgR#z`*IiE*G^c^+rgE_UfbRBjJv> zzV44Ti66d~$1%0Ic4z9UB5YYPU94XtuflEL%Wn5UHt_>dx6#PjlM+TB{Z9--B|#FS9=IbO3cMfTca*DLxiGzTWdp_F{$>8ECnI zIXSC+i(yy_I_TUW$cSZ&;l@ zo$~2OxO@hBf9?CS*fjd=sBM$pF65|p{Fi2?S>S37T>xXge%?O+rW;=Gq6(qX;@_D@ z8k#?fYG$ujyZte__8y);s>6&K`Guq*6&>tb(@gQ+hT&h43G7n@ zrCj!3@O1DyjP1kc4!>k&&Ice3hx6^Mg|HM1CE43srZ%oyO`3AivVvY+$YLez>3*HO zXK2s1Y$s2tP9r^C{e_Ex&t#5cjvA=OR-Uhv&k0Xleu;VVDGsHWo4wHf=sB`xDRrj= ziqwft_m*ohUAoT&TICQrl#;75v`VHR21N+PlU)6IFk=5Oy9_qM;v)BASSwtI$$5j9 zYUG6k#JJWquaWv8E@Q(LcPw0t>xIq{5GzTw4~8JR8^_Em+kkkOR{|# zOO#pdqy2;GnX-}H;$OA`c(ewMeNONhknrxY^e7D+t8pE@u8^Xk`yN%2 z9?EID6Vt}#`j%~0sl596M*R9%A;aW2Fhaebeawb}QC}#Z-X(-D@kBDPgBUWdBjCSvkf`C3 z$#zeY&&&L1P)(GhBbbbHr}G2^D~niMFLL)g>($Tw`(Uam)Ly9Gc4HZ1;*0QHyZhZv zmzvbktOjlhbf5TOT<5TQ!y~#|V@r-K?KXpq$KszkDVq|<`RpoZJyzBf1DOMJxD5`Z zso@96ieEsA5>#J!%Ek0+r~z{1C>kb~bBEvD_6k*wuQpJ{mzxK;3nAjvIkFLq+8Y{B z;8i%H42DGNRcplli!^&Zl^*R9PgfUfjBCM4%nsAVQLGF%LMJL3&FbM%kst<}Zp`v{ zb0Xet|EN+OVyt)Gme;OFIg8a!dRygoen)>jov(Jnv{w~uiR=c|n{UkD<^M7kLP6bHznfYTlKa)Mb`u1DFohn{ z=5vXB@=|2?w`)BN#gSzCn`aT=YdO7a!4#q^RMeB3ojs~}`&|w@e^*VoV(9BBn*$uk zN<+EOTz7SOu3h>9-s0RHasJtJiCv(O~UI{mHq`M7#^{ z?qtG`j4{rahvEB-Ws%w(Ca_YHjpbx?az8Cbs>Ez2d%DS3b$nM#RXtRdW1*&Cchy$2 zwY(S~EB2?Sf1DS2({#yF1_t&7rraj6$w!wtoi5HAixH|(3uK=6 z^tWDeen~qYR`>dJyOs=orAjkx6DTfk2)FW6@s6`B3*ci}3A<#&Jmh;~5!T~&o>G;Y zc{>;R@}wj&DTq9Vr8_;wR6*dIol&WCsEabj&%adQro-soKBdRPoguVGL8j z6V)oAAVY}^RiqTPntpBRwI4z1FRC*Vy|JC3y1jQi_{FoMM)k?J7y6~Jsnka$ylNW% zHX0@x-8_MzHaQ`N*!ODKHM1B4884rweVvT*i23w_UKE}gGh9P6%27j$i5tFYVa%B2 z<7*L3wT9k`AJ%b_L<=QO>r9!?#|RzF0lR0^K2W~h_O|;VJ{Hv}4@VxaRfhlTA14$d zfBE4I3N{n#dtee#%j=dzZqbTkkGj_+B**Bbe?*TfH)+)mZc|{q=(9jio34Q(X*X7H zZ`4xAGefZ!so$T-R(WFScyM_1w&&cCh@goC?pkx+W1L)U(-0H6LfAsWm(9KBpg;+J zF~)Y2-PTZn@ssAd+4k1J(6#fv9i>k7YILXrO#K-VV|E+b(}Ek3PTjHMfoBQbw&Yqp z+r87?W2a?m@%`$b-sdDpW4-Rn`R+NvKlSC+Am`o#Yrw3F&ndMo&=!992A`}ttzCEB zwrSmSWBpqgLA{~*iK?Y=g5|tC=8Su6yhR^cAwjlYXz#%d#f9OIOvBHZ!v*{XxCsvckcr+Z5Cdn(3H)$an0(*xZuCUKLna4U(r&F*^b z+#qD{!>i+RW2V<)!UoDA4ND2#ITH-sxUUB=3ma~P@6|9v{U<$mE_ z;bqhP_Wj8VoV=qVGv9+`E^-hwd4vYjM6)p2=aSJVM|k;STAS2?G3{eNzY)ZXy&<$eOHutdhNm6y4@2mv7%U&}{#x9BKngfm*2 zpUI6VQq<-eSb-*DIE5VjL@`<^NqL;zVY>6NaG30^s4wDw9V!bKYUsSZ2~!L!xzUMo zl;OG)O+#8aD`BZBl;NbHC#*^9zYZFl|KQ~jCvi6dQ}8xK%x-ST(4$dJn%tnSVaLzy zxt@Yo#?N2_pYDJ9m88t*Ze&0ZQ}E+b4Bc~D_|Bs-I|)r+aG862KO?g~Q~t)LEH>Es zuIwzizzSXRMS`GdtRwDtDC9Q?ZSm8xk^G2)p08 z$?h`qW?G8r zl3RmQ@~{Lu;it7>L?9FHeeoTrn6iehgL{RGfMRJs(<5%J_HYnPTxW&j(G62vK*gnL z@_O}7M?XH**vE#v)w-iqoNGPm6GahNulg#}lG=ZD(e_WOqf*&R{3r%JRG8m3MhSUp z9^kKSl{;Lw)FZ^4g8=nWI31p31F5^5;Ni&f(B!E(q;r zxAN&jN=aeASM--1xlae+)Er}ou8FAd@0-0kft>DDEzm(CANfmlbc_ zjpUAlYd+C5bKdrYSEQNwZP*G%mD&a+D*poc)}PRp+PifyVlL_)$PH4%OAn9eICdWQ zh^7C-G3uDXS$T`n?`BRf4le_>>PNPCspW%mR5wURI$q$$=WDF(*}e6M1qCfB8rrs;bw;a-%SOjDBldTp6g;E22P zjAOX=FTxQC+id+nvwP^cR`}lV7(UOAv39dpTbr$5V0S=EQ#$(6nhDsohu=%H4Hm=X z{FpK4#=YKK2sAI!3(RnpkV-E~?Di-64tW|~Cqb6TTgTyHv-~eABlJTm39wx@T);%= z2-%KQr6t}l_Pd^&3?%E+*aVX>7%~I5tzbn=EaH5DBhVOO+pq_1yL}cU6Nm?HpJ%lt zC+OA=t;HAj`yWhy)eM$DD?nKyoD&wb>zyKil7DLmPwW-}-zE-rc{|*MtzhDbc;i~Y zO(IBzQRzz%q%xh`37k51@S)>iog71cGX$9#taF}QIi=o%DXVTE%|fIH4u{F}Q$6FZ zl>3y8WgwbFe`u<2y}_HC)#Lf)xRU+tQ(Sm+n{&{|hxeJ>t_8hd)}K_52#81i_xR=7 zzw7YT4qp^e5Eb?&(T6)`&;2G{?+|c9wCm=56+GyJQ+14DCh)Q+HRg$@^Bfk<4~Ej{ z3vWu2*`IMyAnjE*_YeFTg(*NMj(r3BNZ}VETKJS=p!~{$MBrJUAv>9Pg6dyQ%@IA` zW|a25_LVRTyc2UyzEZ5@m~^(S5n+KRP&A!c?f~D#lE7BwxQR%)&sA#CEK=wozFX%> zMTJLODoXKia!b#Ap&?{4NeI8h3G1)Q1VU%(I=^Y!mN_s~;NO~X4fv|W=FV%kvkpib z+8TjY!^p`WC%Es(VQKj!JZoIDoW+S|I|=K$xxtyCi!QSgzNegXuVKX43|zoC?ohC2 zSmLuky~4dVTEl1K?M;iJvXahgiYrrrg6{Z2$6*QiV_ia0?5;{O7Eu2<#2{_bLF4&u2!`H3^WC{d|`H`5kiUK1+ubqT}WFf87+bGYweRT(z(*(a#grzIWg(l+r>_Bl~pHy9VAN=;jj1 zTo&hO)Rtf*<8u=FvwDcx0U=bFYTT2$)6rp^y@V9SV+AtSw`Dqu8om4-+HzLII&x1B zInf{CGbssdPBD%JN|BlKgI@b72Y!|g?{*!vf(HBLd*7ucgOaop+pkAVMTxgAYV$={ zMtx?<)T=C^k^vRv`IlnLjKE=K)XP!bYZ4RQ>;dr z<6NiQR0%k!j}E2ZG*+mW=GNTj6?Zd}#6>Ovxm}x@kcG28L2i3>5>yg5aYDCV?{_NO zL_cX&&|H}yST-Cu>C}$IlRQhr%=eTINfX6&IdUybo1geiw)?;w>IbM<&JOia_UAny z3$A;7I9t1M%^PuL?if2x7wfLDNLvKByNkomAPpku-A39|D@a#?8XymH1l7>Q*ZW=}FqSpa7oOSLgEYp$ovv(CRvr=Od^IwpJ z1i>~h^c?!2b@n;zc%D%Fv)jB;Kwi4jS;PMt#0Yg*PhguKSgTr1U`#S`5@+GdwOmJp zi@RjJ8$>V^PhcXLH-ILK8&Qc8%32t;9sNfUNx%pamlaL82;xUXq6T`Ia}WJTt@v>r zf%q!kbE&qWN{xi5s27go?~K4OBWHc&+j5Kxn7|+H*hl)2>Fr=bPKn!JP#VRh#YSTv zVDtT+-m!q7%a2{qSZrl{furn4z~WZ0{gn21@xtTttNkBWt<|p-Pi|GXQ2@?9Kt2~r zfPu`Z_W4M3Kz>qvXT?^TNd}>`p;0u@L?dk_Y^1)oX;PopZTS{K=o);4f{g z=KxmHwfIAo00N8NhK4`s_%tllWt-CEj;l=>`^fw80yFauUQaOx{tD89Bm#b0*+Y}A zotQ%Bfji3QhEVL<;s|~I6 zcm$AC9H&lJX#XlZ9*8?O+Xu zO*nJ4?Z-p$KQb5!WQ2T9)T@m)&|nQ!Onp(i_wQ@eUxdX#o>w^;u1H)sie|vaCkw06|T&!#&V%9?p&U?gohk zBfwlKk}u29EqsSw?Qt-l+6n)vE_GW>b>k(LI!)q05PK8Bw|}i#Bubk5FvrlF(6L`X zsy)z*@|x+dww-oLTNQ`v#+micT8pG*d_rUm0pQK< z#gT}>Aue1AClpT5&aB9+#^R6j&~FVl&2rjwyBh|M-StD!(7A3&ywRpxhL#{$zsz0} zT$Wsjr3#t*!L~2Oe$h_Iebl`4zYul=Z8InpvmX+xC%fe zr=AHDwKZJK#i0SiWgf7AsLjnYQ}29y(o*H+u_&g$2uKnP;A%$T|Dq;N^x>dI)wO_Q zU~174LYU}&@$xFwiMrHhvwZpf~b2p*Q#lDd@BxY!y0r{ zXYT*cHJywvDhLd-JIl6(_wer(?SS5W(~j&sCsqbS_3UNU zuhGA)(#=7}wL^zsYY*pXDf)iym(r3q%~xMkd(R2->21J#Onk+new6c+=)Qhd4aZoo zz6isGuq#iW%x;gyyrGQLt=HCuA?Gn1w|ru*Ocn|j^Ve~_q?5J~Wm$P@b7h(7UgsA# zWz%c9UtGXvQ&hgn@(sD)2;W|ZF2BSs+{!M}7)WDlN!_M~dn8ljz~CTcS5ZIZgitfu zwZ7Hc10{gN*^3tFFI?FN(F%_n;-&Nu-t*V-770YL4-Ub9%bj+d^~`6W+) z-(wiy5E>P(cEXpC&seGs$HxN(kxQ#8#a50uTrg;pRRA2tZdB!Iu4h>ra_(g~BzFyv zCj}>YXKF$b1+NDhJ`%SyqQUFC%bdc%~pj`j2 zAFOgNylQWTF0z6#R`fwhkaa7EWhWC~q;mAI^j8syNkzQ$m9aKP%*yN$C0Zrj zubIgFt{=Y?YUkZ~JZ9%yaQT?%gYSYscxpEDHE%r7wf*T}U{D-E_7Yr&3w<8g7MM1k zq|3d*izvDpl-bL^sDM)zaZ92=FWyKecFH;ay*%65Pnx{y(5)YiElh%yA`&fQtriS$ zp#A86PC=7`k^}g1fKQQv(e1*S7~9WJ%qRSelU z;G{QAIpClN_+a<><&okd`B;EGyf{wQFS~gI#Qf-rr=Y9=c-{5vM`H)#8o6$b$G2E7 zofm&N^+ZQ{Kh@Do|NDL9b06NR6GmPq9Aj~8@rKe7xnydL>0KaZ|W4RJQ6t&@@G8I+xWrB!;pD07ew>cwnwGfkxaK020q=3 z1LWqrFqVTzvJ1jdq!NMwFSk~9Y~d|ryer%-f+UT`iVYg7wm?wMcvoRYp~x=?&ZwX3 z7;E{YVp%{6Zbv_!FAwd0n zGp{=k30-PONAAKVKB zuB=B*6m0qySyoXy*^gbI+2CCPvw$Z3)Sb4+Tu1vDG2yt&Q}&7grKXPm{6NQp84EZ3 z2b2SllqN1|!k}|yI`vz^P$D#&W>IjP)q^no-ij66W<$<;VDAWp%ddZ`y;Btl0h#tW zSt(`kf?#+!4QQ|k&(AtX(jZ$Qq7SL8SmLX}jbqw-C4ChX=HR0J!qcIsbdiZIjNyI+ z2t>ay=mGhBs1RMuYR3N_o8Ly=ua)mFsw$Xw=|F4JUbuGcI`>T z_fgQgA4n}I5*id?h7pHOF}Giv#q-yI1<0hDpC?K9QO8haAxuz1VBHTM(kEZ7mYgry z*n0*pUs{?UU+${x&b=9ev#kra3%Ap6N=G7EC}>jY6gdiAYO)R`0W*HT0oJPP;_A@# zZwcB9jtH$Vv)5TU+9@6dJ7eLLP}n}#4b)DKwEP@+zE9Rr))xhFLkvGK*;$7-FkXE#!krbON8^9`K*yp*}_<`IC%P>Ge3H346G@MuH-!HsO9y zg(_At?_uMvcrB_AyLZks76|tO1aRn?D^EV)bqM~9=T>KBdh4pvjrIAA3FEP zCCLm~?=p%w3RBf=rypV0yypkHZL1G?Rv^8#5hq)exI?PvLM`X$_VtbBr=;FuI3NGs)}#Sw?8EmcU9@taQH{ftvbM8z~rQ|9nxrATspqb38j{loRho?}?|U zFVKGY>ls;GWW_Dt)Gk~;Hs~iAC*gEM@iP&+B*kj%%vg(l=`YUNiesJ?@;O0$GGvoV zqu1xx6&oV2bbr$0?JRF!6q8hkD4Tq{q@m~zlenaMT9nE}Nn18~V`<(~y4~2P?}*w^ z?2taM4{JZvMXklQbL2N}D>^dJ9L%pCIDuwWK)*DuZ5?cz_Q;$4=7xA@+4=fZzzfd{ zc7o8rYq;oEj^?BxAkb7K)%iUb12>S5Oc~QgP$00DB*B>nk3X z0c{bPTf5DFmsW6Oz}y6%4s|`MEW8Pd(oCttJAlNC;03!RKyL)R*-ef_O2?7}$+j~N ziPM?x8nM$kJyOA}3eglQ;tD~n{cWwCHy}0mxVC97hU+*cF`izzAo=#2m7|LEbLeB7 zM*G)G7nM8n#=`Y02i;OnQ@JwzHflf~I_SEEWHOM6$*5Xfh?+M&R(E`x2I8!k*77$q za;nZF6P5g<6Lhf<*Kr9H((>D^E6J}h1!A+CBESa;T^wt(O??D{i zbE_0zL2;gQAW=Lg`3McHHeF(08O|RskV(#~TbsXP;`kN!o<*%GK9HO-327$6f)JsxX0ix^9}ssSYTzD2dl za~&Zoj?Tij7gyi@08>0zkE%aH0bzMIUCw1rgw_*__IRCAM0JL0r@ll2+l4np9`tl< zX6k|+b#XCw%L^uAZ2T|RlP95I9oW9RH0Byd)9f^kF)-JR_*JkiBVyBa5u`ii(ABP2 z2TEsy(P=*pZk#8&Uss3Az;c+GBg=(8`sTT(sA61c=N(3eNa+FCT9l__;@T(pZ9vJA$1uA6-y zYNvaSla3`gPEVBZ>wY>ZC9x(giER6sBs7&CQ^&&sH)6h-QFcjpxN%auh0rP%KyQkr z{G2asMLqf?Z2d|;q8v&;wJJ)RI$&YmnuXD!n1ypmp_g^3b7l6gj5UhujyC7)ue6D? z<3QK9lKe&7E0?N@EC)Hqh;A%t%}uXyx)bd#o`;tm-sH!Uh61MA`&*e1x|GK%>$lg| z#byl+jiDJ6mlbaSsgl1{lgTNP9}EY1@`LKl+%K2+$^;OF=x|VP?B|guxpxpjbw)S0 z8F|AHymVj!aol&9a;OSSLP{{fYbZjzf)YYv<--YP$Gs}gv2UDuh{E75niM|Dz27MW z*-qu!iN)KWkH|nMb}QioWC{waChcV$OS!k?ghRrJ2wQ;3+7swNgz>rwR#E_VE<$eh z-((giXt`NE=dKJKY;s;Q@lrQ5immn{7j1&B(!3mN2-UgnOEtsLHm@-ZfTwoYRN49h z ziO-$9f_`ADg_Cup0$F7qd?huFmrW(BSg43U9Jo9{PKN9-T69F;ehc^5D$We@vNVYT zQ4Icnc+oyTa6+j44MYSc;H(`>V`Ie~qI2PabSXDZYkHEGNE z38fTFN?#;@&tVkTkevB=j~VgBpeg8XE(lo4KHD1N2fnkoq0y_87w$YN-2X@vZhOkV zW;K3kEmSm;@!2txMRWc>6LahA*-)XF&aZB(AxzAC+-!(OfI<|bl1BIh+bunnaF|T* zLGORI_31?7S0t6%@Mv47(86e=wauZp?367)@kh};<0l*E%UEnYv1y8zYP6lOdVDp4 z(0tX0ICod=dFFF^3g4wTvoB#Vr)sJA&;hH{#ZQ)&P~aTVlx-IJ1_dJ!TLBAZ&XPp0 z;P@gWm~^mY!}zWAC8>UR>$9Y;AQ^FxEF~&HJxOUb^Kz_Ewaa?`CnW&w6~MtOp?dI= zgwzUw)tlG0no_i$r50ybV`%smS;=5YV`fmQnxA6H3Mf~-{?xg(;l3i$a3BJ8aP&u- zmB9Y1Sbo=s)*V@VjDiktZp6ptRa7BY0>-clz`XrZ%~7<|Bb-u54mt$mA<$nNWQ|9_ z&%XA`27TsU9$iwylQM&C)`WUlVjDE8KoR~f$eJj z4^T(dZ}vkWia;O=e+pA{sv*AYadFyqM04%#`(-_EALMA~CUU#bp1PzA(b|;ct`O>3&kwOE=3sI)|e%$wqEq`f8dk`@IHAeH%-rVN#XToOfK z!5bX{>eznz@#Sru_#3?Qhkq{)>7=1yJ`ziRJ>RO8!&+^PWZMsP4gzzAz`k+YKX%O- zLXq&FdFpSQA(&-aQvO(w3>ar@iN|6BusR@oc^QJI-^IaQe5S=gQv^X`Za%&H4MPQik=HvcV5$zA~vHrTeD6+M*uU=!uYD@UZ(+#v&wFhd(^yIwk$8PyyWmyg_S%v* z;0ae@qVuo!_&2mLh_HKGVKsFeiV3sr=EVg}dnX-~bBKG|^j!yG)038}&m4SX3to-h z%Inj>)j==~i14P=Nz%AAY4@X?odpN-`8&$fdI@M)aM`8KVkT%$l<}%sVss>SQUx!= zes-_I`MerTwu83K}@atKD1%K{%rVmsVf@ZJbIEYEH ziA5s4W7Q%$e{{P5Blx0(WXhrlq?o)Y4J=cu7mc?ZlwF;qIA4NeS$KC6KleM#NJ*mh zuhYNlOLjnnQmH}M?O!uOx)41VmT#5&2fdS!tOmqkSDTbp{i^zIlUb=tztN1s0Qt6d zK_);|4q3@S4S&8x4zbf()vtsSuSL|QWF!bUP*XLJ6XQIDOc&f2y}!xImF)*Hr_UX<<2dC>uROYJ5*X#3Ly*M56q~Olu0)=fqiS^SbcbhNFqX zW@3K6z6C_ab4qA*wY?+t^4Uj`G!F|{xF z!6re&XOi5i`wdyc{_^KBm{@{&f)E*MOcNWY4v706pi^{CC%{~A_Ra6*;~qJ`-H|k0^G(%C?twt!iXDSU6I@ z!d`PD$mkrhVc7HA6_jiiRBAt`y@vHWL{;%IYOy`J{=FGYRky&NZ2_~1;!UYLA%9tv z)qdMQkgRG*LqTHccMQg`Cj?>xQr6CD-eWtPX=W@MmE)I*#C`^8P(ZTS^s=p6+hnm++w7CY#LiV$vxkydFiw7DKcGRYqoBYQ^Av8)!r z7W1!TWxYxN<^4kunAEcRWJ2<8`{_~2qp|DOkM_erXeX|MMT!qci+A|p+25jJicf!; zyj^h3e%<{`gjb3Ebdhpo`7Oj(h1{;`N~__z-pV1%lB(c3{;)bFWpAkKh(8AmIKd_x z@pp%VtA=jZk1w`^yMAtX2XHc~4nl*XBVMc;en*u0HS>}RZY1t~3LS49@D~~{sX2;A zAWnKPVY9*A1)(24$cvL`2UdG)>!Su;a;w&kMd8rfM16YZ;ONE_mn%znR#?yo8ER*5 z@vZ~ZxS1#%zrgMME92L5xEQdx_G%{I0b^DhVl(faIW;Tw^&jUf%C*`>Rr4CD$(^_!$3OP__PZ z$p+Sx?d%q4_)&F zBAug$9Njr9>6NPOAhE*;@L$F&qb9`+N0`O#6ipV~ zAXIs|Zz=URz#wpu-W&662uy83hJqSrIJpy8hLo1~%w&%qaRf3myNCmNWdUzhp!tHZ@^po z^UM5V2)3#2rft+xr0eB<$}+qb0{gcQol^rBivUt8*f+{!dNxm^bbMws-ql{$DQWYX z(4(5W72}MDCrTA}9914ORt5~?`Vv@Cb_Kl0bME?}wX>o;_7j?4tB(2sWVM`H5qwS| z@J=(MU_oYr$yN7i<^CMt;D<5}RfPNHzdes53GrnkiNk%`mPi_p&KBoI-8ao3Ctz5XK1N8&-7T;u@J|aNnw5l$*;I7n_%?gESctj zY?7EFka%R)Nl^v9{$~ioX5r(0*2vtl2RhndHRaKYruGfohC!kk%mUk3nwBO%l`7L| zEY*q^P`_LZE6=uQ%ePR|JZ>iyB%dbOYJ1iFkmEZ(kMit83~k{?*+vY`*Z!ROPy76Y z0TY`f9Jwh4lxYXIf8C^Guz{q+r=)!IHfISjv5?9Uu0(>Ry6!!33ljk+bZFAV<*2$z zus7e~5oHZ&QIS{dp+4P6%ON*$D9kNmMaN^;hvMI8f}b^^u>Z5kd4&F71m zCog#!sq`oPFgo9EW{P%WuA%didDvg|c0DQ!batb$;7$>d3wm#wvYhixf&>~qTfNoyRj+4HrO5(*?(#WU1%$tpX zDVl_NuXtq}@P%k&`Q(!F{YaE~kX0l$UbZ>ZMJSc8e%yi2a2#*h`=FhCtVL8XW~Ti` za$vi2BKF)@ys=joQRC}r-m1)A94N!r>^NnsDVbo)0sYwv ztAElwoJfU5(#!_b*Da1VTLb>;pnKfZ>X~J)A227_qZjf4qL_nNjld^pVsnUScz{r< zZ7lFS4DDubeRB<*SBbL>lg?h%w@R^6mN!A1*(gxS36biVC-45(i=~w-n`V-iCeh;k zp^HOvMdqQ`LQB;7f`?$FqQH_bb@~$=-TV5~42W{JdTGk`*u?La>mTF$t_y=MH2*;) zCn%K4gA>(a;g=BBJU3o&L*l8gw40oKsr?9bQ_fC|MHs$M%b*>j_jmrz(M%O z>y|^1!VB=KDU)*}_M1y5Cht?tEs1%rr}{@$sRL$+d2YLrxT`6kGUaZ$mH8!3syVrx zMUbpnu5ACce2Wn-oxEgdSH09`p+XWh0kzhHKJUEFvF{5niH}(6;8#sEy3{V@Q}yz# z38F(^y*%A11;L@VHeO;97_g68HB3*C-^cf*X>LgO00X;G3@&?b^?q!1Q!1R?>7+;m zCs>(NhNZaY%LdJ~pV)IBwz3zy0TzC2ts(s-M{g-S=~DBs}S)hmRf$DK6%j z^uAL~4F4)w7fV1)UH^`B%;EZy&jd(p+7zx#aiRl=ymW0-Y%r-gz-wixGXg0MAQ^*C znu+q_JJi1UO%Eot#5)W|fx<1Xt#f6Q`Y$r@Y9F0i%xu@P?N4pILO$i23X^acb|$soZ2t_I zWWLRl{=tVJqsn;HQsA|2BTlhiwf^loA->K()}b=sRUrMeVoeK-hG= zTpO7GlC`&ON(xREv`;U#l&m_}GG4gd|JI#uXuRwheWkfi9TC?32lu0`OR7tiAo|jT zK2w#;$Xn(nVpp^mH5i#+$Dut7NThL(Fg@z8=7-jF&YP{@4Sn-a3sUVAY7JAWml*Sv z`1KFF&mX0Vr$@rIAZ$bFqr^3E1cF~aOgD?OKGon#bter~6{Ha(Br;wcv$SVN!?T18 zh9$`6>tYh(6D4`bLEhdW-m{j9SH0PEj&0Cg@q|CGhLb>S6JIdRsbYgMn`+c7@e1HYxV&!qRh)%q70CzlYF(D>aI${#bfeBCLgH=++VaDV@tKE=V?7*Zm(9=d0E>=i`1UMWp8vCPgt? zh_(cVP%W*Zd|EpB=7-SPASQaC8PN&jF8UJMKto#!+b%YE^GEcEIQ4_7(M}Wiue681 zMaM;$PG=Kh8p$~TZlbVX`A7iegy_BKtgxQOwiubu4l&}Vat@rV{wQHy$<5@-ltjMj zCthXs!RC^V$;``1aTbLssE2F(~^1>(vhGcgLTM)GPqa1Cs4hwY%IfaHe6u_;I9iwtbX ze)J|Epo*8>*r2W$h(p;~0e><#jRNggs~ zp31J4ou9F@_aVa3Tn}Vi)cw{is|-&2uyMWDlB$!l^IvvUAN5|HF<9J+l9HD9KWVOy z5HR2EFN;-0hBne;j29q463M-dvZ^`8XW21A7eIds%uVE~j@kB!y2A;SU&TCLP2c&S zlgPOnm?b7YBTH^Vl~m;{L_rk<9srvX0t{Tl>XkW;HwZX%&+eX7=7)T)!oWWg^P}MB zNvY;M&mMR)>oDqk4f;pX0_iMBi|xabS( z$m#@A1DCp7HBHA@+jzpK^WG#;rt)X=C4Q(b&nbAC2kbAq8+>Yg0Xaaqj5*Ckcplhj z!ul*;!w2A9v9LcDeN4C)(|pWEugk;{eKid;=ovhJ#HzVB_c#nXw$yBv_7)LNzorQC zKTjG7T9OHj5S4y)u1R`3v|?G2TG8O{D{c>0rkq+zJqJ)5BGyHhH!(T&8McJ*J#O9y z#muu>OGZN9O`UO@|LHRPJffdgu&H|O-Iq1?1kcy+o+Rf8-LA5&`e~v#q7EMdNkdFT zSF(%1=Jfy`k`A;1gJjvJ%DZSxvLb#uZ}FRmLjw|(MP@9|KYp0)nb`A`hb&SQ+fQ?) z3fJ@xC>%sFPVl~_tIiUB9;(UkU_X|%+H0(e7sQQ9qKv5iVJl8WKbNq?LofDw_FawV zJ4WT%i<9C^>9ReBBs?Kwv)WrWZ{^tg~1)DT> z7A){y=7CX{Y@$0;<><&#>}oy?0`XCD{i(thFy;N&kZc}H0NECc={qOR8G6#VglxKNByo&7>^PENX+0R2`rTJ zKQ?Iv37EQL8!~oQf|>6KSOsX&LBtoo(3(Cxd}EgBU$^$UOX{kw!`O2KZq)~~N4jPO zy74?(?$u7!^d3KeY?=Z}5OY0OErAZ;BXZA?1fA2(5qd1r)143~IR4L4eh|4hk!Hq| zx~1jWrO~fJE}KP0+KwEDyo}pT3Qudp^3FlBYacB)jJAXu#mRQ#&_-$Ua*r7+BI&if z%<5P0JyTAN7j?V-@WTo>n7jEO2^h&gWQ6sda?6BB;OdyG> z@P&e~8%Hu}$y0G17qzFnFu@!7@w8`)_bnBS|Ik_y659~D1qNH|BUclFsV7{7+g^4A z7wX!Gt3MpEPuUyDZ*PKcn+WRXK(z>?uwfmah)XehuRv#(6qm^}uO zzevd5oR`O8e3(%gF%*~@(N`6VSU2iF_*jn&cUm7;qCyJ-)8<#X&pUWl3cNuAr+LB7 z%a)5>H@f8E;1g~^($qPnrUceWH^)@$7|GeomRNW{q1rVIMe7l}l~+Nj?{Jsy9o?w! zlsHujtOVtCcHXjgPwBO%7Q2fFJthB43A%ESd&70fb7kn~>bT=dSnvT9po9}=3CIw; zCkSAud&|8O;;zBcC`d74S6)yv$*I|T+^0{g(tKsC?lnsqc{!S=(o4!nwwhy!Nz7Y? zgx_#lxX^y;h}v)0+ov&z1`W@CY}Owr@5frEA5hd+fTn8+%i?DlJrHhT!spzIq`GscQf(0n5`T2FEN zF#08x+`%P*Kl2pp@^%k51~E5{M@JZX8^JPc^3rYaj|jJgTEu(tKb$%>OTZON`&tCH zlax_S(qoaoh~ha_lv4JF_O2l>*W>Fl9u~V!|NJR8$i1xbcts$kK&+-0{^PFc^*wHz zP*PrAe%!^y<&PQFhdvLtrX*)o`@As0}HCpNEaJYsJlPk-hv=!wp z&)?&jt$Rd!`yNcNKVR^-;le$WX)~tjD!9UxK0mulr(ij?2fL=Uxp{5-xz**%Z}h7d zwF@8d{>iEbc~6glTRDNxP8rnn!4F`W z+y3SYPNdCPF-7Th0g7n1L{dktNGsOe`)F?rZ8Yt#yER@vhd)ho8WiSgrhX`^=~t*= zfdd7795fCmd8pX%Uh8q}eBUowcJ{pyzfDO05}HbHOrtwBY*nUfFKYN_EC&xtE`)z! zr1)c2ZLk>QJEi&TG`?a+)dX$hFd-Hvry6?~X z^ZtB(@89?P$NlJ!`@T!Zd7j7dI-av`030@3X+h}YJ_n<%BaajIMs`y$L$>lHXzRUC z2PeM@cpkHx+mWIbG)*wc;+Ip6p~XD=sTAdXx1=T4tx@R)EbS$zw2+TK_k`QvO~W-= zb*q5%d_leIq}rTK9pAD2)_aQ4L?o>B2`O5it|WnDA&I1cf}(3IFkwg>S1-s17hCF|?M_Z{>`pnt(lLIt$K9F_m2c>~ik6!CA+dRFKf|^YZ8tXQ;KHx-484@Lt!ylvdJjt)9@# za9J{?$hR?Q(Q`udMEOxodw!X4C@wGRaf(cuvLv_fp_1B^=w(>y&HA7vnb-; zcmnMgPKNgaeQKiYwbpoiFulMXkqU0G(GHi|O8xgOVeIQ^n7Xg-82}N8nAF!a7&sFpTnZ>H<{0Y8Rxj9+3?4H#( zBB_r}|s{@~Y`Fte~>*i5VaomxV{x>n?313#mzp zO)#B5a@kqSDqj0;`wZMX-Do7wd~SfH>M7_{6B@fS

  • R%mSa~J}iy-#@cY5Id+ZG zzeTfaA}&VykS{m;zG|t6&e*`Hl~z3k0iNs&qwkZe`Ce-FH3tTGvbt6&=jLoaF1l+@ zOF@JYlmMKnyHX6RA~NjpTS3E8>_Uyx%S$9d8+U68Ew?(2v`gBg6C{XPBbr8xHK#g{ zai9!dEkogq0o#yK`R98MH}k%4L_Za$LsppRzgBq6sR%SpcfaH5E!WyhO5!UG4-*2S z;cGBBPbpRKu+s|5!4Tck)6{hlDOGr5z@GZDxx&XQb7iK}dQbzj#D(ZocC~XDtuKWb zr^R9OVM?@Eojm3k_#t^L=@;}q=e!@lAD8vUJKnSRrJf=xeP>yEg4-+tG!_=}zwm9P zlP#1m%d4wb)#WALdr2?&e)8d{r%;A!^{=>n=%Pj986H7;^UeWaL7H9{J0x(Z6~CtaI(8|OSt56?pVCL~h8lc@LrbRKcDnoCw$t!!wr(3N zieyKC&FlLz?}HI0A+L4sO3yl?U06mat~+{BAvTmy*w&u@a*QAuyZBh_>XvEpLEGUC zr$saQ1^g_;^9_Mp=Zf@uGF7IMy$-r!o-?ZTG{^Cz&Bcb^t8na%p^->9>!A(MIRc-X z?|DpNf1i`L`J6fKV>wYiz9T;No@j6q{%h+8pUtylYS$$i&RW=b zT)dNL*0tE2q~+tHnXk{+F2Q0!Kv-=+Vy+p8o?fs4;|$w{+riMzSvp_A|6%Be7|V!rMHCTwk{Y#4cik)f;6b9)_}Ea%~0|)K`r&E?d0}%Dyc0 zvxBUR{Y8)@4)seJvwdZCBX{5SxnVH8m#->a7r0}sMX@~gPVyZ(#*iLYtNQ+cV2;l- zEwRGRAmLU(1f&}Tg(=yX(rda8G$exVFJ}e=_A`~ukNNsl-7)0W(~x1xYqW6(U6#Qm zaI@vmA-uOIB7;1T*6u{#1_EwHF&Ex+4QM z&3V~IaV1i40SL*)6oBevz0G5v+K-&DHI|#3Tfg$l143US&efSt&0d+`l#(y_oV9p? z+K==*)^dwl`U$dCPf~g$$K9}-l(Ed>oIQMIe6Pqn8Y#;)Tn~ZA0*$F0Ah!~*6a$$K zuhXJlkiQbQL3I>3aI%P`OG9^t;YGhca(xz*Qtvr5$F9GWM+b=DZc1Cn`ik3;$a2-r zpQbg1u=}46$Ki@Hh`q0ej?t@j*Dji=b^KCJqS_v7;F(q32?ab-hW-Pv(ktNVK;7G} zm6+H?I}Wb=9CZu!*tC~JN1rpV&}|qg#u!OsT819yo||`9D5{cpL6G4b8)@!Y`I&EO zqO)3cRWxizO!mwZg~olqrR@ulBn#&ynLwRCeYzHpuSv(_@Z%>>Ni22!>RfG9erG5N z?cGb$K$*&+Y7f1)8mzJlq|$FjvhhA{BrWqe+}HJ^=p@>#C{0#O5-uw*k#5u1^mW^T zerTPlhfKUC0pA~D>fM}|yOrK>-ndMu(;0p$+Fw4^Xg_1(ORwG9FNl$i%NJqwQ^pw5 zbb|L-GT`c;V-ockRA$|hkDq2fl_M9jn^gXE3nag4Ctt$$oYQ({+TLsH2a$iS7K_Rr z5a8zea3rWllgL~|C~#0X%fPN5osOq+GrzAZMFL;FwvK_AhWs82L-Yy8*a?BC>4gJX)LYR|1;TjMWpg z5UL&yw=r`gRTUwReJ%VKpoqtyXuNClw6=8q)(;s>t%U=$C?kf|M+sX(A01CnvZrUK z0!n#Ty%Yh0Oxj`;;e1DdMH36ZX0o&QmC9j`m1^wR&eifYYy_?%r^I<(jI8=Nb8pPW z)=Q0Y>futJZQs>iJP{mARP%wedE6wEo#aD*EWx**S*Xul%0?GCFfayPNOre!7iSW2 zBy3TBls;)u)@9`o`J1=_`yPZotHzwNmClC5u1luGJ7U8u7X5VfO-*)XNrjJm55pEl zN6FVbKOYDyb$)nA7|&D30{Z`O+lfPbw`t4eusleF!{J8RUp1lS?E? z4+r2&=p8Yhw&7fN2LHT7z*A-KV*cp~a&7P%yw0_h6&HW1kif49G^cKm1Nme5u=N5_ zj~y*FeV!&g3>#<^v+c&^l&3PF*ovwttw|4C>jKSPqbMc83o&DfEKg>K#94IQ!3#rW z>>Tpm@-f+{zZ#AJux2d}yf$}wVs^i=xLkv)1S^46=GWjB0oZX7+VCMT``9anl0o~8 zp^UmD()BLDUW!I=#?)_d=DSlaXs*w6RtLicf?uuowpttAjiLASH;h`qQIJ7EakqM_ zu~1=QK4%GiXGacO(PhUrvR-J<>7+;g0&VDT6zB%VO5dJ$kHS?mPDvb&`X^!0r&o5D z2qDGI?nb!O?ck4Y1eKdl>&?SkA~|Pf0TkL=`ZZR&(LmuAEh3ydC>_g19<}V*!vcP) z@UFClPGcH)#kSvTE?$5ymX0!F;avFrw5Tna?7oxZVM@eO7q*qpi~()am;v(b)M-95&O;E33~l z^s}zLLf*&a8Iy5+GosLP877KiDmg z-?CI}+S=L4mZOF|MVwhtDiJSny&uvg0X1+@!Q;dMbY>Mk6u8XP)RImfvg|1F`ycCG ziyiLWB_Rtf{syIGUtM1ZGAy-%X|HD)O3&5QEqTN=h#3TLCkpKFO?1*Y-Nqa8-=oL%t3<@i|<1CRCSY~j0LGKZHt z6iSgK1I7AM^nQc$)KG^k+I)nv@O#%3O_c|cAA{)Qlk_kSCj&d5>d6dI4|=hpqS%{d zFKKf8{id`Y*HcW4wuG-Nzcpx$9c{oadEViYT`7v#%PoGs`}e*~vDsYd63)AD=y8Ir z$XweBnzqz@KO}z5Po82BAvsIfX^V)UMWGm+ zgN#|U=k`75I$N3=%cAc#{!GZG<>iCo6jFX65RW-ay>K>}gREZcqvx)nB=Po_|Mh%j z2Y6z+SqQ|r#G?biiJz@BG4}eTi7%wGw!*a6d&yl~=6nFD&XBkyVo>?~>V~GJ(&#iu z5Fj}s?ahJ^BC3xzNo7uE68^QkysYBgLqU<*;r)Wz`Kj-!1K&;&$*EIOKn7+TOZ8GR zT+QR$2(*1_Y}uO~?HVu{td(j_FWCgv5EHl;m{$ylZs5lg>>l5GG0z9>JVgf07d!r9 zdjO{{FYs+a$@tCEO<~NM-K2v=LaqzH-&+CIi-hYGg#ZR`CO;B#=ECi`?C!lKufw|@ zZ)FF-Fur}WSPLs?&9RMEXA4}x;WECxHSoNYCAStneoyIm?uWVu^p3`FjbyRzxoFd6 zFO(^6@l87Wc7DCA!L90jwm_O|tf#iOBDqnr2mGp)Lvtw$LS>y!kY|Bx)FQm*Q4-Y$ zgIiA1X<@+zQf-8l5J(||vb`Y7D>nwYo)ZOfOwD9ubUyC(T zIeBxR?ZY;o83k_IF1C8m3R_l0ne(fwi?sqcb1J?X@?>i`x%IaneIRD_8vw;}oin5& zlS?KZ`*TA;UHc_M^CH0iR_Mn|NuSQgxXaAuw*h;q*emxtkXO{~FtgdjXtz5fz;noet$n%V32|i1!*Y0t~=O{&)JUhTz z8=?5y!*-qx7eD>A=P=e(w-rBquP-h-JQ=f}4tphqTRd#`S&%w;n$_*IQ`b|Ig}4Y|9XnQIIcDjQ4{>|x1O-N^1p zLUMFd@(m7cTy&(?!ER5@e z1^DSU{`lE)$W4-_No+^PGa&XB3Cpd6>t;^6RJaQ( z*J6{GTjsiC*=K7Czc(OVi)X;T-@~@Msu%BcM!vvg8N&k#?)M>AgW=e}IWuJ;_O1Ld z^$ByCet|kOn&ib=*6eP+% zqqkcFrDZ`_t`4}-MQHZSelZQf5?rV0u3hT1%`8Vw8gLQkyenG#ITCd_S`XLLr44U7l1jDf9b96@ldU{Cu zcu&Ly>FkM58@AsEORV#=Ka0AaTj@-#egzqASwGVu@#X40C%t@_@S0&?nuKk}oXIwn zsG8ek@n>51@8kw-!RPz3>W=RV7dMlU(~(JZn#h=#6@@vPQQQ5MaRi$L@OQz2;3sQ0 zxEb$%?Tpe{NHDna8iL?(jhij<+C>Y&g+D9wc-5HlvMH`DmffVv7h`o|68`!iBK&Eg z?d%s;hkVUa8$;O`Onf6wp~vm}6E8EUn311^E;J2$0RA`ytcI0S z8xYU-V55@QJ_-uDqQC@@&`4f&{!Nv5f%D2_U@>(Bs9rN|AD^^z$?XbT0GDSslrFWr zx>0g7h)NOse)2&J4`4n9!HThYP|n$xu#+DGc7jBBJKRgX9b#@z3ca3c*wcB>MAjBk z8Z2S*Qta;#Mownei#RLBCHhxCT0Zm_Sj`B5qB^>KMpos)`<{hz@#$RA?Ce-__}pMO zqG`0BFvk%rwJaadVEOCq-Led7swv*#1}DZgOS7j_;U zUpLG?(_aNO9m^9qYZJ0}t++sYhJfx|R_>tc*i90?^L}m!cEb zsaup}PsH^FZpfSSQDrx;F#Yp<_1I#BJux*J$D^9PfuRU5EVZKt~nG9I6pzWnrr%{i*Y>*#mf7nCE$n>aVm*DdX^WdpE0GRv!c@F@t^OUb{Rbn(vsTihv0*V{u;W zKyb^$4(^h@E_3tqwU;13hZze=0xaOaQlr#dvmDcsi@N+8MdRLjR1N?6!Uz z7A5h8%c;J-lRGWpUdxvaQE%CUK5o{+;*GddjMvq4eFr8&l52ilx4U&fTP0z4D5^p7 zfO6<_REzvZ+cOI?1j~>-!t^G}Y>TvfHa~*b-re)GExlclgYq*Q=y7&jqW*+mN!GNS z(mmQ1%U#sf{NrGMH~S|`DAA^*p&`Ul$+0(;!!KWN-vZEr9f?4pYa%*EOhWd%zTFa} z&cdey(9Xoqy)xNivvQ$u5?L5GuPbqZ zPT9?dXPh!(e9SBvRJ}Ofa62nqgVT4igHbrtBEZFIi7@}W)SoAqqrp%&kT}@ z0$MCMaYC`8$|}&^7dbvZpCc;slqfw@jTX@OyXv87(qV(cL@$HTU1{{Ao2jj)HJ@n9UGxb;MN9Z`pxa}~dWcG?s#1LmCl^Bb)qH!j2|BnqmuOCYz^-j{?k&m2X} zynSOtF30D3ed^j{ePTu_uS|m6Ro^T~71jzNMr)53XDO9!@}}(pbj>GVvM4ZVNsXDH z0g{me#$k+^E$a*Dm|1 zVUPby~-Y+f}Bl#+4&8+;poG{*cBcp}Yx{LqwhS9pBq)bi5KX!8){ykC)}POn@F7P3ZYcb))bIq z*>SY3<}oJp3LDdUUnOAuxFl<)@$|&`CXFk&6>?UdMNa&LxAPg*N2bK*U5|&uEHmhS zqDv1Wp=^my7Q66To^7ifl=6Jc%)i9f!@m$KVrua<)yQfYgx7Ablevlig~T2VjzHR1 zVb>Q}5n5;=4b(}3;xr+Tox)-O-4FZ1@Z-h2s-Fepd*99{z{e3$N=o@K&%U1=p4tUa z+bXNuF{YkvKpLTL4;V{S92v?n2gjnCf!k6)nt?)c8YqY_I4`|0M~9B!>z*B1GIYS1 z09G9_a*@!e68^2l7b|~qP2WF|EW8yz$V40bl&GOf*x=_^iF#tD6RAo}JN31~reY;d z$niL#VI-@}qJPe+MiV=EwRpUHRjTqGwqn`KoPK&pjU-LW#}?)CBXniE|M2dr*uk2- zY}dzUav<$^Pl9}zLrhllh& zBNa3zKh=yA=mTh{hnV;=&Wwqg`#X*H!{xoDmW63^-@dYBsnSk<=nP)%e(U7^ZrX_C zW)yUllqB<{+0qWKrW6o^PCM5wyvV|rBL)GCoIgLKQNw`LGI2hbmT-H2bIg?ZCb@k? z!*!|W1f9N0ElV}f0pulzcCb*l@U>qxRjJt8RN3i@d{b9cC6)rUHp=LOEJwrIiqKKzK& zO!!S9k5rZlJ(&&Xr@Ib~jH*koI}?S{s>SRk1A-ZEXt-|tVngDKVfNFKnFrrp1aR_f zIFEt%hOBa!axGrNPXy|=XXeL6gtLz!WM!&OstA`}Y0C8a+*j$H&87UsM>eBpDYmKx zAPcD1^Gg!pjCZT=fLOXCL)Zlz0q2dG_Jp^6bv&eW*JMc8GKoppnC5kY(6gwg^A9~3 zS>71y3Oem4Jp{#)`H)~TtJL$%69J*($LVBN1ql;(KYHUU*7`3km;aC=uHUUa5)_cI zjvNzXoYFx6yTyw!nJ$bQ@}WB?(HgB-iYem>sol%T?iX=i)zv6(DSxYXuNJ|flp@tt z3nwB{ESXQ;@%M$f{fq-cmi#(4KedUc{gD4QMT?F_xrc4Tvwx;Gn93)b7CJwKj+i$H zFLA_ZS=;Yzjk%Buu(-r=3y}3ayN10SVJph+nQ}b1sx0$PfxC-MZ)lhp{|SbTX*gD< z*>w9Z=gh9MSm>3Tkb51D;UFktS|W$hz@V*6DVa|9`0L~0qSk9>QeRHjObJi^HiV2g z4<;1lyioJE4}{wXyJIeI?|e&cx~X?LCfw;quZ5R(#cE>V++ngw4v&r}hp6~Z2i?o^ z{7F&sCCnBd0z`h>s97r_>hwtEE`YjH4wEs=stG}8M8muzwpvpq-aWQ-tH9>Ft;-t9t}W@TWPkv4O2O&0cs^#bHCjGbz;n+T z%?mSLAL^ag#{cFyL2g|)MNS&V5&mW&iKgY$2!hnpW)^mSpDq(2$yia% zt#-|?*?X7P-C7Cb{Y1aWTpbp32@OLad)yEZ3y7K8eYt`9r<*;F!037&b>mxw_|yH2kqZ8Fi z+I!5hQ6F&6)K=!GM>BEmoczx%=o$R}-W_p>4(1BM_i)+_VmIxwZkC4~1EVn<>@jgf zO{tx2Sh!2%4J7-gl;8gFpmwz_`a_iPuMEqfvF$Q@`pHSzAt8|bu)C$G2D+G1NY}1I z2r=Fx0@ap@regbuSniO1No#9-!}<{i2|ns74^s97@pN30Z>6j0FyXXe!r|W89S7r2 zK-4ww{{Fc645R0zM|bc+OEs4M{3DK2k$oPAce_bqJZ8sUqnXHoKGXB?Atw`1Y5CwV z-}lqPhk!(Z;-;CAERfTdF++8|urG%KRG2;Ge3?P#))_ZHRLp%ZxXo*{{r*x8@B&<9 zys|A!ULi2Zxf%FH@Hbt9Fb5p-n0kI@c7 z3p(DrURw-xFzJJv;_S8i;t&F^kQ!K~Wp}XW;?RC2l$~ojWfd>46S;_k22#^U7W$pi zfjr4}P3Kxe$;T)Zo$Rfh58zRgk>F5Gbb`zt&_l8RbiL0=yyJWtQW*1dZ&lL%;jy8{ zjuQd!0;uFw&&^?yJ!loHc%*&>p(RMJ$KZhw=OwSlRL(4}S1;f~mzN?3NO9DCo=k1jY*V1?xqSi@n?vIK{ zlPF@mAHViuzk4vZI2!vJY2m`m)u%k zR4+8wd!Jl}!#VB-z|MEIf0HTlE^m@W`M$Mk{>Y}=Tcun1$s@x(hnCl|yT|9Lk~3v| z96S%U9|lp0giEp5iWgfC7G~W_pegKR(|8v*rv(G{F#hL6p}QezJ}-(`@U)L_p4cg$ zHfot_i$zO4KV?3g1X9hj1tr;UrUKui;vh&Rg8COMDee`w&7*zrN4?&oD&89>a%r)ZXgYy>Q~yOt3%?4&0jH^ zS4`VWwjyBr=zgLqfSuY{${l$U%o)~bmLoEoVlx1&Z1S|P(JpqrzlI-GFcwT#ooa zoBIvu`@JWPl`w#}lrq3~%~E3ZtwVw(;xn8R0uS%(<~qa$07*VB_q%QGAhJZREaoc8 z>UA|Xzr&f9*P~*`v^cpYmw$rZhtSYW|Nu-p&LX=Ei|CVlje)2N7G?g=Oj3QCDf*6I}8{eL3=ZG8o z8a(Tix2bTbEa>nfPeG^9ptU%Tt7dW@-9V=llXs!7rFtS}e;SepkX|?dy+Gm=m_);4 z)C|E&W+w<70_diflD)ZNQeFn=3rTqy1hadbrsr2{TNX87?ynh7!lxi!l!Dmo87NaE zw>>=Y;Ct?{z7B;L?-sAxBo{!+4&U=WK`o-CDn{?kA921f085C^5x{s_^gRo_s(hdvk=dyDG!6GE#= z{ixhOeq>0zkVl=36L#NHrbmyE{vm|n4gztK5lVh9&=NzP-<_Y;-_h0gREW|=P;cJ; zIMlPDrIH)W648-~r(PpS7_7rWSsu1RO-e0GE3eIoUm$LI9p=tgT7sn3TLli~F) zIx@wBS9@KvbMFWymjG@12MwQp0aax5>2{{W?+2Ayu!M6%{p3$pAHcgrA&-X_-2J?a z=5BV>vNMO;d*(#rk;<*9R9nwNGeTOyFOEZSZy4;kpU`K|0Gm<^wE<1~9Y#1n@?+b4=ygjijE2Vai=di~J_^@6E zYocDigCRRPAPg_yPOe1=7d6QSjkKA3tv)$;N|KoM45&1KrT2>kK)?s@_=(J)7hn=i zfXrUObMiMi~Xak#uec|B)}ZqtL0D1h{=wg753OsL)h8rxFEE17}*@* zdD6wIU*5YX3g6V0gDQ`ud;%XYv%u(do@Ff8y`FOC(@&6)A3nn6?{xcHDiMVJFWhPV z#^K2&A0ucNh=_esPRMrMX+rd25<+axhSF4x ziZs*C9o(O@lm`@$;;&|x$K(PjI!J}rf}xpDZMZk zF`b(D!QKZ7#FedO<451iivV4D19z}LkH1#RFsiGu06`J;m_YcP<&7=-N&M*d=6UV%<#Z5q<+q>rBd4G}0s$1A1JOXP zMxLZjr1o;a5#aHSl>BKU#Jk|2mxRyEnntOAz#N8Uw{EQoeaaq;JzGz5OOG2{J z+eMWpXBcxYBSSGFYE^vC;N6pYl7qaT=Q%&mG^gDYdz2X_?kb#o%nye0fkyCuK{{E!VX|M-dHLT>x{qoMr&oMCPq@W+MSS1wZ81WRa*?9 zkW07Y&?1O=Npm}O7@a^R;)wCAl*kmqSeEqKCDN#lE&EHE5>?U!WBgKRi$0x}? zJ49>Y{T@3ve}9OQUG2+l#RjC3;pr=kxy@ z1|-?m;-!vMpb9kmL7@Q`Y;@O4)zL!$G}DoBaCd8Yg2Vd;L_Sm1+TJ(Ndj8^)>n`KI zep6R95KN^HYqbC?p8||@FcW=-Ek>{WiR{8W2-Z~EY?konj&s`E1eX#C*^c}D@AZdM ze?S?7a52LdMp$CDcGIl0GqjAXldih#_N}-nQObuO!wf32?@fLIp~S;a z4_Y*g3sGF6;_QV0vR%3C9af9c2(r0v zO{%)GF}Pv!`!^-o?RBqR7Ng-BFN5_n{>rxCK#O!YJg`pZXuu+9&|^TLy6kp0EMa{^ z&1-z8rmK3uUtseY>NT!dE_Bd0sn|KSPVco`W@pgYK6=5$JS^Jp)~@Ui!pQWY00+Cp6%k7+a8o(?94Q<` zQMhzS-*Gf0t0eP}^QMY)P7d3g+o{4M2zvi?b5bi`+#3(MZ&_ZW87@(Kr=`*kQzT1C zisGe_p$WZTIOu?Nu=aMS)5UoEh1oalpJDy$k6gl!+~_1!K*Q!!53Y}+4QLsKoBXN1 zyUG&7AV@Au9WOF8K-xI6_rCsa1BlN<+9n#VYS@=KpYgB{X^Ld^m9U%Pmq9qd;cqMg zRs(sRHzAWSym|c7x_`IvFTxhhhm{lx1#0wc#xn~M^iIjmZJM>cT*7B$s4^=J_e%E8 zsvo#O)P=-hXRxwlAXiY_CLNg~25DRUYSaQ6opi_(d}DcGt56T3RC|&W_$(eQhig&G zBeb>X{zI}gRc^h~_mAA1!-JGi{AORoqaB}%r*YTuBxuUQA^@}hfHh_|d2k4{J#&KO z?&D-+^_LXlQe5-MXnAig%s_awW>cV$^On^?Rt@mE1pA{K&vc&qPr{!a121@%MNq%E z*ZW|i8ZAQM^}D{1#2l5*=he=`HfV$0({O(}`Ht`Q;F`h1orV3$hLadcMoUujNhEV$P?86DD$37^RhB6pWRoajMw8rzrF-jkPI-?it5zx zaJz;QHUPZ*-Y@R{t->jP=LFk9V+cKef4sB6-@LFNxh{e(!WA0)5g@mP8_JC!be%s@ zXIoqs`4eAjucV}8FVZA~oEVA4ks>i}%<@efuwRJgPcP|OmIM`dv0g{16Y_|szs%8h z8~$crmtQ_-N+=)kJkq4^MV;Md?bYSglX=-cWXHVcK_aXE=m9as)}~6QG_yx~dN3F? ziS}=ZC`E@i3Ni%)U}`4hJ2I>_f-{y*kYf%N=c9vaR5_>-1KZ;Zo-^sib0L%AYE$q@tDghk|7fw!hYwP z8FrSXh?_jbA;=sv;-4063KVgVYo@D@+nHhe`}foy*L!$-GK2XnW#MAFU9hoG-gkg7 zdk!Ei^D^Ah!Ub)|D`U0rWb5hdap^$w4`$*hL#w&xQwmNlzGmsr8YMcjbo@Vl?6wba z1Wj)`gXsHN>}PbiyjCx6by|B*UNCcj*l3l{LJqBQS04uNRy($D_4fC#xbj9o?i_fOq`DP%J8}#{ALjDQVWa6# z>O<`@l)6f7_@mSmv#)C~O}Gd^H(1F&5cvVCO}1#yG? zOzA*B>rip&WwyU3FwIm6As~AL-%xglPO3FCSutzen4zF+-b5)b3|c6%{oA2hyl{Im zosXXXvGVylfZD2CFsUim*69iFIX>}RB+cviI+JfY?;X^g*<^HNYReH+Fn4%)DX5P* zmYPX_%AZ_P^l!%?%)fq*_q9*>6o-bN|LoiPl)R|D0S@1|QBaT2Z!;K?jGQ!f0b71i z*L@bpCkun>oc>XkAkqz?*W{FPQ>rpzt+yhc+>Ou4Di-jrwBD|^uXa**!!d$GcnBgjXGdXsePL_9;k zM(i)v##!+vU7P-7_;J2&lxo-OT{Gj%PS;a33u~3Ylm#!(&iU*Msjep`Y1IPiJx(rq zugwR}BW422)8h;OKDPe%v1t(#^UlCJ93>mPntGd0#+R^>9aUmQ3vt1|AY&sg|H0q! z3}v^LsUqH05+cq8V=N0b7tQe(LnR@#$t90@KqOC?rQ6SPl4@X)dAUbJ(Cot6+}o}C za@(XTlU9@PCv(9DwxJ&$&`wS(4q5V?0n=dptB+DG4f9GeK$xTMMxV*9l8*F>ZSJUx zom#%z`R>YcUR)5v^^L}n*xv&W%x*S{io{dHARO^QAY=7CpWJ0x#wgzQw@0qJmJeew zQXqT~hR}B~&Vca24ssN-AS{a_n>AcqQoKQORUEqoP%cJiqz4kI0#A^a<{|_b1+Q{T zolfIlnR$ZlTQ=(P1QZ0>j4yrJFF`(srV-(^My`%JBmT`b1a_sZ(;@BhF7(+*)*_-H z=^(;-Of-g=!bUdu?*7^?UZO5U?5H(JzauKcr^Tw)>qjbzM}H5E9@W z8Br2=N7jsY+RH5jZ4fQ1Z6zI6q@`M3xbL!I1R+K*rV_P$#B@7xaAoq*5b!7c*5R^J ze>t{3&^Xb5%vIuAt!847s1=i%fCS~)`30UcM|*|DGpuaThHUlq36w|15A1t0ueoba ziV`c_m6B@iZ{J@r{Qa{iS+8bai%{VkdsZu{N{fnp;aYOxE%#(CjS|?F$A$hOgt1Pw zI^Cp-gQP({3yXNp&I|@JLr9a2eu4i4CM7X zQoEaUqm22;?_q4Is_Az@;q`ecFBodbOhu8hQN zO&flk!+4eqeJRV9+?cejhSz4FZnL-KGG9#eC&(S zGrIx*dSS3psPw^U`oRudM-~!_B4BuR%e-Q9ZPRE?aWxYgpGt-;;5IvV!qKW z-}(T|VWnW#Nq>@zFdJg#ULncvAR;f4Eu>h9HyqX4)C9~7VUlSPE=DUHN_h{Ty0!1F2ZNm~6)1x@N7OgCT-fJu# z0|~w<>vC(ex8n^rt#<7{`}PTlxTf^KlK{&klf`)b#;wz<;!h+h7-T5Pc|7;JN<9zu zZqKFa3K%y~RgDu3&S_cK_4f-d4OxfSHBYt_+cTK1K3MDI)7Krs@1wRVM86(WCi(BHISEp9SpY6LGJ@aK0 zX<{Xpq8n%QW4_z6SS)^q!s?@f`wKiSW4n?fJs0ZF)^;v_pw2g~vtrKL?$?vm0Dr|h zVM#c;IOsPL`w`vmGS!jO9o`_EG|+%k5ctU>G9mZsdJz&JTD>)X@~al45;GRJvW^h_ z8~zCb?*M3^0{N();ipT}y>fEVyv%u738_5Zbv8DcpZxFL(I6IP=O2Bt?PkS{C_;RB#8XHM{49T&H8eyGTiOg>_Lu z{C#b+#Jm!F4m^si ze=a5ex1Xn_&L7TV0j+agku2U4ka7aWT(fA4aH!lnC%48gNH0M5?5x|+gHM3t2b9=M zbQsfKGtHUYHBLlv@X6Tii{9S3GlpxXgq{SOhFM`S_{-bVqqkQX}kY5`H_xCnMqu1^R`=3o5?mzTC#_iyOr)Hx)qs zZ#^*_z1#h0fNovhjrd}w39r?Ge%WCF*;Cocr`gwo`jf-$IN#c7e%$+o4A4gCl)**5Ra_gyqGTrXW zvm&b5c2YhgX{0gZA(z^Gikc>il56;V!+)b$t|;>_Rj~Bl_#82rK`8q(CAr~+<%8BY zRn$Fp#gEQ|cNZ$^{)cz>Z_e{y=MqWXO$C^2p z0DSAA2J1FSO59XPpyQH)49 zSqO+M5qAV)F)7esMkAX$g?LS%?xe;SK~}@sO~|u&e?R}6^kP>@T~twD3q<&fsm1l6yI*!+Ev z;0ivd9W+)gm{XIbUi+}ByL_y>bcRC6b1`OXo2XeAobu$74F7z~=R<$Hioqm#x+Ru7 z8Ig)WT547Zg z;u~nY0H{Fnx{v(yX-gfz+o7Q!7|SX}2)So}p~In-rx{8%id2lk5V@x4 z&g=y?l~g}OyH1b($4dRbw@sSoI5=h{gUL*pMO@h67>gZzH6uSxfgg44saXCu7mb8l zqE9v$D%jgyIV1Io8f6fpuU-yqf!V%o8SIkgZt#YHx$}rYD%HUtxMvi%fj(#*IFVvP zz)dXVC2z;dO?SM?xfu*jF!=<2%kzZ=^$^YuSq`2N&dcL100kCyIXwiv@aJ_Tf24)^ z$1QLHG~BY$qC{QLDA82!Rb$E&Xof)E=E9B*$nye;Mwu5A9O^B<&2oB%CaH~2OX`)GNiU@_n;ZQx1h zn|vm@C)Gex}&&_P2->3gs5k)tx=+Rj@grZb`NMMbLgZ@5$-%&d&hsA^ZJ_ zo>wg3!HWg8R!*@1yb3Z}f4*_eAav%AH{>-S_41QI^xRRufCPr^Z46nzaj>6iMH7)S ze2IB32AHgxk5uZ-Kodw}8Jw+H9rZe7dxTeTB0JWP2tr9zRBs*ZZBPBu-eqR_y}20+ zs+tzyj59m9LHk6Ne};n3Tn#aXfM0NEy2$7K%KqkPj*DP2*gIrESv55gCZ=Hwot;@& zZ=3zULh%dyw>Mt30JhMBd)^2V!F1^`rWJ_v&ss-J9}apuj-FuXb_j;b^uE6L=}@s~ zt`DgOQ;1~YGRf(G6QVG$Q7dUbNSU-OzH{P07Kf_5o4H`s^6h>WinGf=XMJDEjZ#Ji z84CZru8&>z8x0nPoiD0nkY+mq{KT7xO8;;I+5x4+$aV&A&kir28%b z1R~#xEMj%2&WHy;P5&Vacm#zWhuUc*Nd%#)LIBo|N0F<|Bghm+zf!A8J%#>yQ4 zV0!F`QGEcF-9i+WNG=Ja=cxczk(TYDSK+95Q7P#$)|(}gLCjMx*LMn1Xq8pZY#`}H zwBWLTNfdY-k0NGH$YmuRE1Y;H9097PTB9T3HO*na3hsp{r1%#Sh;t{8k zZXFi9(amtf&Z|djaN08c+bSo&WSo(EY3GY}noxJVMclaFqT%#9ol8KbJanwH>gvDN zmtl2pioVRXO_NY*hzo9su)c)I>EJtZ_p$mo$?_fjstrw56uS;w#j$@eV-pYeEWp*xl9LPTl)P3Mr7&Kzo9|H z9{H|lMy^p#Dq}RAb1+9-5@8vR4nlH@>u}+Y0U6NccRm!k zv!wxfITw3}IwL--OUr#q-A{)&or3)_G;-NUxCMJ^CG&qVdN=*xt^*}@>ph!iFDk%D zI}{UCPICo8N=#E>M_d8$z3mVr=`D#uAE0ExP8_z!3`oe3%ae)adWEDD!0~{BKKm~Z zv{RSn2XHByO}#nt0Fir5`0LY355$4`u9PyRG2BuIRpo&i z+_6PsyTAvPS?q6*MEL$1z)5l16RMFSdi3(@`Mz%lq$He=lzZ{ppyJ~)5Hwvef`rjG z-*F_*f&*YGr96NS@#Kt|_DoV0@qX9Px9KcH79hJ(nSv%>j#^D`D4r)KN z3=&h&LbSmiBYx-GdqZCe_Ad{JhHgKLD7Br)7E3cHBp{ZdpYFx1iGjlUD@wwBy;sxx zhFOqL@eRl!Rh&wU9;7;r;u{}#0`w*D zo2tR^#CPeMvf=UF+cy}9S^MB)lC-=EwEU9#Hu@TlKs9s(OSBOD>x>FgV7{3F%$B(r zr1o^(w)9r;6GB2Y{K=Tj(@>~?mu(d@mtznX?MZZ#Y;{aR)ZTri7xzcRo((QU2rFvJ zx7uCE=uW;mRocn;Kh8XQ&^KiB3%2pUQd^Ng4m^t@mG0C{UhSGT>AgKmF>RUQ*!@d+ z@-v1Os3qnJly;VcYUhZUN zTOG+_+&LP#ye-7U1Sde%I|Rgfu=~#3+w4{nztg|Y&ft?EJ#;IOhG<3pAL(BDj2h6nwf5A`ai*GD6Lhzn^QN9`(J=hN1d35ZYf z$JExXszxU-l2Q*cV|d}N+%%e_Jn)Syow$+w@kQ1sSn3jZ7$WJ`nTm8llT2vi_=+am z^u?evlk22TYGB=C{^R|C(@-C=`*QQS&BLY)G{NY!K37Qcf`r5mg4D7Lq1OyeGWxy> zCgXmi{$o}*i4UH+XMT~-zZm%AheV#h(TQ?lZ9}3!D+>Oy=ewLprpU<1tX1nYqezUP z6l8}nL2DL~J$a)%P z1+iFmU>Ws|8hOTLHMupjB6SC|)H!W`4ESjW%-z&U&SGHiazoVuM-#thnyi!zV7uKx9QcccL|jEe9@MuEFF zZ86}}pezSP(|t$D0Ka>JedG_VLn`^YX%>^O1N*uUrTp38xyu+<&%_^%|Jqcx>=_G& z+Tg;~V^wsOt;F!Gp|O_uwUPRP24VAhSME2a_qg~)HfIyCI*LlOv~PMd5zdy0fuTKr zYbpKWnJMLH6fiDxgfxR?Jz1RJ5y#4a_fu|PUvk|p@PH36C>(uTXa=IVgC(|_;2aLi z8!pf)TKmFo^#cDDzk-4SjN%dMni|%um;ke%kWg~^1$nrZ%sO~c>4d#YgBIJGy64TS zS5evlIBp>SUzoO63}<0X-L@NJ)ez_nvJF*M8K2xRrWzMORC5Sc0aM1*3pXbZBb(LK zQ=x=pk<+jp%z$PFKz4SMWE|D#U09DmH0qirdoD8E-+vO;rxt+3?=8p^hppXJMoM}e zwL;SV$JF`!`9cnw@63T%ve{WD(QD=%Ncklt+8jwjJRK%q7{HGEe){2~3Qos$EH2)L zLq~j4FixS4OgS%nZnV#~<@k$QA*hL4lNN2S9}k0PxMA$z$Z-__jIeT@J{lR*e2n6r z@%m49>kr>mpk8FFRO7e%Zoi+0FB9CRkpEZ0LSnqqx!W`yFfT_2`)#ct_4u}fIfiEJ zfg{o$Kdg2}rNaGh&Eix$H&H+0z!jIP_vCEbnV7>-ERvs=0z=@+tLLeK-&S@I#?O_X zR?-1ZifIiq*mONsMCyRj0z~Jo#O(;8D6Wxf**_=y{<*}R!15L8{UO-x+qb{2^7a&w zxzu`xfZv%9k!M%zZIMje`h^6o>$^OiNaz+o zv92^Ls(0uzZ$x}mmlC?hnJgS0jY=HDdR3gsto3CT7pfgJs|!{4<4MtT80S5Io3qV?T9^ zbTpH^Cgm|fOX^?;6W5FAD{#q1tG&D?6>n|3?6PTM-cv9tKD3zZtGezhL~^ zF~pYtel~VCFH^Ny_#~Io+9?eXQL5SOGq4(^sI^F9STb_^1JfxnjESGPCZPV$SsC2ZP{{P zOKH@)V{HfEEf^j2qh6g+2*I99k`_@RqQ%L=a(5KG%_7kp4d-|FK6nOXyPmJmstOoY z<%abI5I7>WJ4BTjRKa&TE$BMT2CXPGO+5ba!@cCkU@{<&~>omnTgvf^}N(0BN-Z+E*ux*GpyN|IzEj~~0 zNgwE&r4Cf137&ZuGhE)KWjT6GFoZv!>SxOJMDzKDMr7nPP41))*~Z1{s_fjKiGaF2 z@ylpq6L_nwyfgDbuIU2MCisr)gX5?G>Bjrrdz!J|?w3AGmjz8jK{|V`w$lESb=1NCa}Wbf+pEo zpcXPVWNhQi3eDk+wU(}>QJTIY3v8BdALunqtObe1iSo`pk|tbvOPuG*?vHwOt~kgq zQ$pQIPfMcIGbZOaV6!=^%@EV;ArH|KwqS;=irq|B^MT7&+ns^r4UDGZ#+q3uBQ5aC z?mO19F5ya-Vt!mPT+#C{f2? z{t7FXF}yOYh%y`z&5jeh%oC1ULmMsC!|_2> zyMZ$EN4K*3C1p}SLYI+wWo>h>>aF96d>gmlv9F%he{(5vaqLIkGhwSf*X$0D8=Jz_ z-u;5a5ACA88wx^XuA|~lWNUF^=rY*`*NDYsjywn7LTvbsQVVXTt5{ z&DeWfCPvA|x#yLiUC3I&KYekyNgI$0KvNT?Eth{>`}5Gn6v{e%k}`?jV|PTwr|p9t zPw`#ghJA}V51{T*W$kjWk*HREM`i14iGql1&LYyF{4mAF{LSFp>|{wb(xFf|(rn$8 z9Ca8YBO~l}V{zT~kGwuL_J(;Ra1mLhy$++9Y)bAqs67Kqx$eMJm^}utQYDOn_3{dS z3Vs&AM5bqBYeCs+uP$f_JYvany$XiFPn*HnJ~KTOn#cq~GN=gHvLs9g@%8F;nBrBn^c#j2G_UajRi-S$rZ;>+7rb-2&M#4HHbMDEI#r_jn(zdYl!P zQ`LfXDX+oj&$?li_gFnX16`gir}~k&8j8YyLSGdM|vAaisRzY(fKP>ubS}j ztYAyJD}HZfeABJGhhv>|6Egc%V5V2#+M4MTLGHHGMokXVy{golzjr6f3b}sK);jY- zmyeQyA`(miaDWqaufV6#aw6p-)}+7up{lY^P8z+W>S+S(i* z?3sJVqV{lWWbHi>dFXq$J|E&#povfmQ{w}0@U@b2jfb7bTcZFiRfbu!%L5WmI(v5P zRfQ(wU}vY^pm8;=jDx zM7dN^-4%m6nu>0vvHJbep8%;J9^9;(NH?z;^<40aI{ynpECmJGQ|*9BHpr9gjw2!N zBzmC$j>1~>G&X95sh|})5u)xNq{(2x%(`sedpfF73-b_;L8b1^@c5N7;= z_JYF~u?YYNp@Fw4zI%X}`w$R}tv9Ub%)<=xNGB?-Iyt(9$s52aXyvEl0Em<_xMIL} zL{HO0j+C+0f63Hfx>n~=CG<0Q78a#HRscgZv)$lBqR%cw&BxBkzV)XykAUwzn{Y(? zm0>rtY0{!+w?3>>f3@(b)eoOK%|-o`vaU2INS?;Wv2y5a+~Hi-y+T`BUwF%vfA*Lj zFU(nywOBG78mnq^9}MSsPvSM2o}sPx7Iq%ug?x<5ZNhN_b~~{qoj&-ymBYyPC%1o$ z^OaFm=cOpW=TC}O8|gobUrO7PbwX}<&lg}Bw#S8pE;g07@HL$VwCL#nVCP^384pe} zIk)zOF9A#z+vqr4OPN66_z0abLB8>k$xA*SzdLA($C4rh6%?|i!e;eUi1;b`QYcbV zEt_a+NKCC9`AXs7$m@J~jt$%RmKbJtvjHSWEm#^%+!k0W1o)gr>dyR~9oKgix60K; z$MNeCcm%RMhzd{H510zL27yg5Hilj3>L;~OQOO0_{yQLr&F10H8u#BJ2#wKsVamR} za%GRm)9G10z!$l{nW^2+#jz?YW;Xv*FX?o!niknaSg&05jj|pDKRL5Ff+=Y?j! zKyTG6_^*)VaMh=8Q)&bcnDaG?J3-nC;ea9aJz`mgFM7l>dyb1U@x>jBttT@`lha6< zr@H%iGPuIc;>!b-?9sJ9kGwLP34sqZ9!W*#(aLIg?ew$W$|=UuzFGZESGNYi=w>6 zcLlr=;#}-C$+MJw#FaJ)O!i#qQ;_R%sk@InFGc{^sbTnzzqfInAMP!!lA|qex=zQ2 zMV4WJsNlQF_IMCR-y7XwGxXxwW7P-FU~bN3Kj(rkS-7I2YaKb7C5}UJ1?0=g?=$}F zo8L!pTH2`;%p|~et_f2OZ5tRt-`0C{@oZxCTn**Hi2~`!@|+0VSgGQ>gT4bZv%Qhs z2%m)|9z6ZOx@L%Tlh*F{$z+ zv&h9ipA*M(L`1|E4JoRx-QO}bo%g(tR-jss6P7<5D8D)qMWWd8aD^i@VKEUTD-Y8R zpO9`QGibP^7k_!IkKDxtjhJ(mVU>I!k>Zz0>x}*n0t&#dlM#3KCxM`+2h zLnzX_D=}E?ZW%E_47DrMU zQRV$DTeQW~?Z;BamABKJzSPn6|?dwj9!O1kIjooa+bU?xJ+H7hdMRek* zMe`oi8K$3opbP7hELi6POjXY{1PVI&e{4lVCY(mTn$53tf0Zm-XzmgcSj2j=5uU@vXyzl`v(vjBgG<{^4h)rj-Us6$8g&E^3Khj$H?FFManS zu0kysvP}~rzaxxctLh9hO78GT3XVH6|4b0CId((2SvYX))jF^FngE;o_qTIwvr@jx zimv;s_@or=Bl)@&acJ4l|dC$ov=J+z^O8164~NFYGWn6how% zV|fpO>LP}ioLX7-`OR+py!YQa&bgYGv+sCmR3GH;OgFBa!RMO$VvWqRrIaA&BAakW zw)IG9l%sQ#LbS?`(s$g;dUh8APUkCbsAPVy+$_JRK5y5knBlrS`wgR0I96QYGg4qc zib?$QxsnWhMw8Uk`0iOcY&`H4Ka5oj?1I4_Q4W!-&Lx69qC0hg;c83B+!Kr1FAwlJ zpoCLPPl~aUIE9>Pyz(Et7ltS7+aH#cZfqW*Eb{u4f8`(z5_9d0S+HCAhT-nB!fzFu zPX)U5<^&N)IMBDtHGl2&c+XGgoA}cKr=lAp&)i<^$-yUIKL>y|*SR+(__TG6CT-^% z>4YP=5MiC1ut<%zOkDqVUh|i%Y}2=)sra9AG;>L)`&^DQ+ws+3-98_?62>A4#~@nfxNR+S{>{&E zJ2d6Gth}M3mvNz?TqCu~9F3{4^gIW>C^w_c>3Ei1afBY>tV=|9iRsU zjD#~$9s_#`H`h)?=y$etRR~z*ls!bH6vnpVo1bRbh@i{(h6u z;k}W~H|QXb%7BW{JDb~aJ!|XH{HuZ_{1b=B07`QM=HKQ``A`0l+j0A!KP?tJd4;Xp z1+oX+t75DrPC0cm@n<(vY0`A4@86N>)WPQsax@LN#j1;5E>xc_d7nEP0eCFV zzo$_&&Fl50oA{Ho=ez0Vy*u1Ggj>~}1AA(o;@B^jb6X`a2b6~Oi1KpT3|z-&_ncO_ zXpstYzC1pbLxAcKD|re%b6hi%l*n$(e}RRTWay-Y7&y5 zVB+_&$M&~t%+}|5o*NkV{`9gz(0m}@8J$?0d`30*vaWAc%5ip*+<*9|(tZQN-#-jg zw-iA%p>)zuEGF^9ulYW0)Z%>GV*s63uJIVbTDGav>%kd-q)n&eaoC6 zeo}ZtTCb=pkymF*Ao9yRgK9Wg3os2fxUNNO;h%ig?S6Xj;z%t1@sKY8vx6iy*JUEd z<&j1X)Y*{l&;$O@kQvQ7Dd#_-@MIDuQb)K0~hjC{swC;JIu_{M9%fHHr7i`bia~&vU^HXD;n`3I&aXS zDu7>I6v9c!@jl?wy2*Qh%lrBPeCB)Oyve3*D!XGeEy!_wvw@I(asbb5bvBLwy?=*< zxJ_6bP;!-F8J(`3rlJA4IP;&I6U~k{-`=2ZS_q8SEO*@M&9|5W6%l@ZvUf|rB*$0@ zV?TL+qX@ZJR)Cjaw_KI>&S=}=n^@__$q+dBa516yR7nL=BkajNE_dT}fBvSixV zUFE+YTv{7=Ddg}B;zdrhH^X^vK3Nnf?D5r(@rXsv&!Zld#caj6wWM1{=_#6GgfL`R zd2i7$leJ=z<*^*unf`-lR{z{W)12zkU;za{9QYu53ps~U{w(n`hKTgH`>`u-=ti_= zY-AUdj3mkgEsIKqf7SAH*MDWHYK14VtD?F<@{nD{i;!G$xn?U3YpVo2Pg@^U!YND%PO0|nJ~qY zd;@PE*e}JIh8+Ti3P3wiuEZCcQ{EikG&^j(+4kmgSlu<_sN_98Rl-yw+;Vj(Pwchb zBWN+g|KDFXJLCXSJC_H($-t6QdpXa|m%H2RUaNt(rrm%q&vt+N5*?F6->3E5mzFXJ zd$cQG>LZ#QK~tlkU;bV3V#Owmae)2%kPBsnr_f=BeK%%`-&b?YimI*l!yK^i*V+f4 z8ys7Itq&q;BF?M|D2G*#^DJ}1J+G0s#-dZ*ufz&0DEkjL&Kz-H`C0Ns#?j#3&u5yL zz0-ns3AZER)@>TzbqH910G;`mDz#wR!=eMPR_~uAh{slJjXocDd<=C}J}-JP!co|5MYMeJd-**?^`dR~ z(cht_E5LJ@fA3!7ef2M2Tb#nWk8Q~R#|5V)vK#Q^0MTi*{QM!@tVST0Qd=j|)|PvJ zD{LKbGmtnZh7~ld!h-{`ioXXPmrlq1`@lDgAF`>PX!f?{Mc14%Y&&y;azfpyjQL>> zbVEPQBjA32L(8`!%WRYZ<`*AArN(J>?Ur~>7?fO2{`2OdE^3{ z#@e))3;&<95$rp_TK0y?zW&W{gkiANMW#jBeRieRQID^@QJJBk=!!!?=cvKfEDI3S zA5QyQT+hGV&v5rSi|XUgj!@ftKu!Iq_y6%#bD{&00?|uUw-6pk`j0C9$rRyNCFlp9 zj|WErXAP9(P=7e?c4XA!P;zg})fTNACUGuIclKnj)rKx4lVB?w?D0mk^BNn5T--v93m`_Ix0qkr4=t<$8sL9(Bh|fH}I2&ZbA@09@{k3x+H)F{X=o{DG4YJ$_X_WL? z!2u8d&DPIAl&OugZ&w!s;q#oN{wkORPDE=q@MOoe4qP>OdO96!`9M|vSGMgFj4FlW zp_R6Us1V))tIaPKU0sNLd+W92(^0u0UcUONL=?R)^4eeSSU`ynP=7kHdjGV(qt0qL zddcSN25U>x7Be=!cP!HbRLX;lh#OP;ygU!lToX6KzVDgm6 zkeb=wMm97Qj@u9nwDn7BJ%L3He=eim;uP2y)*syK63!7eTN|VnW(wKE2Y@37@?yJmzt*NHdeL)W3;%UfKNM01yL< zY1qQ*n1lEq!bhy&Jq~X}Ps!FB30?%51pN8etw^_+KfTuUh)NIm{3WX%Xlw5oC z3qT@qIgKk7K5a1Z)&<(S$Z(b5EubU~v{?g;fL0l}ez~Lmz?jG`_~5K&6PX*Q z5$}b-6Oxt`GC$P|=I%|SNN&F=i~hg7A*)4=(knVxn)~2BTXd<7u>QWlNXC8rXSpcv zzRFZ3tBP-umOa;8*-Wi6 zWhnOlJn-y^s5RS{$H3^pJ(Z1(2fmOt#bKZj;eRgAse*tf_J{wiL&(0V0W884QK%o< zQn$HqUBB|7$^gK-hwVTjorOHf`P@zK9Zr?ibHgD~3@T9||HY~AOTY!^> z2_}DhsZGOF0T8IzJ^mLUgl6F`lioAeyn&uWkgysHvhPOZxec7^^Rwg%dvXJ1?{&z9 z?z=6gDdzCePt>{zR$s~EC~-KIgRFMv?c)%xLP^i&{Uho!TP*Lz3(gK+R{epl$0Fq% z_{0QS{VBGajKLDZI{zqgMWm(4d7(-T_7kCjb#K#h2d@ z+&_5Dg2ylYGRt`8;m}AE0EqpenJxQ{C;2}jH(Vfs-(Xj{t6I7!k>euHme|V zIqe{uc$nm)@!~lwO-j_&>6MQ30FbLy`QjeX;F{sq9RtQ0Poxs8u+vF)E7L_DzS@B! z?Uz3l0VEsiw75^RBtc{#4F`3vwWMyb6^2uh3I*4Fd0!3!s=FLOc~o;oxsc{$tuGx% z${w+4?%XsJ6blLm9o1WUj@Ftgb#;;{O^~uS?N9Nyxg+LiinTM|@9XPp8?&gC5bmQBia-S6E@p@iG^q>0iKlI8dfz3o2Us6| zdkLuDF@8kqWK%=;WLX0~&OyAMD(v!$Mfl`SVJgCD)mnLbKK7YCgX0g+Ab7=hFLzuS z`F|YVG=(JcD9%2XFZoGLr~jzm4lY_Mki@2jNsw{W!frG}cM@`b*l?vCU{Qo9CpWSwlSUMFEj&aKi4hYW(P?2dmN^ zZnb1T34#5=SPzlC%7=RB)gXByS$Eag9lWqh)T5+47Y=VHdHw1ZJuja*z2BHNxpOX- z1`I)w*8EQ0OtRC?K57&3{5%o20XWzn#sM+SfLVmdk)%K{{4;RI`mBzCISr_jgTw%q z`{QmIuH3qF^dTDu`C#4D4j^!XccMm~E~3wbG5PzYJh9O!cglE)WAm8W8%%O10rJ}s zFP!NOd}`otMsoYPKigNFf(W?Q^Qljw3(4~I9KvDMR;1@$$I^O=ne7W*x9t7x0qqbg zlGB5DZDGJ?Q!JrhTWYI$4_vBjm(D&VBL;r|BV;PIyJ4-WWtrC{C2ipl-d+WfI0)i-(Wi0(B1w>&1-4d9J~FQ2|I}++YhRWbyrD4dFFq zE#^LNyEs?rm3O8XRW<)QM)tTLpbpN97BrG(E+mOH(#XJN^pUHoPj^Uep16t(CJ+3XD%Q@^siA{DfsAbq0lI^Ct*= z>pKgpNmNBpPTzqzS*u5fB$Cfi#Z4i}E?a4>< zP5voGWKZZ)YzD;AKiYLyl6L9B0S{%%H02b$NtjYSO=!H zP-cBHVWnWAJ^N#fR;;ztA{1y)s?a*cUfIUIT|0DNJ}ubK*Cp-=5S7xVUMA%?RE%KF zBcTAV>!XToA_WJBgP*!g5o}Xk+JS#ltll6yD0`ho{|RuYaFD}RS}R^?zVv;_+832g z@Jz`f9Dvy7^Jb7rd!e^`P+abC&9XdoVegJZxv=?7hQmGELfJ}&E^DtDGD$%X9rfQD z%&oHatC0sG2D>1##GHL+nFK_hB7YYkdS%v9YyR~6WX!LF#7u}^Tk3y{BfM`6?7Dx> zwg_6E)kbj{@i}8Ce(&T>>dR^0$b+b^9EHk%mJ*XS9uN7VE%d~vc>{>Mzi z1p>jm=w#l`&It%CK{VEEwUhv+9PPr?dvtrjZ@gwE#i{wCkvA4P#K+6-vfq#Tn+2E$ zEGUfg$3QB}e;k|0xLj21=%rRQCpqX)i`7>VSze1+uI?6`cRHH&vUOBfIN?bu^4;(e zxnz(>@@e=~u~p|NaC6{{0tHFj#@aXE%})je^l&ywo*o(1zU2BZ`%h~7olBj6i}+~~FJNS3pviaqwkVndsNx!WN$2uMxPr6C#Khm)5JsU zpr62rZALDFwPRS4q`L;lf->t5@C1>Vhob7=dS-0+fdkJ_FU7~8Xb-le8!_NO_r#gZIOSNmGxQkC)c888D_N-8$}}Or;A&b)ccq z5{ZoL$gxLU_!5sAQT4nGSm)!qK6U5BBMTI7nh>HExJ-HZQBvUWdgTz9z2VrG;pJT} z9eUe9M_Hxec_aQu|6O_*eKC z@FuOPQfYZ2fBtxL&NZi5`+Uv?e(Vk`WMMbS$ug)`ZLE8`tf`Ftz7JytQ@SiNVc^(e z_aZB8=3R`z%vtjOF7S+`*7Im)C0F%=&maiXwEUC(CRxDON!?i;I|`*j1Iz%sMak!c z1cB@NC>}1&yd-3xGO>U8J0@s0(GXL)y#a8NAiBAoI$r?Mt$yZU~ zURqtXu@O`J@CusQbg+a%$&QdLW8QX8tLYG})EBMu+dX*if2&$OdxLsE5AvOEn1fN2 zU@QAwPGoN}G12%0MGct~Mc-q@{6Hk#+`z;7T=YQVz(h-_~7C5)Niok#?s}N=B zeRBcc>Otr^p5gW6iERxa2+&4?T61|HIIN$(cT5Gz(b}A-4H{9F6G}fs$4n`CC&@&p ztB_JSi+b--F7`Ot=gMGVVEynhcx+>vKK8-nqm1&1t+6A@_krQ8DPAM7;tqMS?=thk z+rg8641|TWY;0_{wHW0CWtmyyMBgQb#MG}VGTf9W?l$WndkE<~1R`oN&xKr+_6qSn z`{s3{8#jTAzZD3*@b~dPkG)6RUq-54fc5SHuB)44Pkn5jL#rd#(I66nz5+>4!-7Bq z(>u6V389~~RK5%moOe#&)A6nvav ze@Fts;4|`jojzZu9`SCp-TcP@RyVP7l=0aY{1wTo=fHdsB3GVmMXu~om83J^13?sJ z9R_pYU#}bcQer85>D{Sr40s#?p0d?&LgMD>gQxGXgF|5yn$)0}7OTs@fb{9#wW;V; zqIT~O)AL@40iGE6wnvowUtR?O9;kIUh0ni+C5joannQp7E5P;dZ{lyI8vT#<|9INb z2xbXi=5Npuq&*G>;o(27@81So*sZ~0>G$}-)_w-w)Ng<&gi1L=`1$WIDy7WV57mO; zD;Em`OCv(LI=^F~bug^*$_|jB8|L}_8=)ICZ3<-&tc7Cob+9tWX`+2U!c~@*moFc! zhzDEE&>G`N7>hQ)uFmgcaGE0>GNZIt89L6Hv7>AMg(q!l2#_`k)lQ`CqulFpPt=wN zO`xeN3R<(lRA6P4-2Oy0UVva7!t3_qQxsS(cbJIaL1#_yUWX%G7|wg!n8?NdIE^3> zqQZSnF@zJ0k~Huy4<&hVpfMhKRr0_jU(R$6R1x>3y><0kRw*E~9U&L}S2e@na2LWH zy>uEW&{=7$oY}CCZ3I54$;K*OjUuTWe&x7_UE#*E!V!{*APvAewHLaW_5M- z#Fg!V%Xe-TY@2KC?QYoK_szJx%6n>T+&EuaX@j*P#h%%*tG$7-P(j8{D!hGn%Ql{B z;{+6q0#j!8i2M(;=NG*qro|)#=A3D4t0h*vyc5Li=4_3Y_gLqyXM2?;5mvf1q9cyWU%3H#y$Zq?|`3n{h87B}Yi-5Q(Y!pqLAP&fC!S8=Qzrc`t|`!eNBK_6dH;SXzWM9wO9K z!2@6viPh)eiO34t^^-AHlihz5R0-Zm0tOQ2Rar?^k!5MA7UxrK48(mQZ}yd=r3L_E zz41^6_fbvK-OlnrjL-V`{t?Rr@*xI`*o2jJxQg+>uCzWH)(ze%`~)IOg%8l9h+~1q zFYG*i`c2hMD#p}w*4u0nSCzv)!_(ft70g~HfCxEYznT3lO(7&T|3PIl7Vb(d=SPAD zxhhH0l^~z`P+SS1hK9h5>A#Mb)LMK7g9Yy)hz67@+XB>zIju=DMtY`uRMmz^S{~VZ8B}_dSa%X3!jO^I%ylLu zfFwE4T+BfwOG?wYAF#HIBjZp;iH3-0N&LLF51GO3FbhTvgfSTRj!n}6FFbtziGyh1 zBT}B~$r}Siv=0*nOwt^iZSJJ>x>v8hBc8tgs0}VLVNUV-?l~S#(D!{wL%`VpxFF?B zQ9cq#k3srLi!z_A71Mz}MF!PKAV)UA|5t+LKz>AEGoZq6^S-PQY{fuG{*NPD(r5h8 z(O!dS%8i-9Bq&_&M!#AD(-KQC6{YENFGnmefJ>y>cYGKSr|S)+q=M zk%Pv{l2cJO^SN+wNR4YNVtQuZLh|Zjb+{t}N+rfqZ;I23A9)C~B%%9uIA@{xbHDA@ zEZv$N3k^)r)s(R0UCQmO!vu?rV?xn?h7-kFN|Ui>AfGPKs>A7w9?jq4``qO zZGcxP`?i!9JK~V{FF4=B=IZymK=W zO{MJ*mtvPebon+pEt>rxWi7fPHIxc>lePFLgxuy^rPZKz%DSAha5Khp!jh(uh4vi6 z_}fo5Bx<9eyV0GuHvqA-Zbir^4t(=s*-a~7K}M{@z>iy85C;TOw&ax^eRIatzl53w ze2mAeA|-v-^_h1mR z2sTpc#|$W{ao}>1A%A^GX7T_{`1Y30D)N5O34>ZZAT-L5i~{qibQB;+1R+n^eKn=X zKt)eq$$-!Q$K3Gs=~oCC?V5$9j&`F+t>^EXlrS(netDCIxBTO?Z&eVOnrW5R?r+Tq z_9mJ{p6l`z+659LvyoanlZbWD;kZ5gx|()_71?3B$e0-zdi>&m zw@X;I!v5;MsI?Ja&3kk(%zzyeZV!sh);)(l0|eW04~;-IjrCn$RtkGZ+^ha!5iqLh zTUgMbJm!rvex@D1_P)wn5AG(Cf89-g>*gR|^nIo0#gc3O#P!q) zdOCrsD8bpCp>PzeXmLxj&k_@*JmG3XtCMZZnZ*vb@Ez2a{r_QkW;n(`ZW9A*L7tjz z^pc44%U?JKvwmOFZM6AABG{e>Ab=IMrX<0`SePviAp&m{ITiWPK((~AWR(VqZ3p7g zRpZ-J`$D7&&A$y+;`nOZkjJsMwi^SW*QcS_b$fu#fx1}_2^m>4+!I|aqo8T(!*D_e z@t+sZ4<>2unr)Z{`|7pKu)c?|PeB2qui94`pUxRl^co8fDI{zau3ze{v zM8S2iNvo0N^FJ0K4*geA6mSV3Smz&SA+#27V*G-x0SNTJicHP1jF5DAC?4MHW!11` zi6&^)vPmd{S$hkNE{@P1k%s)v5<{qTF<*?Sb=!?~pkK3qg!DeE+F&f?qxkZ}FYk@p zSK|s0l3B_7r&{QggM}z+&PsAY*5o>dhpk2<=zxm91)$}dE{%M+dk)inC6J`{qvYSb zP>ZUnD!9mHiDEK*M#WZ9Z6y!2<^NUR{3BJi;&CpfETKORpSIUFRAg_X?Pam}r z&>=jwu1*c)VT719KV$LQ+oiVsIFMjr4J+JmAN@3u&#mn)&T&)99!gkT#kAt=LARNYw`+QHfq4Rf2?xSEE$m+X0#Lr> zOqrj7Sc@)+NZC!P9l!RYWcX<~{%atJ+#|QjVjzH`F#8Ub`Zo(BSrk)}wD(i%%jRJM zaUUh8LykPvMoJ1k?oe~9mSP65qz0r-VOj2w4=W&f^>vyl0&~_4AxOE$ zS|EG*Oc!>AWS90$nMaLm5SXL~-Xj;PGsH$f{m&W$fHAfu-uBz}9}CA|QTm9M6P|Zi zec|DG*P}S949jBk(Z;B~^eR_{E!%U)WjPATY_C9@nbJ38+&|ub8XQCL(pPnAS-y~W z>$gsvta4xao}!z-Qy9c3kFq9-91KA9kgo8+$Y;6El$q8d^XvobH$+P07}{|#y}<$S zKT8#N5qwpc5Bx8c?r&!km_Ki)l}3XS3Hcma-@*}+*;Yh4l|pC)xZE7>;%bb7*=9wlwK9`wcda1~ilGXRfDZiH8|t zUY~O%8&}qDt z@fp93LmoQ7+fRKs#FHTD_WgP0@GWuZcQ1BZe9_|F{`O!yJ@|qj`u!{dz6!O>D7*8E z>~(s}&QlRIQ8df%D;a-(M?jXs-D~u!6f>f~e`vq~(Y&<|J&#stL0)dZA2z_AzVS<6 ziE0QI5fMm8NdAwnL7_dcr9$pwor)C8M}9x0!dyQAOS`;XZT;&&wYg9 z@Z9l%NbZ~hYfd=fDBLgpg-Z3$A9>B;bI(rkFC)Bq5%1&2PS`g!4?MAGTF{$aj5+$+ zW7JXS$wq$eTbKSltvb7pZDKF&f8h@O3&#jN)o+KJGf~Q@uO?5w5?`s2KV_Kx);lR? zvP=nNgWU8_r=w@(%#_QM$70t91&JqCtZX*1sbjx;kB=t0PG)znCY#$lJu_P}Xo6c8 zJpuCL2Yb!R^{&KgJXmOq?e{7}FqGK;5N-Y!u!efDik`7>DHX3A8Q|MT6Gv}bt?F_5 zL|Psq2_nGd#gRlLSo~kGmzOEdRj-XdnlyErJ~PNBY3jGWsk!2rq+j`xd+6#e zj8tT3MUTK2Rmq6&d!^1kkibF*5e-}j4KV&s5A*mJCiskk%8NJpf?y|6R`{wByK0CR9t zrKiXWZ!H#$(*VH>Ko8uZ_<_bIYS^H5M=*G&(x31B3_t9E9kH|h`?3ST>`4M5hFoP2 zjIWVkEpQ9$Jw#hD6+Hk-X%%e12*vNisy5SnZ;;2+BcA3)OAX(+Ch23fs16HT5@Z zmymG3cYXLHX(X);3@YHs_%(QkVgc>Zs&=?3)|sKoFyFEafP-E-;2BOIpZil>eu6Sd zP;V-6@EWbE>$gFEm{OwQR?D&(vAAhgpi2pIb+Q*z zgz73F8^yrlTm{Gj>F{sJS`jFr@>ejW=DAyZ<(&q9D8L_75SF{f_i-W(7C(64B9u^> z1{*e!%5cJ%6Az$H_B055b#URg0&n7PVR=Wxa^qSd+oMBg;{A?oLs_w8wWnPqb{x<1~f{)!cAQd(+XJK>1hYG z%m3Sla$A@1ojapdLXzh|_DZIJA2rpD&!8~;%4u92xJIbF^_WG{O|3+(++`|mg;nbi z5Fa>@>tcYTvyYLo)8kM_w0%h3?>-C+) zeb*}p%BEHJ02$<2e(B6NIDvhFc6oqrNFk8TWwJ}XAGzy^GP$x)O09_TG=pb?bwCoO z=SN8)a$7I?Rq+|Je;w8s+K+;&qDGPOdB;^p4m4=zZC2`YTW|~vvM?VI=3%#y+;cLN zUYTs)$qs1?00sK4AbJ}YKrVO^O3a;cPB_b=;q2yB-~K@lqD=AvW?*Hd*F+yF43b0& z^IN1l_a5ecaSztrjc^eW6q(t>=K(&|1q-`oo-^eJE=L-$`+SnM?u@WR4l=u_2o`tj z877`szG8)m00KUa@JE2y^Ya_A~5HXBD?Awigw?m z#{dnZSmZXCPVNgBi)340nG*aA+&B!;kvMqVqmxbfdAsuTE{0u%kRl+8aw0Sje}bJp zd7uT-Zuk&$58mcW2qhn-)Es^O6!~^qC%i7T)%)C6k^@_VHm-+V`38EFoZ~3}^MM{% zH1e5~XmZ!gd}Lt~y^=LbSA3(Us3|(H1{N{z3^@Wtt@zWvK<(Q`A>h;p=lg93FJzsA&`M>c-G+zToAik%%ga@S$hY zzxE{!7``};B67q20MebC!&&V?`+sdD$bHtgJAt!;PVsus9(&>iv=aQ1nO|n4qP@09= z9z|rPeWQnKJXwmWUi~wXe2DnQ`f35;V2XV&7-a!l^mXPW36GUABah1$aPncMFL#?s zW!eB6)^wo$_GRr3rrG#87m+<%507v$RuQdd<1)@Z=u_nL|Cqu?)4qrbjfVQP2Qoq} z$ps1oAtPKIUaQrD3`oMn8erPM!rBiP>HpTG4QSEb%VzzMH2X&JB}#p8mFNM_tdqzS zATCY*R;BBS=ddaO#zt33-K&GnoUSSR*9L)y*pf(2n7g8%HO zA5&6L?fGy>&|-e!F;csLPVA9+zOV+IppKx^SavCw?q;d zv{9kW6BnZ_rcen+G%Og}SOb(m(ohCjw<4DWDn5#-fnQ}aG!5Nv0Yi>G9WbvpP|GTo z50{i69%=BsGW@71W&RuJgNm^=$wYvm+Vr$$Vo(pzRRsOqb={0wh&j-v(UOPyl@&$? ztebt^7z*cb6j8%eGle`vu#2)JuLCbAzRed0a3FjhxNRs~Hx`W4cuXi-9Bk3}TWU)K zUb}#IX?EK>v=yF_va%e_vc5%acyY}#1)k2ntW8GwU-2x-_=m5IT0nJ{{;paTMMU89 zUmVGP=a|o@?b(_-ea85s-aGJ-I5t_RjSQcgRB7;&(-VyZTg%a*5e925Qzy z1D`P4TLj7`Ago}`M<`OhLB)8bHX4RbMDgqi^dde`%T(MTH_uqi`MTg4H5Z(rxUBiS zKx?*fS-#F3Pj(cYgh2g*CDv{uHAV~A2=p;6IKmUa5|ku(zaKycexjkepCa%d5HZG0!Cd$*Pjqene)f zg1ngS!(I*y`m&!dCV9<O3l&&-Huhu&!v;4&?Z&~m<^+u8vI3$i&(Ul_WjV2NB1 z#suV0)4{Jwmhco$u9!mN1jhrw4RHXIY??*t0!>}D4Y*DTzzBfKoVtSqdG%#~eD#)w zCrIpRqC|TzHtR;9=;=rk=mafxYu0uaL{xgGtTY4Kls9h5mURiC6t>w|tF*@u9wa;eNAFdXI>&eSxP2l%=!hlIRp9*eoREg8LF72_ zOb7#E;qJWm7cVo9z-1%|1JySML>Pz5m1qoUiUo@!7@>C%xzq+AxIDcoJ9RQ9L>BimB*LAkwS`lK9``Bwy-7>>yHWd zBD-Gkn@Rh;j28A+^C0nEZnu)kYshQ4((<{;?Ik6MMJ>vv5nwR0XRBsOZZ2?a(o*IG z0G>{~I@NO~?4;MDgCIge)iTZmYSO6h#%dCx!7}f<1ka`QGP0Y8ou{T5;Kgw;=H)on z8fMT%!V0;>3-*Cf%FjYzE~@5J>tGn~T9Cxat|m)V<4V4ZDIHwDrgmkM^efr071#Q! znF(}%T2w%b*17la@n4bY4sS-Y6`I2`9l80Ju>CyUN8Iqb`11Mrx$8LoLAbB}wj*>D z1OZum8brFZNVmkkj{*y0?m?@fRwHN4gx%}8AI3OiH|}Ha@wxt?SkIXJB^+nVdpeA@vZ9KzY(_%|Uv}kii8_0e!=7M*u^? zgIcOvks?<2-**#h@^o9+vxtnT;1Yb<(l#W3g|h)yFZh;seLd<;_@&O35sgRdBr4~9 zvbcD9jF=X+ISzT*srm(KyoGD{M+glN!#+U=Rsa}6KL?QXV_`DMG8&%x82|H5#4Y9p z-Evr-^Z0*cSdoD3heopOcC7xV)mrHvGAHfiVt4&~7p0Bo(c(FOeYY}Oef%Qbj*;Q@Ki zxqHX5VJ~_TWN?}05s_|S`L!v=VZdt>#R(d1#{1>@ANgSTWA0|8{{?x8aZ?qzv4LC? zxI<`VYC+nx3C_0D!?Xc5H+%Q@o@P54yZ7gPiD4*o)~q~(h^osxkC-wgOJ#lnZZY!d<=Es9~UU)>2?1p02-DcF`^mCoB>+XssS?wmFBzqC=(^lwH<+W@MP|IF^ zhpX&y$xLUE(=k+ih+*giXt13we2hYV~?PWFpA5nlQVY_~O`|yFgeDox_ zX%n>i`gZ@K>E~ED`4i#fR|eY&o-w^wk-MI~${9=iky8Hhp*;@dzN8Oc(z*y_ z+&dFn$9%SN>iGI*98vAFKS1z3s@wXiKI$g#r>etu$mwDA3xLsN!#POvkJB||-`S=2 z7dR9l(pxJd;>E6m&8hPZV=&LVS??a-hHci5fv7DNaE9m7DnFo6)lK!TVlhB85S19# zKOp})%=h8ColSR-K77hbOh!fP>x-KSFY`X}X}v^_7;K0p$D>~J2cI)W zEj_5D&AmK#JX<%E?Mlt)^{2$PA(!UsiQ6f*ZNtbw1?C#$nlM!Xl7VehD$MrnaihGF zlKLnW!y1VG;Vg>LJhpHQ?cJDm~~8*O~2b+&1)&gBgpY>jfz#L5-#- zN^co)hVIV6PnFhvUZuQg4^E$Wo4u1s$|Vn1ef)KtRqN3wpO2o`b7i}xxNTdcn>ybC zu9<`445|Tvfi7R)i{tiCpBF&TRX{+%eg5DO_jBW(bhNaQxpBKvwTYHw3rWx~^jmUS z@QPGCYnZp=Ov76X`yD-B3wnHft=_bo-3l&CR}FC)NYLI9;Z;wpwASt1DqpE-AL(>) z{(Rw+8`%w?u1h%0-aooyn_n$L9zu>cm1`5RFTiJ!R^!`_emuL+ofnOx<;+?o%Ztqy zA14`L#@Eh9&%f$IWA|Bt=*4vI4CzJ*ao9WkINC@Mh( zl`JBX1QeC5faI(oAUWq~1|%p*P9ixAO_XE+k*q|U93&@+4KxkkK8-W>d+Xl&`|nnL zRd4?>HRDKgp7Wfr_u6Z%J#nCwTfZdTeR59yopWfT_TTM^_m$6k&xrQlB$O<@i|`QC z3P0Up+HKb5B1*fvAxF9-OkZ0{Y{O=SGd*w9s{+20YwzBGZ{FP7 zBKSlq=pmRj0cWkKP)*ymQGuUDqKJ(_8D zl6}ZTY~laSBt$Zc`o`pnQAPO6f9xa#rn8SDHV4vE7DeEp4q@kH>o6 zSnKc7G2iWE4EO9}apmeV9I4bnQa1=e2Lt&iR}BjYzUnYVnukf=X{G5#TD0-^C6y9I zX?SHyn+tOhmpy{~qxfchueu7lU6xx=nUQPZPzX-vyB;+sc-4=-KT$zYds(V*{Z?rS=o z9+f(A_XZlzcb`9WDAabU)#+Arh5V8*eRh=6`l2=A+FD9ic0`4B@~2NL4~ffJM_wVm`7{9SLSLk6{d`_cEyI`s% zYnCmu$Mze%TD0ocVfp>D(_ePe3OQ*LJk*nn*~?WLS;~Two$1{K2W2z6>{UlOG;PIB zK_AY2E$u?pJoi75m(7nH)Mx3^8jkc;9zC^ix-iyKjJTzL^^vS=iLBeP`YdKcgn8SYL-9O-ZYKc6`&L6 zCccjL416@9vE08qyQX2)^F>6?gBQXR%jx88Gc^eb$va4if$2>jwfT2E?YS;%$+(W)DXWDoJZG%Co_;up7BhTuU{C*o zBLn?A1OJLk-90xU)AHJK5vit-$+&rX-*z~%^7L_5PoLi8i>FMM=DMV-+Zm!b&F`pw z*dSA(tmzB00(ORFt^Gp&GX8Uj7y2SHFLsg#m&6tboogL^&Xm?RA+CSEN2#j!c#^gr zwb9bE9pwA>JBTs4Ll)HRGFz4mbq-^=Xd2sp>VIC2KYrONG40Z(wk-9g7*(XdJiM^Q zj-^>{qXIXBiA3keuj8#P zc#K8lhi<*N%MqZt8S6Di71NtHZt#hJsi?fe#>MS3 z)l;GZEe>*42cFX(p`;v~^7A;?q{(r7IJH-N)nuvVd7n&S*X`A6i|@o&^RkF!vrE*U zKCLMoTdz{%Aa?$GYL@+Y(5oO7lgXG9m_=`9@pVX(otLqOJynM=M;ahw%(tIbFR=g+ zc>;koaIRYpy${;TM1S5VHy}1h!Aoqz?V_6smekBBFp7oK77E+QGoz`Xv1N+0oS446+Gb%$jweKJ1>Xh=UaL&0aylJhbzU8zV$?fI>W4=XP zGW>`-J7awVI!5OKU}czHh@|?DY9L6YI_5ZJ@Kr*lDsXjSw4_f9Tz4&`aw_#FV-8Pj z4qa^9_$i*q*`~zQOP6JS=IDc}_ip4B54mfJLog?ehbSX^cHcG`%vegptrn$NUmY4S zp2j_=&g_}?Ez0@y>C6}VvnD(?p^$sM-hLa6T zxRC2>&SS1h4=$?HNINL3e)WKk(U)fhvVK{TpGCj*P!3GyYGR@w?F@k?kFf4~M zIfpkWh^XM~O?wkr6eaoKK}yN7d-FXGedB-!HTF&xx)YompcDi%&D}COEO;Wn77?ncgoZ*QwQ|CU=(<+sL7vU=#Gm!PlkGWlI^D#?)FED zgzi}mkiZ=Wp(4 zdfD^F?pED{=!qBxNS>&uEt>ymf)lI5SW67I;yZhf;jDIX;<>rGqH?bBFTQwlI)yOD zshsNWVJ{dEDej$vVrF#aa)&WCH_@#zEX?IX$5-G#xU6eA^dy3cXCmA%<~wK_ zXL_~4`vuO%T52WK&Fl0{pYxD{&Jc*4epu#9ohI4i&Cm8&^Eo6Ey(cos6Mq5~PmPAEmlw>stz;g5;Z%Vbdp!JNk76qJ zm>rFPECgScVoWEwHLBt97QbDmD6TtXu;D#*|Kor!G=6=eXTVJJtL0+bierYBHM2*i`bjS2QKSrk%{bBgY|g0QNg%nHYEb}4LF}Zx!b%5bLFp1mKHFb3jhK>o?rhs@L z9tawOXY>qw&W{}9{wxkC4b`z@V{jHo(FX{xrO@J6h61V`drhdM>*p`S!-ZdW)k3w4 z8GZM9ky~?&@1ZEps+6W^@2fU%0EB_Yn7aCmvj)sjD%wsrK~G^-AjUOJW0N5X#CY4e z!A!OGA32)|@BLNZSkwFF?hFc@q-g(ORhK{cff+6yf%vVrx|8IsKA}EjC0Tttx+8Bj zQW1x-s#QxY)OOc3K9b#?`s-I%s7EnXn6`|wxLEm+_{PNN2-PW$D`sWM!z zjgAR1LO739>JPW?oGOQHIRSTiZdU^v2NxpD19~{T&S6Xw2gh{R(5-q(zO8E_Vx!kp zf_AM*dG5>SDF=+^Rxm4I*{Ty4mlGMi3q7LcBxzXj65_ztQYyjLgoPu+sqNxBjd2}< zI=7o!ola04JTmC}K0jPd2v`=!^*;X0Dw%2a+_vM$@d=^NRq#3uLLYMJ0T5|XM}O+n zDLCZbpZ@sqqdoNzOolIw2uQR_dRp3in2-(^uMUeElNge&RTs6{C^(xp)L_?O9}2bc zN!Xz|cwicXq}}^Pr79bj%&O7H2`v<0v#fu)tDoK~y(D}hIdy@zGttI%(U|y}yn(|0 zPv+N0%_!_}!;<6f!y36+U_3k3zFnUUblu4*@90W(HY+a7s7y@Bwf$YUzU{T!%>pN#ai@M7dvw%d zfRocHH1Ml~kiY*EjrJQ{#Jrpmwv-a8X1fzJ;r6;DEmo>rNEBd%X7845jVYB4N4VB- zvlgMzR#fL@X!L-h(dbpDPoc8P$boc_P-!2HVy=Zb7FqxGxW_@L!x%aybmW6)z<6&5 zkSUIZt`0yygJ}-#RFUC~?Q+8CAU?Qxf#S+@ThVcq)fJSJ_a%a+xU>2VRvD zlu)z6q57c=(4~T^YM4#Z`5fkuux>Y8kJbBjhEB5eXZyh}`doed79bCe@cYjCEQ?x!&n^cM$e=wXj znEc6NU;8;Bg9p<|J79FEC%|$nurr!JQ;3*K9|1`HJA{GL_D#Cp;X8j;zy+89MaRbW zNril4C0ucR2AfrIs}B>0;BvHL@<$sbIJ<^3nl+#WPd-!H@74$WQD>cryj3p zWumlz4Tl6K)J)~^<-@Pb?r@f=I=t!Kunru_{4wAwwYI*#PCW9ZkAQ-)psT_3Y?gIZ zdUm;SA>pVBYY1W^&6tE+*LfgjU7}>rmzZ>j&IF*#ALg9>>C>ly{B{vXU<}svu8Z*7 zy=Yaavt;}^uJ%q`a@yt1YljUpJH7{vQqiqWp}~1-93|NnEB35F>xv1OB^7L%n z@Ku>ZpTI)z;lqcGAz^@%-vUOQedzi6ee^&^%*QX!^rY|FfBeU^Lyui(ug9ia3Bg$_ zQ#IG3PnZWh3fwXs`rJCOhaFHc3l`;6F`dV{RY|=+=ko)!koyGX=~j^&O20;2`k`Ce z?yLHEj-a#}8{QP%7A_XK^okhfmRt0;5gAKe{y#)mvxWoFkjV-`O8xJpx2dG+_~Zb+ zNOFuGH5Mz0u#}GD2l6XK+y%zjY;?o0yr50EzxNEJW$mCbw1Wt8=kDDlhw13d4d)#} zPTYeDzF+%VvVyQ2X%liDI2@Ko*&soA5L&Z`oIJN`ugw7!%~2t@wLJEuu;Ke}OVo2J z!}x8I@amA)d;<++7G6Co&_@zd2_9f6mGnf_1%oqQmkVff+Ng#PV?;mzG-DH%=v*(j zZ_l3ZaB7)aolBZLt30}c)426D4@=+uym`gRcvpaE{Q6|fux{NP0oOkg0Y>xvBpWU( zboF5ovws+UM5wbvg0=D%&Se%G-dQmEgqy{PtF8#jiw#;n7m&vjj3++3d`$|p5{W%X zeOkf^`)@fG(Xog}7LyONB~Vm9xW#mtm)wGjUjg)%Jcf-Yp%z<54d$h{!mE6lL3eUH zs%XCFy$=&qCG(PGV`ogu>NQOutA#CtXMsMKc=Zv1vm-Q`9W3CX2jY&j>()dg4tZAjhMV4QUHZI_^L2&fpKgB?n&ozbmHm# z&(6CfYO^dg{_Pi*r(S)VDHH34mNi38+#jgn;L)SkKjm9ao3~jAX{)g=DsDs&6w`-Q z3B|_A+^1KM@mao!F8=&;13z0>R}PtdXUG}hmHr(KF+zABH7Qpv=?`J2#X*j2s#X6F>3hGBGxxjR%po0g8d>{=?>emT`>3pA~obFZmP*;fy z5>GOQU7-m~CJvK+?L)!wQWnGh>o!V+__~|yAz#r=%OJQK z&KgNZh;`T3odT|drCU>Vu#aR^M1ux2lf@Kbtg=hOwaPi6!ef_iM%s%tgxp=;QN&+Q zUwGzRWBT~;CxY8s+2Yz6cp^??%+RzzKU!PaJ+w>QleKm zQ{)YWz^(SWrQOUXRWWIEa}x?#RGDH*BU#&~ zYt#r^*UVPTvwrl?DR*@~&un&2{;Y^bHtc(Vu9X4MdC>ZkN5Fo*^wXKsQSxNwOe0Mr z0HjA;bchGUzsy=`sRdqr8eFmY21DDn5XXLJ61=RYZm#8Mt4X~sm77V}yz*@PBfDKG z9S$$-##Z>BK6xe>Pu5*qAFH%!bL}{fzMr-cpQ6+0O|}X}kCy_Av6<2M+rRS?t80Ja zMn;NLarcgM8Ra9;dVBsFf+yE>Kb)Kvc2tKI*La@HTk==c1q2(r_i@V4q`B8BWCr+L zI+kG`Pd)ZCoCx#7rU!-9KQG2J!D4V~DGh6#5#!bcx5OYSpS_sUm&9uZV24!R#J6P7QDK<&mf2nt_|e41^rV^a&5)UXE9K z2CQpu7LGv8N9*1?EpY*Hk(AH^{SipN@Y0E;*RcrRq{6_i$2?P^!2-7HQI9yD!NfD} zj*A-*uoAdhQLmj%eoLHHBT?Hq@#De#iMdF~H~P2MP!Y-N_U_A37=?P<>0lR+*VfA8 znP@{)^t~S~+P+Qh^uB|?i=kNk-+80G|q z=~KQdw5PFah`tLUU^*IA z@1)%JC^2n6VdmZ*W@KWwzPNL1c{74~=pT^&>fj-2y0}$Vhcta$pJLI+yTd8n8Yo+1 zw&9)lHD?2*$e+G{d8P|GoxN(KG9`!Es?@NDm*U&*<8hK#^W}&9A&qpU^%$$h$irx(%Vw*CJzsb7XkS)<)!bHtpXN zqi*!3p?UM4qoODuLK{sjaW=WOkv`Vz<5?`<9gB9Y=gDlE3uORs0S~s$i)v)Jmd!ip zGJikVmokNq2dGZw&uL8x_*j~Bx{T$a@+)uWdIoxnuXa6dgZdmh!6p0ABekUMSJ(Ga zR3vy5oDAuskX9TaypqyMOJ|ROMCV4NJ)Ip9DZAPXw#xrxgWc->u?N0YVkeiht7~et z+GHUWS4pSizGS#+4=CVI-|e0!&RyVBtlQ{G_kTQ^_Upvs;7nakIL}JOWYyx`o;UFk zAq)m1l8tD-A6-Gk_alX8mLs(@1m&=-xPsxpaS!w6fuYeTd5JUtWAmVwWErr)5r_uP z2t!lbC>xnK$r^Nu6=D~o;;yve&wDhuJCCMILZ!2;XE&N(N$`65Cjo!?i+VF(e0r_% zj`x?(ON!nWO0x@d$8psaoyD68+}kC!EG^$bx|fCA{VoFxX}wIgXSV-4X3cKEpsdfx z=VHis5`Zzy;#wYUvo8u+#!gGyp!-qcILQI^WC1sFeS*i}6$=mkHma8>MS5fl_i?sY zXix~Ad8rwuTLTflp)?bUwZQukp2D0Kvbk0-9p%*C(V^GniYintCSd{xBI2t|99T~e z25v1q%5#5~UjIl}Iqpe$WP!4s@Tw$@l!H>LgIG#Sf3f=9tgzPmN(#fE7h|zYV6ADE z1O3GmornB*qcS_}foGY${UQo`x;hReaPS^<}fsA5(y*^ z6gk?TgIUvGcIKJ+^#X;|M<)-o8kTK-12pCcf?hLe7dnUuXkgz$sN5U;a^`tmT1{_~|n-b`?qmHAWyc z0Nq>#bWdhivbn&iZ*_GQI}P?Z`It1YaPGlMuQKxQ-MhD=)(+q1^z{n5vvO4?5+mpX z+5kP6s^7xge~=t_yuEOqqne+(Z8Y3Pcvc4`5gxpAL{ej4!ePVhu)_SoC|I|SF$>gf zpC)lJ$YJyd;Cv@(s$PpC6}2HB*#6D%Te|_fdf!Cvys@;cmTDEJ5x;zxXCuJFZtREJ)_K*v zMl$Iqs_RAPN7J-W$yYm#7ZpR@qB?*9s!5S)`776i#5h}*%g(`+&s;G) zfQ9Czz0Q;D8b2=V^an-K1R(z?9k{iKy;g6{1F^Vms1VAD1>+2=fa7u4Ckfl{FPM=2eG}9&7_Hleu<{`64n|!|H8i=Tmx{jl(Tnu*?>#sSF(uS8 zgD2cC-{xn*5o`WKuxpl^w1IoGg)ez(03YO^Bjcb4$6wm5AwAVR#>%&;!tIv z*Q!6}N+?@>$xU8#=f;gE?i;Iy8b6?P5v%jCx1v?mf+gntL}V-7={b z0$WT}lEIvlTGaKH1nWp1uz@nH)CV!?yQhac_!Jbe6&@9sqeZ5`e4Y@rPc<$$nGO-h zc)r;6VH`mhu}!q-_Mts$&O>s&t#e^sxZ4sOFxY<{^ne&oSp28uJitO zl45oxJ?5{sYxT}RlkqUIlWi&r#2!DIIu}W$sBy~o~%`gs=DNKpnPyRdJUW*+Ss`f59H+wkbQRjM$dG>3l}jU?zB$ES*ORdbdaxTmiL-61w^mcZN^VgqK#a@X+2xASKv`1V*rXzBnZT+1h6xVZx1H0SSQt&BHJ9F`2aCF#;ZL?wx0cor#yo7u`=oFg@Cl9}is|IKe zX-)jOqz8a>+K#o+&$+*`zG@#5l#YbeF6&T@8aBOYP3_7hQ*t1t8R!FxwUO5sO^at( z7J4OMuCfYd_y>r=;eQ}M)Glwu03{{OhEI}xCX3s%&90jJ3)jQ`8 z$hr069f;@ykw!v_RrZglVdgwL7sFE_y+o<1{n;RT!}a*j)lJN7^|O|i(Q}CQF#wRvPUQPw?g5(1&(Obfi3vtFv-gqi7eE89$_1k2GFfCuzMB_y7sKO z%~8{exLph7;D7uwc@L(|HBNJ3E>NZP=pdAy6Vt(ZK1_{x zIWyiS+4!GU^&5A!nXW$8P7#ztX!(LgvdQp!HvoIT(mecmA1bT`8TWjcfIRvhvn1t$ zo5{!c7x^&2UbkLexeV76ZUwfppho-+&Jf>|{CBo(+t%32dMj4AM+h$oK^BkG{Cz`; z!1oIekLKUQA9CZmTktzwP^wCb=UER`BHu@DwG|}}l0=D&8T~Ki5X7M0dm<5XKPacp z825P;*wgYQdR}#xi;RLc8`uyY^9q8!Qikex>~RcSTg5LJz^kVn%#@;qR4z)7UI9$n z-V|^;1j`u8z8f z_DmDt7HAicu9O}k)N7L(8Ro-u7Eu1qm?zD-p0=Z#6ZsfS00q?bDD$3(o5QCVm zP6Hz_Sa5ze2>`IMx@|UjZnKiVlNl0}^Ft=$IT-g@g!q*6$Y5EQ?m+POgMNZez+L&? zz@f(Tj5pD>4+4SGT3UqZ!f?zz%rE|J&m?YrE{ElO7LReL9BM{rAq}Uy<6dP(rrh!9;1c2NI z@c=Pnf$i#CyunbS*)g$yH>5xCd6`ENgcT+!P2$oeN10ig@4iFHyD>fC4fDd~PVA1; zR}3PrHs1>g59iW*#%>ojY^k~PK#<^Use+L6qUBM3O>TeN*-?u{PF&z;AX!Udr zj&fDEr^tV9G@uL`t3oC^lkk6Az+Z4i=>iv5#|2zFM4&Dw1F0AC1zb+4?ron^{=9tn zHGnpVbJ6=iNZTeP=O==uJQMs#6ET!j9@(u;Vy%0Y#3p0&;d0pd*HcG!!$%3H zPM;Q)4q=mrEa5dYI7z$}*=_)`6R3J=#%`i|Drjoc9(;dWa98&WsKE%eph`fN32;hv zT9w0ItB>SuGVg;Dsn*(AAoMJkg0esfqOp7+dX&&hO3PWMTs9R+qwd={zUpv`0u{qx zC#8C0R!dRJ!H8I@zZqyBGC3hhINOAMQ%mojT-t{3)MxIUy>t_jM$@{TM`V8Kpx*~p zgTP;C)MnHlu{f}Rt1CZa*c!KK3aDZ-DrNx}z-XmXslb$oLe`K}mtQ(Y0k`8&kaBwW z?)Yg6&sYFnI+w#!*-HYt2#eB|Lgzymbr2p6+{|=H_NM!ovw4oE0qKstT3JbO@q zZM76f-&u+e+rs?%49FTPLL{4(70%ZITu<2n>8fGsFy2dKdcmI?V?QUm<;$|T%7f5eGL_6x{sZFMVEb;YCaEEERQV# z)6QjB8^JqmB6R6H(;fI-h&bt=cXG8EdVXaAVmQ`NAkr27_k7z_as+#|ac?bc_&+$Fe_P)K0Fc)k%*fl*G+H?=!0H-cP;{2)1Ysk?s3)8!;I1 z!#Y+NHBu#|GoAOOr5KsYMef&6o;>-%VDtkD9`Kd`O5fEb>K$J5 z;Aeu8fc?a8=(OMa&svv=xV^fYSY?5^E4k7ry|gRbQ!K>Rbdw@PE zLB9Qw=p_iIgWVkx=5K7M@p|-j!EH~Amj%r)miV}&e8Rx;7jgGG9e(bOowV%XQDZxCd&XzTQW3Ny>R*+09f+p(OdCUCB@PxxZ^+SL>@p$ zC_*I!Ur4r5i;RWxPgXU>JghXF@u5xY8!<%1QFK1>cHREA9xMnH_6tvm*N4{`l2_rx zRW}qxQS`urq2~>RkwCftGU=N_f%UgeuevzqS|0R=6xt7M(>Tb@O#$?PI_k=^4cNP( zMdShN+y~H;dXa)&PUL$7-eHcf1ACn15=|zMd8RUUvzKfd81ihhoWue|QGWcAc=qL! z#+&u!?Ur6z%@aRr7J%mdo6VF3e%1e^EiX$^?gbg|^79tJ;O~2=Y9)NU>&9>CZ%@xR z4It!8c;{)5mjUH@ix8O_K9xX$|0e(^i{bd|85F@E z8+SN_xEMT9&vbA7T9a-!qWOlK{Da^9ko@o~4v%|Hd9OX4*oTT%tqFCC?CKc7`KEi2 zb)m0282jf?A*=j3wR)FbwM_a}i43aDzphXgDQk0o3p4)4^9^upJy(vu%3L-#0!uz`zIB?`{{B%d(`oWI`!Tu2X%j@rweT}|1H56wtR?E-3QANW*N45pP zkCjur2Lm=bT_w!c8e2^uS~U#rwO@^-_rPn2-;Pp{cu=%8PRn?bE3k+f!&VKD)+MWU zXP^s;p_mQvu!3T^^9()%G9MTz9h${PU6r?AW!_a9mHC0#thCni3SraQSt6>}&tOaFZMWd`_m- zEZp1qS8g|vuH0=(@=FI<0Plkn=o|0nO+^40gA-jT+Ey3O?LS^2>Ovt-b?oIYR0@MK z_<7lwE6cGUH@8`?j0M)1>L$K#6QL7E==N0B^i$KGsHWK*%LKMJAT-12SlQU64XwS( zvw@qd3|p~KzX1s`$g-@S;^WcGp3Ek~hTKC?5+tkBBX+IG#&RKHfS(c(1liq+eCPo^ zvV-5*B#f_*+59d}kI<*6Os^WKyr2~j;U;toR@yrc?cPZ~HEwFN?{f-Aw?Y`tCnP6a ze}w=DH4Hervd%^)vuISsIOoh<#hWZ#Uxawq@W|k2Af_#W2}K&*_G{p@M0I=uPl!k~ zig6s=qp|L|HlEA_?%sy7>v5{qW`{yKj0Pfl?3DvmP8?8bCl^{;OCDORn_2=|f8Pud zQDMA7vS;S>n?ANcP3JKRTmV~CM~8CVOvN~mk)sZ+WCPJ|Cg$mVpa+qY&zITOf}B0i zazJ!HGP~__*QUUkdvuet!wU|p-er3Jc(nf@XHrDW zKDa1Q(xF9u?!x&ELto;lR!I^*knuKy6hOVyK7cT=1_iS7DoP?{So=p`VD%h0xdepx z+a_SC2aXhNao2Ziyt`o3mKMD0z{wPt+4rF_-l;$D`Q!T^Q-IqGuNgMC%DfBOC9)f$ zR$#^en3VA8hG2|(-sks(K!>G7@GJ@8M~wrsk$+Q{37BQbAQoixG0@n5hu$bt_83$D zip~5!9JKV{v7b_PVYj($!-?oYz(tzTcw05&WKBfZf`knqZM$~ngpEcH$W1aA&*TJ zL|1Nl4G8}PMXt!}*nHa(VsXt}3%G*L(DtY&(Q7oul`SF$cuV)JSbHD-uF$*{z>J3I@Rx+A( z=A}W_T9#G(9DuhmcoO?8hD@D`UN>!pN63g6GkQ^B@WRs|;K#jhGMOKI!8wuNp{)Qe zAl+Ru;rei3YBwDQZZY_*D(2|k#5o}=8bmd6Fvyw%IHnjEd|qA@8YS5Jm*%tM4YLIf z#3bK=QWaED}pX;yahGaVZ`nY?VkNf&_HSMzk zDmgz910r$X!X(UCDu597-qG;9+9HI-py-@RK41=&*?)Yr)9hZAFCjDeiu~`hn`Pik zFk7uEb*Bu8 zv%o-Fd42hEqsLjbN1R=86<7s;aYA@=9)BHwOE7?$?aC(_yU7bT_hAz1fchQ9&Xul^ z?9VOBG$!V*%XOMV9MI;-V11A}vtV8Jgmg3NLq9g6^Q1->D|K(tJ`P)7w! zb=OQ!iP@_}a5NTh{PhO2)L9h>Ohqh#NXkj9yyWsC2jB>*^Ku`AV58(W#%NSSK(;jG zTcI@C5<3;hi_|;^2!^*`M$=lNb4z)~*queu&{N%h^A;50+n##90Ga^Ww7TMd z(u{s(t5X+laEF?=oD(wqLEk%?(XD~DZ_tS8w}HcjGtKA4pdT8$c2S6>6QZx}d>Ll2 z&5BAI4xu<8?`Y;T3^EJ9U^3W4%e}(~YthILbq*714I*)k&B+GB5iB9;ik^WG7k5Ze zZ;&AC?x8v%7~>O*GVa5M*&TB&Tur6Ka95&~ z^kOq!Z`wJY451BZ|ClZOgi*S{9$Bweu9dvb*%b2XWn-*JHUud~U_^2p`u`x~vwX9z z_9R%j2s>n#2-D0|o&T_d;>WPQE-MTlK%RE+Q8&Ux4%;bbX^u~MmjVAbghR_17akTa zQSZx`<#0p@OsrI3OZ0Gupk7#Tn%gwm+5n9404w5xYG()s$$dQ==Nuc*(jTj0ym`oD zE6id@C8^I^rlYXunT%>2;Mj`4nHuGEmvuHx`7buu2_} zeUl{a8H{>muCRrK>6>Zd9{4Z`+D}<7SN6jHs|#Vbi51COg+5_+Q$M&yWq8HERmqw4 z+G&C^p@Y@pfIPatkurg{_-fa3%x~pgAPdPIR`{M4e><%s>?Vp|td-^zUhTTxI$NFC ziD(RuuSd)C#;Bbgfs+ZYn(XeKUWo3?S+*F+qLP&Xti(=alLq9oHqc#|S9hmD0ql$y z*3h~`%Yk2G=xjk1rOgIZYl`~5x1k1C1h7~k-?PW6(N^#2v!^CNj#xAA)IrolRC+{` z;?NzVD5LqkVDFGDScaSxla_0CCoicm_0hzc20N#Uws3*c+Hd*fmCSd){x)p&1DUdM zXt`A|6A0fDkc~a?wE<3o9cdg|%r!KKQJr6#%&ykR`3dgF_DPf&B0aqhU#VmY#V>9H z+k##J8Tm0FGr(2%c*0#66}Xf^$TAs3$GtdO8(mJHw z6_i(@1*94FG)I;)n;I0}dJN}*#mKK=u^+TQ4;pi)5xqP*Egvbj!KcJQO&8X)@QY)pGo@1_Ou|hGk zih@P8qynJ@An-FD@{gYvnRU4wFnLdkDvL%~2BMOYsp9(A96%scN|8H-D}w#7k5-_S zPS2kJiNcYC2m7aro8c(EcI_JY(7){Q=rSuDM|C#1*rUpN>rL4WXx@Njld}*GHqMBU z?MydzMi#8iL9{q9RlEun$~@__2$XS3Tat&|7m2k|@z6iEoa-hd8vE5jk`#u48EMVe z2t_ie+Eld-lP3w$HsX|lyo4`)Sixvo;7g9l^cc=w5i{lyok}@snFNsP}S*DQ#ka?m7+t zd1tv>2`Y=?kOeBZOxXLEJLgpXfUw!+85Q^AI>-rA;P&XWiNuZg6#Lgen3V^mZVhK| zNb^41_NP<;o69KlHX@#&{FdYTs0qHjZkC7$K!fpr@de2mq;-X~7XeMVN>iyo(s`Ho zsAsncS_g$=k5x78%;_(HH0!-Mll1F5z~O@2&#~@s3KV;|#zi*AS&y!=h}M<0Uv3^PhpLB-ju3VQs>ll8`GVsypGKj6Kr2o8I*eIOoU# zdDq&47d6CYZz+N)gUY*vZ&l#0UzQDk$r?2$SjC=!9)Qc;25R&{C@Mnskbc-$W`(m5 zBG&MKUfKil{a1%amloN8}YNAoc3;0D$$Vhqud5$L^^qr)G0tJzmCtY?LpL=r|; z>wV95e0I$}*Ywwe<=km!%=BM)>``~UcfAuAr8>v@b-Vy> ztKPUd!gNX+HeJ7Yu!KSXjJC#!d$q?;EZSU0w={yu6zA*diNgMulLn^X&zgs=Wom#O*E9mHSAIzltik!@jkg{#9# zNKX^Mv6PC!lg&_m#KbNwWK+D8K65Jq^KpxJ>uEs(vIGh!Foa+92r|xJlLx7<0$|yT zz`%kv;0ahKA^DM{sZ$73Rw3V2KJ}EnSmRldiP-dG>XQIz3fJi%uqClA&wz%c#aeO(jG9GEvPDE+kWm}zJ=^K$zIpomL> zO~sqL)_X5<*RI6wZ3f65HVO`@PqZ_5 z%C65mMl;TE!7E%S-ieyNL5WMf$UYQi{X4Z3BF1*Us?Y`(kl*`mlSC*&mhkJ>-|VX< zg~#(_Q#6$bi<*ArX{&Rp4Ql0|XLyHqb!%p+m>ZmN${xCp4-acf0xadhM5!Ot8mW1R z59;YiqPijr-MQ=OgSFU5Ma*zOL8Tlpku{7}i`^@FWXpbzU`r>%P?a2N*LM&s4@k~& z9w$G5b%D@LoHP>pBbj9)=IA3H=Lz*-cZ&o>VF9a>*3MZ9vmTpnnujNbi3OQ_x$JWO zN@^)>lS_(1U)Y-Ch76;Lq;0ZF5t0}13-;DaXOF@k)Y32=hgX|63KlH^^5@<@H|%=X zOH4*R6H52YTj-q%GDYb9Y`De#ChO)SnR`pS0Voy#{|VvJ$dd$Mi?#*3-NrvS(@*2T zk~~^tX?WIB zU4yI6y68 z=kq5AlLs7Xp6QE9oUODeQA*C~>Mv$_?0U}vg~+sz zi#Rx5645PLWwhG%b1S+jN%dWC=79tO^mI_}#71~Z3FeeFESeG09M?AXdFc#Xf?p?CKzwIX8HyS zYWMrTgQZF59m(0*qg~Sm-lvEIqdfj6%O_iRNIGft>xbIX>b+FN9ajE<9VG3lTR5-?+?DVeL)4~?uZiWAKIhWt{P=H^I~E&5gDx3j?KQ4S zN^Nx7%0XuLt!`;IHqo<|m&WYHB~VIWnXHHpT@+0EHvFjIXjIsEh_E-7OSJKZ0O%u* zEi6{`eD?}&mN_(EK$1@c&6N^T-`pVphu+E0Kl=UM&|Sm- zgeYfqS%hv7QH1}%MLeHaAtkFB-(6{%q^MT_xwjEaqr`5h#RqMG98^Ob%zMr@b-i&Y zkNR2O2SB-TuMaH{1-|~ajUgq9knO3fC(B*Q7DB7Rewh-JAcLrpPBXi-_8~`-3~&|b z#IttaeV3~^mI_`zBkEq-`G}Fd^g$u ze4@-hh{s{|fMI1rq4Vc$Zp}8&q{_HOWrNq1gGJVF4}nWT2}O*ucj%f$6Zb#rrD?kLT2tF2wJPqu-I|L8SG&>CnRyJSJ zEkl=a(j}-N3$!Ry-Y*y70Oqdv7}i=yarpD>PoVf|tQE;G(+S59fHtKE3e6^fIssea z{k~`mY4(Ri)dX6Ffh6=X*fv^!@B0qAOQ0oQLA6WdpoMUqUy|wh-~q)wQ6UHf{WYOokAqV+9o)V-`E01>Dn0j)wb`;*c)3XOYOW4i!D){Sif#$W zwt=G-vSmWDiiq;Nm`4az%)#7k;6Pe@rsEVU2&VLF7n!W?+h}hjocnzjG18M9`!82O zmaHD1oFNZtPGlm5T!rk8UzC+@pHcqGLw?r7OyE2$^AxxYh=q>ML6@ZyC5k8PPyQ~g zzg#ChaRyPra%s~joQ3B^dAa53<}bapdVd~E{VOM08-tL*AKotEs&(O z`?k?K{brhciFd75egLW<7q}TBWw`*o<#+zQTi-}}oiOe{y8Zehkb_Za1Ib{}-3gUy zxgZV&#gyA3!zs-g?C&?ykl)Z8$g|~%U-5zTh)EcRYAM8-*HE<-WOJ{JAH`1Ohi7mp zbc_Mp`}c|t;ry}$MtD2yA%iVjw|){vJsE=$)cep6e8?wVOwG$( z^YO+!UWt~(FDp7uvV0{vlQ{S@IK|AY{l#uV;u)d0s5_1r$|>%^UdF$cRxrsd%}w?PpAJdcp{So(g|_gx+F3^ z;vOXaX(c$!{#im=u;kIl3v77*4TL4$!-&x5AQ&zG8wkC$g*ifh`X50!!~bExcFUqE z?ol>Z&#cUDpdTXBBq;)rF=}-!>wJ4@RFXf|7t}~i{0j$3n2}@>?+a*)-kfT2Fgf-6 zX|jAs&P{Zp5P#_Z(bN1to__n2rElU|aAQpsvV0oE=lstFJ+59pm*>1$s^H@P*Z<)! zEY3s`RM*LD z7Zlo%Prr6zad?b)+h48*_ty2Ot>nm)G}@P}{vtHmn$aXs*6cb* zl^95w8PbWBst@ER4)Vi)9KG!E?tO+Y7{42`JLOkV^2}_2k(pk>*5kY-p(S`G=r8mx z824I~b!0GQVWpnQ^bKt#$7C_24>i7eZ`oZ?&)cM(QsJUg1+GG1z~lQ?Nj+;wvT*J4a4u(a{j+fz&v4hVDzA;@)uH|6Cw%Udxe zr@lBq6S5ki5fM(Ezwe*Tj~oZ05oBjtzOlPcJz<=z?@FP0QNZV+aR&mq-)HMw@~zl# z=OT%7iL7nc1A?b$!?m9<-gRb?_Ip*UG%GHo_TF$GeoryKQ?8<{(1!X^RJUI!)NdXI zvAgUM@E(wMXo=6Zhb|7fEovZ4r&Nied@=aq>xnGen=UL` zT%vzVj(D$^{7TgoLK-a4FjlX21^!vCULw)Y>lL2mmMLaxi@S9eJr#DJ)DxxT@Y9%x zFBEPT_d8wj;#Gye)xJ}_e6g1{B<%Xe-pEj`gJF&0s6}1EEZn&I*{#x$NVSZXZ9?BF z%5dZex7xA$JOJ;VE-StZyl2gyL8i*;mTK-i*ASD2&B;u2`yO*pG|(>11A9BT(>1PJ z2|p3c`}}{Is%mJ)&E6w>J+kbS8zJM9z%b*Jm-lJiqHk6dWW24e+b2fA1(MI~|@ziLsny4bPnXJ3HLO0)xojli&R zgGzcWO(#f zerTtjIKusUP<-puVgT|BtcO`SHuyQT5gCD52@MY6TD^)4+WtG3|IW;R*UT2t^}oC6 zzvs+vV5ntp%sXpi@7l(#RtfE$(4 z{fiX=MkkA*)B`Cn8Kec25Pxr7(G31@1HeN%rCzsA;SPMCamaqj*4+tu;%Bm|hgnx;iC~i)1Ov2lB&=xW))gF-wKmF9j_0>L{ zk!YEW!0qBE>(ybiL+cK?H{FwG^7}G(&&s6wGqo@;Mv8LzpHqP#=l>esWN|_=qh7IG z)JkxQo>lz7YS8Yr!z@OjL{~ONH3{D{q_vv3fe~1}S>JJr@v^rzo1{(0^n|95Zp{*u zss8V8KNKWtv1MyTr^}ZJk6jfmVmkz~wQC$>@yCKfTl&%ouezElE%%+u*;5s`0{GVd zFwPDIHmt_3pZj zM*a&k}d2wcSvx# z;0UwgsjkN=Zo3wG-BSp@%_3p7bPB>UbX?cy8qA~G%4M4-wb_&Uzo)ha zv4pR>M+)9avW%V0d_*xh`aV4G+arsti)$(L`^ zR}8v*Py4E8&&;$?Hg`p()!_oh+Szq?cF6OYa-qgPzn_4!{SVt?Lm`LpVs+;zT9=N5 z{>>H@dJHD~2Is-Bs9DvkT_O$fQds&tJq5>;qck1M=VmLevql&c4hM{l&igX+xc>SS z|DLzaOeLz0sskL>bfwd~?d|v3WTdM$S2BkrluiRN^5xe_H^mjMo=Aa2p(If@P*<+T zmUfkgQs_9hma!+5{%RHKJhU1kbMOArbN)s9xaUj#(efQW%)`0DZ!%*w-IKy}$(bs6 zYBK_xdb7LkRJzi22-a?V)tb=w{}}tuuqM;B+dai`kB&MbMNnX*NeN&;MEZ;cgis7f z33UuYM4I#t<0wiKq=g`mj8qW;A@r6}2sI>1i`0w|h?Ec^KnNkpdD#0q`|R)69IwkC zUV2ILzRz=)b>C~PWgn(b5mh|A7&l}{yS& zYgkXJoaVXG9nR|={7X8JFK>#73VU%U|E{~B?`?X3&s*+_^j6>4e@j3dKVOc3T_LEH z1`d{v%5!Eu>CE-`sbw#IwHUiL82q+`Ggq@{Rd0sXTpD?#qSxW}`Q)s0^{p&srh)zb zB+ox{zVdLfw>Z>1=HRZY7G05J^_@-VBxZ@112>}pV%Fhx%SvSadW3MUXJ0UW7axOZ zfNWyKJMye1<#FAO>SlDtow-p@jyw1Gs!^W9%1#G$+lqv`gU*0zzrX~k7+NSU;qURM z7WRsFPwlmZwyBY)S91FrYKLGa}x8c0Utd1V}BzEVppyu5>*fQ9&!EFAP5AVj` z{ng?JoT>XNPF#=aC*{3!icLv)&bU=%=rg-uN!mQ0N}~kSGNR(NzZraUPjW4u`tb)J z+tmzH#M<_-o)xL(gQHPL?9*PyFi z)r03u$JIb9!Sx|fn}{+Q9TdYI>HO7Q``D?~)?w7;>R-Hh`O$L^qmJXh9j=npB=GX& zO-eee*;9%Umdiao9n*@kcbZ|}%`!+mNVLlHeN*p4X2wq#dL7JDg^ zp;5&Bsup2OYQLmi-*QB4$q2*BY^5Rsy&5^FUZY4VbMF;BJA6r;!z~Pno;#7O_qR`P zpNdQ#7|`6Dl+-j#{ieWWY3KRkJ5uQ5E}VjssdI~6z~yDw57b^6}C20B1w{- zJF{!ZFdRJf$$_enkKfBT4f z9P3Iw2hPWV6=y%i0+&UH7ZvM4tU#x~&iM;3X0*?oG%Ek=4Kv&D-ZR!Y4%E9S@yg!f`lzK(0(yDV)O=AlSN37dlpZPfh6 z(pyWs#;u2I?uyRg;15(6I))AW+rmVC2L-j%opJE(Z;h@uA51zsY z*m+&N6B3Pv4h`=@#;yhEYQMrPMw52eGG@xghl8hrt+(pD3lP>z!~1zdx=Y4%IHax& z+fRAdNP|>+TIiI=E~wqk#0@=)&NHb6>--?uS%6Er#BL?EyY9MQauE%ZzFJ4`1whT; zlqdyo2$Z2(#Es{4QL(KQ^be%Q65Nn>IsHonyKsHMvaUze)|EGGd%?!!ew1Y4cU;+# z35Qls$Zc13@D;LD{O`4orLE3h%V^DWU&Kw!Ru-oyM#&Idgcm7ZwCh`SOL`FrShGN} z*pG4XA+j}+FcRrpp8=h%`WvR~?svDWYvXfc`yjOd+0AIl`v@&N8FHKrsqr<_MBlN@ zwy3JzbOj=xG9hH2O-8$8{9$Eu4{#OwoANjrhcu55Q^~b%F1zu`xKAkqIZ#%UI0LK0 z#i^Bk{1na;MEkv$~#{cE*V6)1$_Id6ZV?)^p`E_lQ~8 zMcl|NIZYAZdV@M$DT?jJloHcJohS&{eJAn3e_*V!iHRW$#8uk4>v(^6TH7e6CY(DJTPJP8eJcD~&U*ygt#q7-*1=yFL3+>)VqrWaTxhpFUbk%-Ev=AC^(vNH=$rC@hiK~$mQjj8J;Jo^@zy{ z&h%he@GxW68dDVy`PI=M_EWkWX&v3*T;H>&clIi&$>BjOq(5bhb93$sW9}-Ww_bh8 zIc^7OeewVG8LZtziwX5II!s41pfPW$I|PaSDRTo8^Ny~it@5{r4ay6^qn zaq4Ni*LP)GY4-0fKo^hOMSnSns&VcHo} z@>oR+vj`_?U09;BM5C`&1`=FX`uDyR?NwYSs}0JPU;fow7Wlce^mcvOGIHK>4>w?& zSptRp*Gea5Bz5F(yvk7-Yx%2tr>!Q)I|>bI3RR|Cb34UFvG3v3hL23`e9~%R`46|T z-J(W&_7;Ju$7|xsdyq$@YuK2tdnsYsQErqTQA3iBnpeX6^q{8%`&7lH*rj+ivn~m| z$@M#0hM2*4Ye%&Qn1!$PCEl>0PSmb-9DM$J18q}F>IG=x!|LkbRZuC1xum?z zHwG>e0}It(pI99dv3YfM3miu#C+D{kJ9rMYhD$Q2EX_^nTyiYI!Z?Ot9Jd|R{`wjB?7byxPn@<3 z9rf#6m5Ru9kCY`-;6DHp(@EgMIV~3>${L1_kCU@6ZA!{CUq8jp@7ap`2~2GMJ94)Q zwuH^k9|u%do@~EoCg4_GEBE<7 zn_Q~Q)~#M5WLq7cikA4cxG(1nsj(?8%Pf%I?)vEd;@kFs=ApSrjcQM1ikRC_`Don^ z7in0Vtdg`$~rkJ0KniC}h#e2-i;94(&X{%WifQC(mqCGTVK9Papak zxf!VAqU>W_oHW>pv)VRbP%BKt?3=HP?FJ}pf-2;HfqfEocP^OBBrqje`TDVneI*f- zlIjORR9uNvuju{wdtpDjCp4Y{EJIYj@!P%Jyys{gh#I^=O#2L5!AP2|80R?@Sr}C& zvb>t{&KO?Wa-mYWXs|*sss2Rp=UIj>nNT%}INxQm+cvdt>so6jVD!Dq6i!}D z$PZP$xvsLP#xG6LjdVEHF5bZ7y{_>?RbA*KMjB_8YDb0~i{rYeEGF->uqyJxn4UNv zyJ^+{Y9Ym^A1wuyxq46m{P(OhYc)e9hU8Od&sQs>K6@pJOhX(eii%jOEYQST{U*C} zl*k@lk5e#6z)?aN0jq*h(7m9#PTx{o(O<%OuHG4WoDnivf8&{Ybx*7YaQj`-Yibzu zN6AaNu_cd%llA@x-s@kzGcjQkJYDBqSo`%O6&*})lFF}`m=GgEtES!ANp_Tbj8N%` z94qijCu&@Cvy(3s6(3@`6{EA5u`Ro3OY4VzQ&mc(gy9;mpQS*tP)=hT;`q~-*bnZb zBqcn-iiCfQsWY2qj21SR7#pw)38r%)TL?Qt&m(f>f7KDJ+zn0*e(7JuP$st1vT6N( zxR~X`lgyDzOWH}xu}iHa6g^ra1P8#uQz0abGaQ#1g=#k$S`B%8irrEEvVLLI@cFib z1J7^C*z8*TVMbTcF#1KYgl&KBziyopP;KgTM5t-#0eQp_^chcJ1ax2&2gn<>fSsp& zAl#E3yEfBmM~h`Y1thsj;ArR5mo81l?tUO7B#c+und#gcdk?5Uh1JQ9z&F5uW5AFC zygiDl!M?`9{5n{#WM|?-0ZsH)rn6-3IUA{2+3KJDvgVsaEqL_VXnn>*)tz4a%7xCH z?*fajJv*x%>SCoMZR=!nr;Ent-^gQrr6bF=xb;c-vFD9>;4|~TV0S*$E8{ovA!e3V zIb>d~{MoOA-*}fNeI(rsZp7F=Qh{K+Y%D*>(|xIpM<;0o?=1X|D1Pu9JPH^)ooy6J zuIrM%H?H=I`4;BgL%pRK$n z$M|YU#!@8PtrN>K4gTSvxPnR=M!w-a(FR)j83#suK;+FCP>%@r4)n;1E(g8S8+!Ty z2rh6sftuF(V-J?Yf%(V*FS_fYnQYvc_=EZEsM?x{nR+i+Vq&6Rj7SAgK%3(mmL~yA zi}kf%z}n%#;2#dX{+sFSk>yVnop}Zwt8)fh@3SLTUxY?@)ID1Cq|8)!cOS8nyHO4u zI@gGEwv`O_z`od++&{6Lx?Hx_g-Jh`EdRd~)c9BE=5%~~Jg|1Sms|(PMk~P3jf|yx0`;%x#azcVgBt~xs`s{*7uJ$x zz0KvR4B=c1YZ*8Lk+D6MKvQg;!3A8Z1M{!{e3eXuhm{ff))@HIs{-v9hEa3Xmha5QYv&pMTw!sp!fZf#36*jo=9R+LfC!oM$37k>Fb`DnAz|bh`o>W6!+pC1< zF3lBf!-A+z&f8l}%I{gMS5kFVO^^Mne6+PTsot@9dS2VsIEh)5}xs8+JqfS|o|NSxXNlCE1n0CS?rXItu6{vTCO^>2dAb%1%z`cI6v!9w|2I27;~!-uArXq|K{9;N<>} zEABI+7SR29Y$1>dmJ@0_KEgBV&uq9atQLNt*a25S4m);(C_#1z)SZtKQA<%2&ThvR zD5GzU{xz(#1**R-6I5jZ!@{^y2BvneAFY2W;w-l?A?Y9O)iyEu;ro9agBI!rPrqR%e;_D)@-92Yf*vZ1~G+pQ+sSpaa&_{j>A7xD7Hny7JXV-LDeKS`{EaerW*rRc!q(UT1HsS=7F-!%HWO#Y^DF7=YVY@bo*!&zay6u*wQEvhH0XmTYuX*3&TE(9 zx1U^PT^9kp)PI5&>D@a`pWVQiv0`C#oUSk7(puM!keBnXb&T_sB&T}hs1Ltu{-wsZ ziFz(I>Bxa`B52%q!Ob26+K`QbH1}@~A0GoW*|l&FlOA1I4)80nl-3Wu*&Hx?ARKNG z4&-(Zh~VJgnB@W!dl1DNIDjp$Z}81^2Dyl798v_>z;00a#QgC)z%=R}&~1s|{d71& zlISM{Nb4Bj7aL}27a8dQ%aWiWl`&^ zKv01S%%qCo&;pZ$F1MQASS3J{w)?l##FAS9=Po&LZV1HE6@edbdlgkX*%=ez)&J}* zh>K|lB9NT8e2)Uf!uyh7zz6mDaJzcuEN>Ffv#;@gGa3iIr?3Nk7lW7;cTBQBcP`p8 z`Aqf zI{$ZRyY|_Tl~%~BedLkvXe7;w$24)Eu2Da|7(JKL;oV(x$Y148fbsY8s%!jeIeQY^ zH6wJKNkP$zMLaHL3c7^(N*GNmv7+O*`Bf>;Gn9*iGy87cMPK!xsQa>pbD+`HFH1SR z&X*R$As^XYA6kEtigo*QuA-eT66Fr!vCzieU^+FQTShWK*lTQdsU_stSv~3SV^Q!k zC+$c&X3Xjm&3N^)h<*2)3s_3ky(-sC#(+1DW|$p?TM7YRqgegsHO<%-WE007VeU0I ze9xq|*9hC3crxUK|M}ea2AQlG!|YZHNFG&`Db@AORPK5M_T~&Z($iQ1)Ud8K$Rw3J zTkZ&i)pvxmQG%B-?ari_YMKfmT7wXKoPwc@kV;24GPHI=UXBX%c(0fE_CEPT8nbtd zr$1z}dS_g=7TlnWOB}bw1=pP}7ojUD-Z)N<#&;y-IdrCT?f00kUrp*~81w61@-4IM zS^?nlpZzx=JgBvl5f{xYQH-t|kkeUhw>BZo)o!=PN`Rzz5Y)3Q>Pq`|rB(UMTHE_k zPXznCNBf$O^ka~wb@Y{gLCD7rltPVcQbbk8UfHYRVAKQkTfn_vA4!r7Oi=+2drf!$ zYQy=K-F$Ink3&l+!ka=BpX7POPp(sn44P*NK;aSymBWB0ZiuWew$TP>OorDhis3~q zD{aeipq{3j%Z}@v9CxF=&n2qv9WBv2Yn9+`e_W`s*8sg1V1&2j;WyF{Q@t})Dj|N; zO~Xbm{Xo|A)dAt$_jQyRmcU6<8SJ^f#n5K#gb9E!iApcR@&~-74WJ&crKuipw5bOs zojA9y&){Y^9}=PJ8PceL_dH7lDn`}nqZrm>&0|s*?}PW=-5LVdo;3RM=Qrz2CC3;n zP*DHq`6DRd_Fqoi&XtPi_|XC_k|nMz6Jca4zCQUnf@Y zdP;wS#3HOJMRw^XPeqXl`3cLpJFjXknxKv6UAB7;B|hcw2&L2&?-6+$+QM7}6@b6k z!*PI>!n#U!<5+*|2t`TbsV z0=X-r!Dg=dux$I#s-Y4Hr|(bYv#9J$YM|EV#r}w!I=%JJCU=H$i?ehre;R zO_$5KKDV$KVv_aA9e@r9Qe4`Mor{+|tFKc*m8@3RhCw(T9%s0h`|=(92Zlw;e{YZGtD{LX6}C7C9(pL7 zVdC^sDPB1B`#{_2BcMz<*ef+JbJB{O3h4auR?g+(>zRNubqr*e@yNR9Xpm`SiBoXc zMyseba1#vcla6Sipi;huf}$^73zfciZW0809~ABaFbcSv0ag44zm~dcSc74f&)nWI zI*>RHPdRi{{G+FvWo9X`l?smqeq4S&-8tWbA#@L&kEdq^orZ!x{NsTkA4+E+<{EzgkQ0HJFjkcJ@74OyxXNq6U^P;*7x88_`hOuhqf zuk);xE>&Y7A$S@U|V4jk3qu1!a!UH631XB&Iytk0`yk4b7`RNH))A zMgJK9liWsauY>mdyr&1=q`w(&>{=!&V&tv#PdkXp+=UgI+EQUuig7$ZsZ?TfqSaDu zY0}%>v37fRb<0W}f#lGwjq$XRL61vC(jmgDmG;GO@6MZCpV>I!VgfZ^D;f2+r$^Mw zvX0K)AZ}gB-C8GP+s*|Q$VAeu%aRP2^4f_i6O$>1Z+PN5c{2ABGFo7S+E<%ZY+P_~lbn{=ebUyIJ zl)M<$>>kk6FNpc$+E7_^<+5BZFSfNUW4b0$SU46WOeOa#i^6Kipf3QX)4jV7`5(1R z-soQcmU`knupp=f8WK}+HJiyQo*h8jeo{@uurZUS#)4f>7wTpplhf4FZmj1x6Z_jS*p1D8n{el zVU{m?8$(kO>)ya0Vf?_i*IhAX@veRO zsNmTW6)-DIJ`m_LZkCMMq??1ww4}WaA>FESumZM$qrlG_2-s;=JJr3zPD8xts?it@ ztMzsID(?aihK#7}OG~a)@HF;hbK7fB=+7G$q*1&`dih!A_bdbqgJm(NA(brEwhPhQ1kO>4+5MB#va zMESQHH};0KbM62*SgbPl1Nz6gG{e9x1pEkCpdic4@5S|Yz_m*+ezyJD-gvNbOHVeG zrClbbeF?0?koJA3rNQAq5%XO#^uEm97)!o|f#ouqqhR`d?$KrQ6rzZuMT7IJyaH2y zZQ+^e;;{2!TcK~HWq%CKL=M@{I6b2YNkYzYKS4^jzY&Kl~8yn|QmO7H4z1w-^kdiK5=$J^jF2`52OqDSn`G6r&h0 zgTH;PcZO0`YvMU{+*7WvU^7YOLWh7D;yyG$SKfa)yBo$G z-?2IU@b58xnnogFCqDERy!$;rcY)U(#hzM3^UW)RwvVFv)J;2) zdGENtz8~5#?pv$%to235L!pToFI;>-)+ehx!LakQ%)0{;Ym}>cZb6`}?73fIIECG)|6Wjv+Rx|R zI=|WZJm0NXrn;h*kvj?i=P6W1e0nLEc>R1fUpO)in)P8sh$}(E0I+euuIxY0DQFSY zORSptJRQB}k|3)U+ii{EtTQoVBzZ({xPLPddFI-Zez3*JWDTMD>7^$Fl|2jAW zkYrEq%p@7R(@Uix#V|0Pr*{D~`XUlrH{&I~Xiu79ATReC7R%dh=r}O#daHBW-f_7F*Z-JM$ zsdicrHb=<*6@F`b?l*$Mqb>Ivtj_F5?pIlz@|`zW(xnj{zx|$)^oM)QH)7jIl#ir{ zzJ2WW_AxcQ(?PVezKid{AQ|IrmI_4&l?Hgm6qfnr7;me|Q#Y6ToSVzOpLQ=2@S4|0 zVL>0qVG^(uNG0D8{bhz-Y&JT*9;Fn=K_ytp?yfbDwkT4|rO8eYQxzL~Qgb+8DDaqzxOE{)Csh$TMEtJ3F~?$lf5_7`O76%BI7GQAW8|IqVe7%N zEM+;-Y18lJb~4Itrug?!WOB&-_gRl8jr3K~*DC2FHi1;zCS^BNj>e8&gu6So;$xF9 zRYRT1`A{8PDTn?%;7MI~Azia|pI16^Zi8x{__#$kh*tdO!-vkNzG=2MNEbyGh-nvH zWDNCWn>*ahh*`7NMY*+vmcv1DP;YK5y25D$G2eb#zsaUE7QcaAIFq6l?>h^>aNwl`L$?ZK!gxyUnM>L-uDSQ(ntSp$<*G1}Xe zvqTZ6UvM$=y31?LalqSr8Y_PC;8*5EkI*K1HrM8sU}!3MK5>4j!tfT4?>KU)B0*(X zkG`Nz%z}x+u3dBXhD@qGon5T<5r(j=J@%GlM^Li7ZPnbv1VQ_h$tkRLLDAWS+FAkX z7X?Aon}KAHK|S_FU}@svnHNqY4_+Y$>Vwx?8RZH4B|!r(>M02VwhySox0xplzx-)f zMfA#Za}Xj#|1qeYbSp|PMG!FC$OlK0LT^@(%amX%mHHWJuHatY2jgQ}70^E>FFK`|bZ27hk#i36qvsqo1`lQH z8a2jfDCYD>B7^IDd;lNx@y2zGeU);!YyJE=+ehpnGM z>=jz*T7jUm^yf}N|LC2wZSX~3$P#!qt z1f+K^az^>5TwIx9fdBLkG0V*wn>**!?h$liXu2jD4tXHhQ)a7ca&gXzPwGtMNpGTWE8TnLR8 ziYbqI9y^iozc_AF9?ES*?Nb*vJd$K%y0d*seKe!+7_LJ#snQ ziCkVH2}{XWMRK!%I=rBYseI(x_vd=k3F$~vQnZME!7ubhW%OBqlX4kF(iMX=p@6g1 zOyH~Y=OqaJL(PV~HT*IleZ*-P_Qa*sc}8I|@Dhl(%;cSHt4&7TtRACqbZ=lVN?i;# zFX)446B}pmcOP3uneY%@y3&e+%8ST>>gKsAediL)3xy*vVtAe9$RrBqCR~M=n1;2` zt@DusDU+LDn#&Fyp2-?X0eBK7JBr0(A*O@{N>rLo`77&=8o)Ic6wLP zL)U;Le`@}iCPjP%QK#VDjlpyE)p3q)G%Du?D}Mbq*IpI!?V*QCx}<)%b&j+~SH$$i zfQdgl{9PB~CCID>ClMU+hcnQtL&G-f*t~#b#Yhc4DSUa8*F>Q$>{pwOXUGyVv~F~t zW~L|xxjk`hRG!_x#?;gkBu0yH zT%T=1snOz#hVDuA#4O?N`-~CYgqh8+Kdns5-so=bgwDp!0LLjgxyGElcX~l$?nf9q zHb&E*Y%jx=lh?vNlyDW7&17OVjMzgaWy=O0V67hV~|vJ9-c1Nrna?to2v)=^dB z$LmOLj(qy%;!(x>M>HFqL>3=mjP67k3HRB{tqd#-#JZoO7tYWyHJv+Cq4Z3%axjH5 zp|pu8<~kjJ*@AwBG+%RxHNN})k3mU&haTTbCy}yzWE0wHP}%#T-se9aZgws!*ZuI} z&29go?+b%^C8cE+fXHjDW`UhGD0q|`w5wy|ppj@N zYBA3_?7kD(Mklk%%m}=`P|i0rbI&|ie6MDropp{_@yj0%WhyK6Y(h&aPV{S=Hw`YC2MTXb5A8ZuIf-~ZxLiw4`A1+ClZN^kh`as)qgv@2ceyOqD+L`h>k8su zykF!VHNAL%PTh^v2+iqJJ)a^G#3$-q`+i~SN%%^OlWlu%3$>0}4<@JG<~f&=(xl@})&Mga=hDb)8&)#?4QiD1}E)YGwTY(Pg>i>s%-IP;>?@LUHI+b43PVy@MvyM>vm=y{K` zzn|eh{$}gLbC41NKk6^94_!{Sq88zXjLgs;9h>qqCR-(2MJShvc}>^73Et=z|3F*F z$&)@-LuraJ6`2MdD$)^?eJ;X))|J-n1rzf#Mem&;$y(-+t8b`hWQG#6GU7K{xB3VT zq?a3c1it+6$3-wQJf4%P2t>9mWQDu1gi9%tzaU>LSBWl|i9?*8$$((A&#mtmLk<8h zhEXv`*AIK<45SZE+5c?Z)`w$^X~e4(MF5Tc%4Y)-Jdppa#=S12+Sbtr&f{JOw%51P zvtaserpv$uR%pxnLDslqd(_MB-zTAmSz}Hy<~*6ez0o?}x#D|_m{okUo|*4qeb`-a zt&Lv8D$|VTM{tFsk9e}&+fw+&FrO!*Vz8<6M-Ic+TUI4_48(<|*jf6;JGWS_;wl0^ z%E!Brb4|+MDx)th<*K54qKLuQ4hos4MUz_H<`tDizWZ$*#4NkH)@3zYNODZ%C^a0$ z!|K*{*|;*UbNujQBR^-JPS|_gy~?m(!^5{S6CI*b$H-0-x*nI>hbwk5$^`@lMw|fJ}HhS4e0Q#q@}xYOeYiq!HD;nY29O7kk_9h3UC?!@ejU`^GW4G+A`5*#C=#3C z-zLjh1w9=iwO!p5EV6)F{7BTl#07rKyDkcw3u57Y7$Gi46m=8meaq)5VaADF$mFCO zo(>hrfxX&<3!vP-GH{--Nm~v~5E3TTrBu+N>j&@swP*~p(NU%ujK-tBFWmpVJc1QE zs8UfXdhWieW&T z{GW{4wOt)J-VD@_Sr(dXJJQ|Jxl=ZVnMakO@p&35;IQ_6 zV2EG-=NY3_RP!vNw^y+f(_Vg4gq_YFE-@n1jM);wj}^KeN#Wh{n+7C_8BPKAqdGJ8 z9cgbz{vL<>E+f!Q&&8acj@q$W4Vd06GMk@FvbD-7oR`YWy-YY=!Uw9kMLxOP!JiT(madqmwMTBuH*8P1P%};u)20L3tHXxUJEe$O$}NTW-+f={ORb5*0hGLq z0F0okaT-jtOMrRz<6}e94q`B5K5iGH_3NL%<+*-)P_a^diyt7^C=M>Ynp>c?12ve%rx8?(#kjEl`=E((a}5h5Cj%%xqD2O zh1WVCIhk2xU6>!RH?__+M%SwEwC{jI#%c$9lh%YFX3g_V7SquQk4=lmf(9y@t2-pF z&p!{Zbn}4!BRSuK97_|24gXwU42LWOIth5jKaVP-laelahpC`r*EeYN`cEdp@u(N2 ze;ho;Vy8`DEm4+uRAQp9^K)_P2#`n4yEJvTWRFt($B+2uq2;cSNl?O#)+X>*Cv5ba ziiugEIl*m*q|8JvJU!604&;aW1V#m#6*kR^V@E%}n+_?8QlI-MP{B>G7G7ysoXX zu`0)*qtuo>;5>3K8oX?0&PxXkbYa}i)T-1^0#s z2Z6GP<`yNEB_5TZIZxrdW12B0&&{)=>*&wWNb|fJw~>bJ$Kg&gNuIqJxCo*h)!YHd z5q;pmJXnkGE!^u&aotMxS4|bKqlKMd7G9dI;1aVg_nt9swUWA6yeQ}msWWGG%s(HR zZnTNjY%kI`*p~dbX=Hkm{>E3LB4R&s8F9lw;Xh~mE|BO%xNQ<7Y6}FR1Mdn=ZpCV7 z2F}X}H|EhiexLKJCWJj+UljIA;{E4Bq&dSKV7#KoQ=lC1Vz#nz`wGJSZdt*7KBMj; z`Nb2N=LvI}x?LMjTW|h2P0810h~?LTpGC}4%WP+L8Sqy{8t08}Fqd0CrL7;D(>g81Y$A_4ohfGfF9bd`Z(T@qx z_y?VNkLNC2bX!?Wy^-_J8sR2+d05QT)H=Se+^7HZ+%^;BDGfBpLSb8<$FixZ#OSQD@{)s16 z!=4Va-Eu^{{Wb)M$mjz>BydCCUrbTlv=t%#t}J%#IbvzIo48tzD>5*NTVk+x%^>{mfi<8$kb+fFUnjFO%23AqWgCWn7k4$`oGSQd1czeoa0wPm13A|TUJpL})Ikw)LVhQ(DSDoi@l^~g*AGXP$y0PqrcJ1Xcg z@~4>-n&zDruup|wnx3*R!JGPN2qslGu5&RaiPfNNbM3h;q_W5*&C%}^HEhf33_Zrh z@C4J&xVJq%>SyObM%^}lVTyCv(^>#oM|7=Eh!P0P$+II*8++=1+3=0(K$;M<(*Ipf zU3`j6er;LYuu*VSqLhV$>c+8iCYP5-m*`q%<4v`u@i8BM4<#@83z55-3*O}f*S6^4 z;tl6P4^0=0jDEQ3)ScKN7-@2K%OV5&W^X2hC_>Cq1r6`VI?qw*h?z{-KL`MJ(YGYo zZZJ%QtznNY!CzwZ@Jzz-`0g`v&rn6mF~DeQ^k9V$7y@aP)%*QeEc&B%f#~#H=&02) z2#|2&Cr;vw5FF2SkEj{{p)D*mR|UNW?)|pswE*z;4FhJL5Cq$`t)iRch%W7?QaZ10 zx2J>E$S!5_i+=9{e{?)|$YX+adtYFO+q)GPp4Sq1(Biw8-8}_sJsZ?>ayuwx(=101 zS;&g#wEwGtn~f$CM@)z!7dL!G=dCe+jjse9)PP_*5&7zy_atD&IiZt`CL6Y|iDpysq($*=5a_q;E1HA_*jLi+;nh&nSlz?! z9e><*C}`1bFTALo_s2XU1*IxNxtCLNoDd$QPk(jYXSKVcJeyW$-W9mWcKCmJ`t>@1 z>k#MAH+*ort&-{v#tB`ax1-k{A$%-X2|upSRm1k)K}Xr0)l-68%h(K#jImCbFJEFV zv(J|y&TyJ5=WD-_OJ5#gslkyE*U)k3Kl3e@?3MroY5w-i})3H7bXezp} zPJ3W8?c6r5FZd$xIPds&?WL1Q@!gZ!(XSgFrjLHOF+(_}>&L5I88XQjvCIWtyYZDw zPs!)SJt1Rov&Mkd`GY3V^#_#<=|kB-;Ga&@bZ$`gfK7sBAKZ{)P~0Lvg}LB7FL_Tm z33h8|?>{B%jn{4l-ykwv{2T2xN*g4!i+VN&J>%%|vXzB#^aqD!`iGQqo28ASLpw!9 z@q}xYPsCFchhI3=o9Tba3bUjA;(sX3OEKlJuojKEF6X}1tZ7dFy0@ggeAXs#pDin? z8}sY)i-fX2#gjM9A*)&;3!hqFikP^}AqSRfA0P+P99x}4l0&BE|1dwMh`W(v8g3Y1 z%A{sS(RLA{kE3^(SkzOMCPWQ#Kw8rVbTiux?U$+&<~qZTgDAZe$s8q8HMw7fe~%_~ z@TZMZUD887kEcovDqf#QU^>?SGK?YXpE`pYPpKd2k|24k7&!jdAxZ~5?6oh`AclDq z>Y5mZ>~w6EXfr0*3;3ln9L~Umzw-N`DDLq?*QicgPr z#w;ZGOD9NQPruB-JSf5ZqHsh`8dUO8&y^>7v<%=_ACC*kJv!Sgnn_-i@$TCb;Vq?B zGktinz8Y&(FjH(80$dxL16qTT=;vVda>*-ZN=Ow1Y2tvn!-V>DPwRTqbU;*7W(&%m zDnZO{=`HpXJ(Rzx9RzBsahL&Ikd%*nC5lIjE?g4_tHpZ0)-Bwnf{=5SE73bMAr$Mp zbp)umFZMlmha_M7YTx^C1~))*5$M9kd8Q6qn$HSQ1|wpWkqJS`kC(X0z* z=7kSxey-|Gli&10%?9{$9olmcM`dtkK|c#+`y)7 zgCJ_UrC0g-Eq`7*Zmpn}0Gem6@lw6bDpU^ojb)B`lZkgUw$t-ti!qnMZjx#RO&)zK z!oO>um^Gy%#v^i0X+IHVoaM06IDT^%b~Y6TRiV5a{3$}0SzAf?>lXCM?7hX9Q)T%( zLf7P;#y+ED6eQ&e{B&hu9X%^-RlBXnC_`(=#g~GJYS+>EpC;uskhV}hxz9OtG}t)u z&(a1?;V(XsK1HiHM1Ks&T>8naZTqF+D3PV{N(QEHEebPE*aCLYn>^<>v>=fgVO@|d za8=daXAcrvBS$=|%Y>;Li^EH&cz}BECWzd4w>89ORN$TSev;C?&)iDyRC+Cr2T>5j z!DLAVAxIIy4 z!$02k=}9`-x^1OK@PpDI0u?r22O%ur%0|l zZL> zn>TWww{ecRuc5dWGS?B>?HE3g_+UqKb^5$->Itm(N4%)PKM&lO;8f z-WfH%VdhWV8((_$W(8`vjV1j)2-OXI*2i?1zT`c2`BT%=X`rX%jhtNW$&W(k&V4gs z9fLEd^_oPFI4#a>km7yd%;@50rx=~%=j=wpPG}kp!4%fV6>)QytlT4RbeX|}lMy<_ zyC34G`=`4I5z*b}o6VniG`nl?dN`uV2$Lo~08{652f4{s`BU@SC>nPeypp z7Y;7;?84`rs$!^PRXakTg&5U@IRsL&RI$-l^@t4oj*5iid`xNxiF&IYwZYd^3lf(6 z9K3J}mf#pfo5x6uVaJ`8y!Y3B;Xd~y>|#Chi=J|@&vp{Je-HhciRc2wNyPnH4hgpS z1DHg~2BtndU!i8=p<4g?)4$>T=iXrebRx+owEMx^?pDDoBRIVxzd*7_s$qGLzrIq+ znsOT*Zcp=fx^x{~M75|>uu$Zj)l$?Xo?-;;EXi;BU0h^Gei|e$k5rpc$wA9Pi`Xc< z1CtAIJAL?6tAEDEHm^bO(Kt#e`<1_A*}=v6G7{zx`_RQp;p4DTU#bTTC=^ znOtf@8p+*U=5+cLbD87P+{Vf^61j)5I*g8Cg~)X^mo?1JhB0&by>-s-`_Jd}{qy_R z*bW<>Vx9}EM+@Lp7NB0#7EAqOMM}=9}~9NyIl^j zPA%KT*>~!+$@$)_5~GMB7q7jROvR~R4OVhaUMUGR&)I(e+|*r1z}jZe_!Io^8&G%1FtnaXH7n@#xuZP zZ?Ud^xZD}tTndlL-@3m13Sh8+=R#RI@qcCy9HJ9H=SM!R$IQ5r_N^(ncx$Yd*@n1( zQToWnGW$WpPr~?x>Ea~7Br7(Lzv!1a>&)*MPu3ZtFh33knA zyuXOPJ|%PlEwJ7>=$oznQGMWk;s|rj9~|VNqGGGuoawT@XL#Srvfg#G%g;vfm#x$M z@geh6>c3I`wCSX|oQ>wV|7~cfVN2oO&wgK@h1b`YuWvkY_nQ2d@q3Y4sX-X`P!rdz7LVjc>#aRxZuXyl=)fhQw$x?CeXZ(*sw1S*T9;AQ4)hSBD-C3TpwM zq=Fi64CE7*BD6g&B`vm@R|jwlZcFTr#z8}*j^N74H99xJ2pibsugBstH${6|D|wBu@TrB*Ks?$*ymio?7&N8%3^SSxBn zT&PP=BZ=5xUP@#AiT2R|8=BK#SE?eNHJeeH}MF zN0K|Po%0r+u7PP!#MUWQ_czepI&^6672kWa8t(VKHBcWs$dPiNKjr(=DsvbYxxA=@ zR6p2{0*mPhA+RN&>7926_d6SAZR0dH!I#Lo8x}4iT>0*03r>=E{w=-?dR*Et31V_Y?}q&zu{E4E4QSoz9n9N0qUwkANgb(Z}3gT{lsen0kK{9Pldo{(ksm%EiIG9%XcGJeFTlw`4i(y8c6GlTip){h)g-Fe~w_Q~BU);Hfkbj*z} zTxCr}V40F#IS4*#l_2NRGa8gbwKij3_fMG6mL=f>L{;%WnN;3n4&4{xQ8#r%)^9wD z-fRD8D_?w*rb;>!^F=-RQ(-AWyTn15ZgtS+2o}M+_v4WznMuvmOVmqAnD)-JJ(%_f z-*NVtM-9w7DQQ)0QeTUwqHj_f?l1TprF*$=FUuLPnSJ?C%JjHrY8bl>y=Ovt=&m!X zMzGR?dzdVubj!cfV?MMwyI=umxfcdw@}9mR85Jr6Hso{ITw7+tDVq(1ryC(K_Lr`Y zuD*lPLmus~7ViV3gxn6~w)Kc0q=S!MyvXotaB|MDB~LsL@tvP}(j;vZ_&d5u}!X;}*98Dz&XptzJHLBWkI4 zDg}6bLTq=y?4X&|Ruzb6ms!Kic!~+TiT@c*mDS}dJqLRPEJf*C_7A>3yaM)GNvhYB%qKd77k0tG_ZBmV z9+Kae#LOD8Ifd?FU!YD#Pp)L*PQ+oT*bv#&me5u5;aECZ7X(K z6*X{D+fw`PLzQLb9F4h$4urKnb8w_q*#{| z&>E+WZzvOMPt#L4aJRHwa;S%~mEmRY&nlksyK7KS+-WX|QmDIot97{s%R5Y4Tb08R za@j`=!hh=uS}mF__!QMNSRWq%gzCPQ$g6fRRheM0eE|ztkGGx>!wmV!3%4aj*a56U zeTX$?RBua-(eQjkz*r&f)2kH-yz+t8C6z@!uGs(g#qRTj7d4u zFLZkREFW?>)h)LJFUT{n_$GN})=`6#sF&Eyc->Z4$pSZYx!gvlWOy9gse(epvIaX& zLhy$e)0T3*+6$c?BMBC?F1SeEt+yUdGR_;VWKmx?{2^?LcWO_Vx{-HEO47Ch;*G}A z%(%V6OP>)GcPP+e&>p#F5O**s=aiO^%ok@*Hh6uD3}UsoBB^*i}(Ub>Hh<5Bodf&|Vlcg=hgskz%KkF(qUS zCBFX1BYfEO=<3oTTDwRvJ0g;$JQR6nzdoM(uGl&|hc=5D1#XYz>`=MviV9ghDT%2+ zq`diqhG&a-4b>M&D3v{z%iNKaiDG$P=TQy)Kc}rFObVQmat=%kl>1S11TM8{PM85Q zd_cta8Av&A3e&e!aG!fsyFj4}m!4aoV4(1V+HN5lsr||Af1Yo9xmp+4mgr&BHygK` z(s{`l>c>bmgu;NHG0e@?ak)TY*#SCZQzT|r_Q`wZhpqhwt$1ip-hZU3{n{*#(M72F zFu_cF<}qb@{F=xRuCh}#MnfShypD8Al%ogT(h|hyr+C1tpcS)Jrs)33X3$PSYu;KK zfoYHXu<=TKzc18^VyjbaJa+)eY4bUVnvCteAV5oiKvPz^G`X=PG{TWDktH%onYr!z ze6m%i^d*-|$pjBHVIg*+EkKLz=ygXoZ%RR}jt}+kMtva2-Hh{R>te+DP>ZX)0|d^; zpNFH==5N5F4phg~Y~q1-5(_Tr(e_31+N5+f4_t02+s|sI-SMt98}hjD{Aope7v z-EuVDq{M|6PA`I1W8cG~_*Bs}OedK9W{S?~Q;O?tQE96gtp_sj7 zXBM6;hqN~b3;q3nt$M4iRS!!N8mGx(%##`lA-Cgj5xzw*^ex0+bzRe7<9sjlLL-k=j+_JX$;|F%q49NrkxYbVN2|wwe78e`%kpT z{nnYP((U9~dDECS&%NA~aLx1EALF*s4g7|KeOl^DPhUz#7@0OvYC&X5xlp-i6;mDA zk3aH&^8Ni`PmGwE?g$%-#L!k9IfL`-afgt3uNH=m6&1?0I^`y3(5HS4j+vHo)ooIO z_s>go3x|9#LuBa5;2>TyG#mk9iZ{dDmqRTONj95GeRas^mw^S)BwtcMA-_MJZHc2Q z3wAp2}q2h@)%08FScwhO8q zOOIQ8i>iyAvc#06VaoIXGL47R9V67iVF7X9R0du4+7@%|_K3`b%r_3iYYkHt!!k$= zIwQu_Fu(7jX_{iE#{)-Q$j-fXBOxTP6~}BN4U-q_UpPuFZ_2AMtt8!w&PwjXZ*#fG zj*yl~WSSFO@1$ocVKj8I#;QV3$|KS7uxBy%yBUdz_aw_9)D<>#Gm^Hr5U8iIVLZgm z-(hVP=)|9WII%jppbDf=|3Ss~Rfwznd6$JM!}>?L^`&jSX8Dr(Zh&Bp)=K|c%D)Us zar;z^8~a1J)XK_`BMIl zGN4yVNzyxJh6{OXqvbmIESPq+#&q{&4@}oj>}GMMXx1B(ESmQ&ih$Z6eAs zC&I|i+zF9!HyT>CPt%gjtF%sqouC`dxQGlZtos2V5se}G7^-m z__bVMA1}h{QY#XfX-t?j+F?~rJCnrgq!S-);UbQQYh0cY*=Vxb%(Aoq4YlN#fl~`fQL5mKY-98%FHVTF{bwkA3zaIYKMOgc6g`%9 zCioVU>et2g?+`)msUfYmm9*Y<{0oqC9 z+TtZpueRNCf;{RP8{rx&Q*8%~N^3(K3)?wpr6b@yR)2ffm!2s}%F1oQ2aNbYP^K&0 z4|esM?ad-TT=s>fDKy@_)Oe#gM~hjoJebba+2a`hyp#uE*0SZv-FhdD_e%Xguj$= ztW9OXCF&d=6sF!yB>;i{mM@Ev-h~m9_q2aQh|}I;=7>LvnOpw75w-xq{5#>>!4xRbMI^2@ zi@a1wSton+;vkiywsUT^>sbB2hwlSz7;{*tuvLpwG+1F+wxJyP6SE!j`+)80U_4~x zvf{7xZtwnOoDBUB0@RUPqzuBfOE$oV_W=Zqp2BscWJUaLc29&!97G9m*Jh^D{#xbn zjn#S?_0KiGq8=5=AbWBsd26-_2e&0avx9;ZEC1a?!*)b(ew*4`@LFGJ;2)jDy8Z=u z=ZseeXvB9r;Ib*SR!lZ9^+?f)F>Z#( zE~}|a4$!lp@ij`bg_94|4izSmJYjP0X%Vg>ZybLX3pViIvKBEI^R(^kZQ)%TKTQ4V z(T&(Yf(Kf-ci(^<95WEtts-K05LSS_NFa=QKFpk0D0gWrP@y`<;qnaW#M!Ey;eK*hY`I+x|HPt7)scbW^^orc;P=r-2p z=FZgN69Dx$O@Yrc*;t@Od~GC1vD1B>0LSSan+q<>!?WLQZzx`pY z#~wN<(1bRajGa5Zf!-xQo5-~_6fw{+g63Yg^Jm-wK`BTA$Wv9k##lbt`GclapqM+O^>h5Q@GiHasqz|MJ8m+_%4h)Lbw9B`qKx~_%-BJO4B!p=gH z>sV6}HdHp%>#nz@DqqLx+#EZ)eus|mlEU|Wpg0Dv?j>HLG4nmY4O8UuO)0xAoHkg z0QW@ab^(^mShk|1GL$B^m*<3JYMzSX)aWc#X1l-$a;NW%1V%=cU5Y#hrmK1V&4tvy z{-f%Q*IQY?G3*%%M~3!L!-9EYM^j9;tV(-4cC8JtvPg)@iQP_mQFFnmeTH!njtKd| z3d#{3yyvhdXh}#eiB7t+#kx;PPzFq}b!~hW-uP?TvTCo9FmVgApP~vR7 zm#P#;(J)@>T_~0nov{89cmaG)Ddrd<(%mZbS&}Zfv<|*&M4jlVezics&X;!5xDwpA z_4rRqma*~SzickuGd8e>Eg~?CRXYQp%>Vo%EQnImTJxa^RBRBtb=)|O8$AH8GQSV|i7JB9Fdk%)^9 ztZp$-Q||cH7bV<^=O+Jw>TQ)PLBjUdS$7l6_IO6UVva$`A8CsOqoRWQ)BApxq}LOu zWO@oX4kE`vZ{XuY_pWkY)57An;>Pl!8sP+(Y7w2KYw zCwUP>COu(gzH@}$CAXss+cZ8t)->&LmyPd$o%4^@L;4(GGGq2-k>f2i@#N)$wfu5u zys_sVerEbGArlqnHBpc_V7_1`x&sx?>4o0)Ku>PDcUv^|I(;K7-xOwH<}#k2*zjpP z>sr_Sc@5T%>u8QEe4_nKpQ*WU3ASsAXH)Vtij9sf%+;11InA)@iVc`#-%z8ry~ToB z^lQ;X`T!;*u}>SVLhILXCBxNF?3aLXv|qrFaV9Y4!D%DYehk-O(XdNRkS=7a2dRXGNDne2f5H%)-Uj^)yS+LIjEWpjXO zT}ib{hN{tp=XsPw$8we2cPknrGMUQ2?Yh(q2oSLYx(4NyV<&{r$@lSGZ&kblO zc=IZEOjs?;8QZA-!XE}rkM5;BTL~0ye0Sn~eUjIw6tFP@pAF;fV!wiR0P9H@y1<@V z4%SOBZ|YXa2qA0#2wz6wFB}2TeL8V2XZ*$cr$MabzQB@#YK#%gSIBKG{=)4t=T8OS zVg#I=YO6XM5Af}X6^{}^IeV%Zs9g<cDx=~-# za!v+vmx(t+Dwul}2f#GKv~2vrwtfm9Nre$sdWfX$)`D6hl^PYpEXf0xm!VR$Ih^2BeL# z+~fHHDg}uP!)+wmdKO3Q%}hx@Ob}p3fq(&eU;-~T^51xKf%k6Om#v_C^4VI0XaV}& z7VhSL_Z+?wzaU|2Y6S>>v`|9bYQRzmI9$q>V8)z#x8dx*J+8@>x zka>r+Lu|Z>|Mc*1ZCV8vAP|N}@=MWq`-a{72W6)2Iqp5tvrZaopIXpJVj1{T9jl=2 zw8-ptV%J(YqwEqs`duUUwfnH=Wx2Q2*9=&qWrH4l(rv1aE&~&`H*ZmwVs&=RKBU+6#8$Uo7r6Wsgqigg*_TVFq$!)kL$OndS;A<g0J<0@s7tU=Jk@^=I-l=P;Q zoRK(rFTbd`+y!(2%YDE;Uj%SrTF$t%E!LiYRFLIMz~ele0(oY(ic?Rr9`bD!M^1IcjHaLLF;;OvKQ4F4R- zG!xC{dd1~J!=<>rmmcGc>> zsS#q&vLtS}f_C0-kgd^Ut4wX7~SJNB%P;|5aN5GbH~E2}rvC({yF} zw*c(_c9mR4!Eg*g1(GZD7C$JBeUn8F5-$cVmqZlo*Q25<|NV2#WNf_}>C6QUk`K;h zSkM*Umt#0~w3*k`6c=0ej*v38lr)#^`KLx!Q3oX@jL#ePn#WLb-3ss@C!>sw2R=v6 z_3QVUm?=`)d0~Br(J=H&W;Zu=AKt;(__lbim1Qljv_?APEx{f{x_Y2sa!v1Rz2n_0 zEW9E=eh%u4aD^*ZnR5C!BcpUhb*a5&E`Ii3Zf|yVk!j)5dOt%P3IuxN83RKbm%aZJ z`{r=&O2nrRigzp~>KaZe`oD|W>Nj4Munaf#6Ir5}Grq;MeY)9h8lY!=H)~FKCb(m^ z_o;e?C3B4(?X*e!{n;n2eNnWKO*qmv0;mXS1zuBKFD$#QpJjHnYh5Y;@ zPV&}^mQ19rO>h@y4OEqt3&~+qW8AuZQMF5a(lZ)OfAgX)w>07;{{a}*+!YpsCZ^D8 zHWX7GS_f&%&bp%BBs;v4vNATns!5c}{UzIQWHl7|XJ_?65j!4Ux3TwU;z|VM8*1ZP zzRYzoV3_}UTrvOirfl%^XNxaO>dOmP!Hw#F{l;T(nfYHoe)H&yqvFf6rv>0H>^~nr zLB4#{mnSFxd26sM{}~Xl82(uyTRY%RZ D{>09t literal 0 HcmV?d00001 diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json new file mode 100644 index 00000000..56974dfe --- /dev/null +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -0,0 +1,883 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "2649928843619125724" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "defaultValue": "test", + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + }, + "maxLength": 64, + "minLength": 1 + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Primary location for all resources" + }, + "minLength": 1 + }, + "openAiAccountName": { + "type": "string", + "defaultValue": "" + }, + "storageAccountName": { + "type": "string", + "defaultValue": "" + }, + "searchServicesName": { + "type": "string", + "defaultValue": "" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "openai", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('openAiAccountName'))), createObject('value', parameters('openAiAccountName')), createObject('value', format('{0}{1}', variables('abbrs').cognitiveServicesAccounts, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "deployments": { + "value": [ + { + "name": "davinci-instruct", + "model": { + "format": "OpenAI", + "name": "text-davinci-001", + "version": "1" + }, + "scaleSettings": { + "scaleType": "Standard" + } + }, + { + "name": "text-search-curie-doc-001", + "model": { + "format": "OpenAI", + "name": "text-search-curie-doc-001", + "version": "1" + }, + "scaleSettings": { + "scaleType": "Standard" + } + }, + { + "name": "text-search-curie-query-001", + "model": { + "format": "OpenAI", + "name": "text-search-curie-query-001", + "version": "1" + }, + "scaleSettings": { + "scaleType": "Standard" + } + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "9849219586701655621" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "deployments": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2022-10-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "OpenAI", + "properties": { + "customSubDomainName": "[parameters('name')]", + "publicNetworkAccess": "Enabled" + }, + "sku": { + "name": "S0" + } + }, + { + "copy": { + "name": "deployment", + "count": "[length(parameters('deployments'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", + "properties": { + "model": "[parameters('deployments')[copyIndex()].model]", + "scaleSettings": "[parameters('deployments')[copyIndex()].scaleSettings]" + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + ] + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2022-10-01').endpoint]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-services", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('searchServicesName'))), createObject('value', parameters('searchServicesName')), createObject('value', format('{0}{1}', variables('abbrs').searchSearchServices, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "3155545119578184695" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Search/searchServices", + "apiVersion": "2021-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "authOptions": { + "apiKeyOnly": {} + }, + "disableLocalAuth": false, + "disabledDataExfiltrationOptions": [], + "encryptionWithCmk": { + "enforcement": "Unspecified" + }, + "hostingMode": "default", + "networkRuleSet": { + "bypass": "None", + "ipRules": [] + }, + "partitionCount": 1, + "publicNetworkAccess": "Enabled", + "replicaCount": 1, + "semanticSearch": "disabled" + }, + "sku": { + "name": "standard" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" + }, + "endpoint": { + "type": "string", + "value": "[format('https://{0}.search.windows.net/', parameters('name'))]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('storageAccountName'))), createObject('value', parameters('storageAccountName')), createObject('value', format('{0}{1}', variables('abbrs').storageStorageAccounts, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "containers": { + "value": [ + { + "name": "openaiblob" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "5165795915910021957" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Hot", + "Cool", + "Premium" + ] + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": true + }, + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false + }, + "dnsEndpointType": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "AzureDnsZone", + "Standard" + ] + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2" + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2" + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ] + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Standard_LRS" + } + }, + "containers": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [ + { + "copy": { + "name": "container", + "count": "[length(parameters('containers'))]" + }, + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}/{2}', parameters('name'), 'default', parameters('containers')[copyIndex()].name)]", + "properties": { + "publicAccess": "[if(contains(parameters('containers')[copyIndex()], 'publicAccess'), parameters('containers')[copyIndex()].publicAccess, 'None')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('name'), 'default')]" + ] + }, + { + "condition": "[not(empty(parameters('containers')))]", + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-05-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "sku": "[parameters('sku')]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "dnsEndpointType": "[parameters('dnsEndpointType')]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "Allow" + }, + "publicNetworkAccess": "[parameters('publicNetworkAccess')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "primaryEndpoints": { + "type": "object", + "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-05-01').primaryEndpoints]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-role", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "8ebe5a00-799e-43f5-93ac-243d3dce84a7" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "13865489589286283520" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-index-data-reader-role", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "1407120a-92aa-4202-b7e9-c0e197c71c8f" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "13865489589286283520" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "search-service-contrib-role", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "7ca78c08-252a-4471-8644-bb5ff32d4ba0" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "13865489589286283520" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "blob-role", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "ba92f5b4-2d11-453d-a403-e96b0029c9fe" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "13865489589286283520" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "openai-role", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "roleDefinitionId": { + "value": "a001fd3d-188f-4b5d-821b-7da978bf7442" + }, + "principalType": { + "value": "User" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.16.2.56959", + "templateHash": "13865489589286283520" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + } + ], + "outputs": { + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_STORAGE_ACCOUNT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" + }, + "OPENAI_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'openai'), '2022-09-01').outputs.endpoint.value]" + }, + "SEARCH_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'search-services'), '2022-09-01').outputs.endpoint.value]" + } + } +} \ No newline at end of file diff --git a/Environments/OpenAISummarization/core/ai/openai-account.bicep b/Environments/OpenAISummarization/core/ai/openai-account.bicep new file mode 100644 index 00000000..45cba57b --- /dev/null +++ b/Environments/OpenAISummarization/core/ai/openai-account.bicep @@ -0,0 +1,32 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param deployments array = [] + +resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { + name: name + location: location + tags: tags + kind: 'OpenAI' + properties: { + customSubDomainName: name + publicNetworkAccess: 'Enabled' + } + sku: { + name: 'S0' + } +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2022-10-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + scaleSettings: deployment.scaleSettings + } +}] + +output id string = account.id +output endpoint string = account.properties.endpoint diff --git a/Environments/OpenAISummarization/core/search/search-services.bicep b/Environments/OpenAISummarization/core/search/search-services.bicep new file mode 100644 index 00000000..c713333c --- /dev/null +++ b/Environments/OpenAISummarization/core/search/search-services.bicep @@ -0,0 +1,35 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + authOptions: { + apiKeyOnly: { + } + } + disableLocalAuth: false + disabledDataExfiltrationOptions: [] + encryptionWithCmk: { + enforcement: 'Unspecified' + } + hostingMode: 'default' + networkRuleSet: { + bypass: 'None' + ipRules: [] + } + partitionCount: 1 + publicNetworkAccess: 'Enabled' + replicaCount: 1 + semanticSearch: 'disabled' + } + sku: { + name: 'standard' + } +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' diff --git a/Environments/OpenAISummarization/core/security/role.bicep b/Environments/OpenAISummarization/core/security/role.bicep new file mode 100644 index 00000000..d0737c5c --- /dev/null +++ b/Environments/OpenAISummarization/core/security/role.bicep @@ -0,0 +1,12 @@ +param principalId string +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/OpenAISummarization/core/storage/storage-account.bicep b/Environments/OpenAISummarization/core/storage/storage-account.bicep new file mode 100644 index 00000000..5f74102b --- /dev/null +++ b/Environments/OpenAISummarization/core/storage/storage-account.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ 'Hot', 'Cool', 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = false +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param defaultToOAuthAuthentication bool = false +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Disabled' +param sku object = { name: 'Standard_LRS' } + +param containers array = [] + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Allow' + } + publicNetworkAccess: publicNetworkAccess + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/OpenAISummarization/main.bicep b/Environments/OpenAISummarization/main.bicep new file mode 100644 index 00000000..4b7ded2e --- /dev/null +++ b/Environments/OpenAISummarization/main.bicep @@ -0,0 +1,141 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string = 'test' + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +param openAiAccountName string = '' +param storageAccountName string = '' +param searchServicesName string = '' + +@description('Id of the user or app to assign application roles') +param principalId string + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) +var tags = { 'azd-env-name': environmentName } + +module openAiAccount 'core/ai/openai-account.bicep' = { + name: 'openai' + params: { + name: !empty(openAiAccountName) ? openAiAccountName : '${abbrs.cognitiveServicesAccounts}${resourceToken}' + location: location + tags: tags + deployments: [ + { + name: 'davinci-instruct' + model: { + format: 'OpenAI' + name: 'text-davinci-001' + version: '1' + } + scaleSettings: { + scaleType: 'Standard' + } + } + { + name: 'text-search-curie-doc-001' + model: { + format: 'OpenAI' + name: 'text-search-curie-doc-001' + version: '1' + } + scaleSettings: { + scaleType: 'Standard' + } + } + { + name: 'text-search-curie-query-001' + model: { + format: 'OpenAI' + name: 'text-search-curie-query-001' + version: '1' + } + scaleSettings: { + scaleType: 'Standard' + } + } + ] + } +} + +module searchServices 'core/search/search-services.bicep' = { + name: 'search-services' + params: { + name: !empty(searchServicesName) ? searchServicesName : '${abbrs.searchSearchServices}${resourceToken}' + location: location + tags: tags + } +} + +// Backing storage for Azure functions backend API +module storage 'core/storage/storage-account.bicep' = { + name: 'storage' + params: { + name: !empty(storageAccountName) ? storageAccountName : '${abbrs.storageStorageAccounts}${resourceToken}' + location: location + tags: tags + publicNetworkAccess: 'Enabled' + containers: [ + { + name: 'openaiblob' + } + ] + } +} + +module searchRole 'core/security/role.bicep' = { + name: 'search-role' + params: { + principalId: principalId + roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' + principalType: 'User' + } +} + +module searchIndexDataReaderRole 'core/security/role.bicep' = { + name: 'search-index-data-reader-role' + params: { + principalId: principalId + roleDefinitionId: '1407120a-92aa-4202-b7e9-c0e197c71c8f' + principalType: 'User' + } +} + +module searchServiceContribRole 'core/security/role.bicep' = { + name: 'search-service-contrib-role' + params: { + principalId: principalId + roleDefinitionId: '7ca78c08-252a-4471-8644-bb5ff32d4ba0' + principalType: 'User' + } +} + + +module blobRole 'core/security/role.bicep' = { + name: 'blob-role' + params: { + principalId: principalId + roleDefinitionId: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' + principalType: 'User' + } +} + +module openAiRole 'core/security/role.bicep' = { + name: 'openai-role' + params: { + principalId: principalId + roleDefinitionId: 'a001fd3d-188f-4b5d-821b-7da978bf7442' + principalType: 'User' + } +} + +// App outputs +output AZURE_LOCATION string = location +//output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_STORAGE_ACCOUNT_NAME string = storage.outputs.name +output OPENAI_ENDPOINT string = openAiAccount.outputs.endpoint +output SEARCH_ENDPOINT string = searchServices.outputs.endpoint diff --git a/Environments/OpenAISummarization/manifest.yaml b/Environments/OpenAISummarization/manifest.yaml new file mode 100644 index 00000000..522212ab --- /dev/null +++ b/Environments/OpenAISummarization/manifest.yaml @@ -0,0 +1,41 @@ +name: summarization-python-openai +version: 1.0.0 +summary: OpenAI summarization demo +description: Deploy OpenAI summarization application +runner: ARM +templatePath: azuredeploy.json + +parameters: + - id: environmentName + name: environmentName + description: 'Name of the Environment' + type: string + required: false + default: 'test' + + - id: principalId + name: principalId + description: 'Id of the user or app to assign application roles' + type: string + required: true + + - id: openAiAccountName + name: openAiAccountName + description: 'Name of Open AI account' + type: string + required: false + default: "" + + - id: storageAccountName + name: storageAccountName + description: 'Name of storage account' + type: string + required: false + default: "" + + - id: searchServicesName + name: searchServicesName + description: 'Name of search service' + type: string + required: false + default: "" \ No newline at end of file From 9ce7f4c7d673981b67112c8a85c7d5759ec01dfb Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 02:52:03 +0000 Subject: [PATCH 017/112] Rebuild ARM templates --- .../OpenAISummarization/azuredeploy.json | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index 56974dfe..72e22a3e 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "2649928843619125724" + "version": "0.17.1.54307", + "templateHash": "17999043670447524747" } }, "parameters": { @@ -249,8 +249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "9849219586701655621" + "version": "0.17.1.54307", + "templateHash": "6037704335748105467" } }, "parameters": { @@ -342,8 +342,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "3155545119578184695" + "version": "0.17.1.54307", + "templateHash": "1483229855564020533" } }, "parameters": { @@ -437,8 +437,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5165795915910021957" + "version": "0.17.1.54307", + "templateHash": "4037976535302248709" } }, "parameters": { @@ -602,8 +602,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13865489589286283520" + "version": "0.17.1.54307", + "templateHash": "3526872952926862635" } }, "parameters": { @@ -659,8 +659,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13865489589286283520" + "version": "0.17.1.54307", + "templateHash": "3526872952926862635" } }, "parameters": { @@ -716,8 +716,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13865489589286283520" + "version": "0.17.1.54307", + "templateHash": "3526872952926862635" } }, "parameters": { @@ -773,8 +773,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13865489589286283520" + "version": "0.17.1.54307", + "templateHash": "3526872952926862635" } }, "parameters": { @@ -830,8 +830,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13865489589286283520" + "version": "0.17.1.54307", + "templateHash": "3526872952926862635" } }, "parameters": { From 501f5cc473afb4aa40c46f9561bf5644259b85bd Mon Sep 17 00:00:00 2001 From: luxu-ms <92287281+luxu-ms@users.noreply.github.com> Date: Fri, 12 May 2023 10:52:31 +0800 Subject: [PATCH 018/112] Update README.md --- Environments/OpenAISummarization/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Environments/OpenAISummarization/README.md b/Environments/OpenAISummarization/README.md index 5c2ed952..af53f4d7 100644 --- a/Environments/OpenAISummarization/README.md +++ b/Environments/OpenAISummarization/README.md @@ -4,4 +4,5 @@ This repository contains a Python Notebook that shows you how easy it is to depl ![Architecture](assets/SummarizationArchitecture.png) >NOTE1: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find "Object ID". ->NOTE1: Model "text-davinci-001", "text-search-curie-doc-001" and "text-search-curie-query-001" in this demo used are only located in South Central US and West Europe. Pleasel provision in the two regions or you can modify the models accordingly. \ No newline at end of file +> +>NOTE2: Model "text-davinci-001", "text-search-curie-doc-001" and "text-search-curie-query-001" in this demo used are only located in South Central US and West Europe. Pleasel provision in the two regions or you can modify the models accordingly. From 8eaa8c7401952c8a4506d44083676ccaf8dbd4f6 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 10:56:31 +0800 Subject: [PATCH 019/112] update readme for OpenAISearch --- Environments/OpenAISearch/README.md | 2 ++ .../assets/OpenAISearchArchitecture.png | Bin 0 -> 81541 bytes 2 files changed, 2 insertions(+) create mode 100644 Environments/OpenAISearch/assets/OpenAISearchArchitecture.png diff --git a/Environments/OpenAISearch/README.md b/Environments/OpenAISearch/README.md index 7330aec3..cef82527 100644 --- a/Environments/OpenAISearch/README.md +++ b/Environments/OpenAISearch/README.md @@ -1,4 +1,6 @@ # OpenAI Search This template helps the developer quickly set up the infrastructure about OpenAI Search with enterprise own data for searching. +![Architecture](assets/OpenAISearchArchitecture.png) + >NOTE: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find "Object ID". \ No newline at end of file diff --git a/Environments/OpenAISearch/assets/OpenAISearchArchitecture.png b/Environments/OpenAISearch/assets/OpenAISearchArchitecture.png new file mode 100644 index 0000000000000000000000000000000000000000..7df8088d039709fb52c9a329dc0d042a28e2234d GIT binary patch literal 81541 zcmcFr^F!3lq)R|bN<_M(JC{yDx=V5a6$Fu3x=U)M7NwSMSZYB!mN@IH z&-0%12b>>RKFjXj_w0OUuDRx#`4Ib3Q~5Ct6%Gmt%41cP=dVyu&|oMi59YBRBL7l0 zyQhHsh3&3l2tYx>>;3)n0POa~8U=*`MfJJdYd_$AK1R05)YU*!My9N{(GwoMIr*Qc zk!vp(D#b`R^jK8T*ZLM0X;HBW(g?IvRInllxM!V%7ENsz(+=<3?auS00lv20)3!^? zCqO&91L=zez}P}g$KlmR1U6#?fjrjl2c!4Z)6&xaK79iD=cN?As5Ri`-eKJIs{1lWD zC>V_!ogHeQ`4*Iy^_X0pPH25rPNo}g0oYw_q-#kbay{wBC0+uF|M*3o%An!fT!RxU zH@A9{_!fou*`&^9ak^$&QIf=vvm|1_&St*RMFu0l1ta7A`@ve9Ve9+5TbatGyQCA= zJGjVUC&G22X+23&^X^x)Y&MkK)GL*QoIG%}q~{9<4VbOW*!7_H#cT1AQ-C>dk%?csc6NT4a-q2P}Ie3MYd7*UK3d#JO`C`%rpwnGb zPz=;ROK*4ED`RpTY^q-^|L>7H0M~+gBCxllpFiniH2MDR9lnhH`Qco{a62C-XPQNC z)S&GBmDbhbVF-KI%_iSw?J#dTDVLt#oYIX%($!INsB_=b-R{TIf`a4w%gr7|9$NPk zg?B8-4~=bR=xC<8F1F@0hVhjP`CadnYP6N>RUYs2@$j4xjSuxi;%d-`9>tqyhb(zR zO>JxCK!je~Kja|dFd7bS?o+Dt``g1%&z;X%0ZYoypMO6Ly$`%P9_IV(w%l2Q-?SXk zW|#1(sAzMLy)c7VQ%o-8cF%mcC|QP+gM-5iD(JQ()v^+fJ95V4wQ>eE&stbmpkjQO zWggP9BQ&r9Z(2V0cln0?e_S3*%pTwZ?Gmyb;YX});dNfF;a*LFLRht=jv_oF>19LO zV|A5jo3qhkX1=|{98L~7Wo}uGqPG4Pjv?qW_jGW0SO`%Azd0GpVpNFwzCT;Hc?6~x z1$SMp#S5FPoJr5rSVe=ULbiW=Ov}%Q>nca`r}clt>$~a>L(}-whBST7x%Z6IcgeqW zwgkQ{tl(!Q__4USuYI{QxUK90EPMBpM4hGElpdz8uKqL~e-0ad zWj6YGUIV6csu;eyx+?JLL>x%g9hQ|A96QqM1v=j&tzDgsGB~?Obls-|fixh27Gypq zcpD==x%3DYWMphi9SDs{S)G4O3eUb>?TyZZV&c*K!l8c2A4W(V_v`nl26_a4 z4$48th1t5#Aqfl#)yaeCAaI#3wHc~}+(#>PLj0zXw|njbCCOC6Lp1TK=rqYGZ)>c; zRPiAO2HDsTUh0RogIW3uqyweEn1RziM*jdgqlT#~oKp~-NNsn{OlS0MZcX3J|1;r4 z4A=djR*2}%jk*yEK*_~9LVSFWD&(yorCNmt!Xn#T6&Lo(W1oo$ zTI;E=BdrK8T**xkzb|VtL)xxK?%&*0j@t>KY<&~{Pn{tezf{92t(t7TZ{4dBLJ$)X zN%hbi^0jOb5%VQ&IJfQ_!65n4a64;jzgPfiYqb(HHuX89OyFP|>wsg>?HM%Bz^8PR zW~S#gq6Gdel4gzWXdb0z#a@z=bBMWk3bjh!@Iv}mH=1p*Zv)UN4`2E;g%F}Sd^K67 zBkd9%9*#NzGj?t1uUl-X$Q|hF?N}VN0M|O~CRW@3WJ>482U|D?^{-;${x^09I3T)y za}4X^=hDc6P~eg*bsRHWBCq#N#Gr<>xqQCr0EcMkD(;)ozQ|e^qhGKg9|@+RaI&gLk{(B*_m;_c-q_p8qjSkGKn=% z#bAD4D9l^E%EWyR7FyPldu7r(R*hVeQ36L#{jDX2do@+eT;gwIo|}j)EHTFX+=_ZkouErGob-{Gdy}9)M&od~ zt83Qhc#s|H?gZ^3KvdgWT?tuhB_}4n=P_>dtN3Nyw+7vPGEsmo3|Is7g&L(xPx1FcNblGF0g#x0`{Z)iOR)u?q}!T`@OGR3_Ksl!pwrt zbOAgB`(L63p3(YYTie)Bg2KJ`rYUhd2C|p|((UB#`4n#L0|NHuW2*nu4n{d%NIr(1 zGnNJEun~O^(b3kIqM9{=9QVVJyDOLmtv9CffTs(7%}y&fMZ_0Z+1)QiBj_l7JeJVa zk^?@*g*>-T!=l3PA3B|91?$Xt)(p~VRoC_d+|8lQ0g_RONOH4ZUol04)YTa>O`rl` z^avNY{%}j<&pJCTkO%>|N}leGCn}lGe($j-h4D=Xym4NNn7$+MLIff3P@>AfO*%XM z+k^k+U}BuKLA1q`S&QfA(l^o`fh|uHKQ=CcYD%ua^r75szK0@*UDvx@vu(a^i2>DY z%$mVSGGPW&9Pn}mx70jl^1i)36JD6j2MHLWa9tg54r|cAPTBMDX&22DRggC56V5bM zY*=KgdT>^Mufk>ricYc;;B8IF1<6=;oCrHjXAk0ekAg$mWX15=h*C_ntS-aypWwT^ zF?q^MhhF!rRE+%(dy0_qj+K_|ZJj=QFTOzb!6(>`yCKf9t&=YZ`(!R@0uEt?2AL+; zx!BReoquT(9`0+o+}08|U#V^BIk;Sq`nn$CD_P+E%M}?Dd9I&E8O6uRn=2;E(YEp4 zT^rqv7-}^YRTYed9ID;SVVvDG7#h>(yB^=|K9G(EBse<>>HtgeVgJI|_}V2=5~p>3_~MeDw)ScntYUpHd%Bxr#;DdD z0%$n5R^)dtc6{WqEPiPGFY9YlRy-#+4`M6@ddBT9wn5vYQ815=js&g06YR`2ln0%R zipnG>#>S>15&p&N*RS&kwj1rKk*t8*xN&3xG#3Hnb9)kZF@kW>rC~>0qa)`A2$C|`|(d-R9<-iCNxBU!_Wb11Vdx!)v)={ zPi1jo$?4<|fD=$%Ho6lV#8A8$35(VB_MZ_s5e5Vk4&I5#}Z)+{(1x84Do@ zxNt?)nYdXQDEql|hmShW8+D)knN@M$FCHv!mv9@u$Rg|7X5sx!Y3b;*)<&IwJxww{ zggY!6v9U3jF8ztjYj1kgdAY$UOZiR6`K+Dod}Y(pu7eXY1#IWXKdX%fV!fXCj60dp zb23-$xS1+DY?y(6AsFxm`mMztZx5D*#fKl;ESoK zxD!et+iZcW-^3&^aqz-BxS>IR+2dHZQ~*AiH3zY4iaeqa_bje3sOf=q{7kvqo`Dp~ zE+@c|es7n_d-m)f1ak0ry`MlSG!R4UtpnsLfjJ36~ zk$v*f?Oa?NvkLL<;bTE)gp-~%LKLyWKz6KOC#!nube2-!rc@)NH{A`ES!I2O5=T~u6}ub zxa=nL4%r0F`!RkfSiGgSoBpgkQhE#Y)kyozlW@g{=1U>EpeA)viByu%`2#5M+6QA=XlE+cbjszLbS)YEp-Ft?Bs>KsYmDyX>c!2-wc{z1QM zDFoqCrv)ZlyITr8JOl1QyY3oBfZ}y#>%o1DBdfaZXH)utkmwL30xV9@FEGzk!NzNY z5k6XZhh6ubH#=IP$NOztxlU5)PdlY(yaZQ~i7#>{ywC6w`{IAO5T^nS6OSVGWZKxx z|2ZBya|B%k`SX?OO5@FPP^Tc6g_SkuS~}qL7Z>C~A8(_n&#aB@J<>o9zV=_?vk$X8 zFf8}8t&#-5Js3CriH9zlA~C%Bnhfnyrv`P$c_YjsUz07g04Kr z{9gkV7R&?T4QzDg>3z4m`d!Bc`@Oxr8iBo0kA>hC6^uRFTI?L%!552mjU5+Dfl3R$ zyOV6IRG7 zC;sXdy8Jm(n>cTlR>%(vBZ|9^1Z@YsY-h2$(H)j7p}uG0tZ3owW0 ze@Oh@C!I7m_olC0?}0$tpiZ@Z-PifZaJdCt?E~AdcgvOPVVj%&jo&0ds#nf;NC+6j zW(Tf#yZU)DAi4_1mh1}Y&CsZP-XtsNeEdr$-A@4q4UX>##R(Ey zO%=Jhn+dcC3R^2hJn;5P2N-(SlElvK3WN3N{5J@7eo>J*PHJdsig;U04Fnprx-jl1 zh*!a>_&MGb($|ay(6Euy1^tpcFYyg`Xx{C0>V~|)XVV1QH(GRfN>3l)_VKQj zb=_S%{Z@H27JoC~I&&+?1$-X1lWqj8p>>@-7w+aUpI7LSZVs14=5fLK8&X>T9yho~P>Q;>Fy+m=A`F+C}69s*(|T2WS-)FBMG<qnzKKWV6|Vl(Z>^%B*p8smp!TuHq5V2U^00ysuzxe^ZiZIzL(FG5 zlgDhH@ew@2eb+HD+sVK;U*L?h?|~FHaQOR93pk0^DWdx15^5G$86_4y(};-34mK%w z!HCY2ic@ElJfkl zWBT0_Xm$wV@?U4It4-F6hMCQeD~q_ueL&1@k?eP-5n^zPYr6ykY|YPgOL^XehEzi! zd7_{JpYh{uUwnZ!0`XhKh3Fw;LM|tSafBgcDu03g{1*3HsmGiOQz-)IWQQPv#gp+I z$NfCF6UQV-{(^gyKw)oi#TZcY`0zFs&=ABtNYmJ>K`pT7UKahHLiMLvz|Ln09$0IM zRK*C2z{YM`aM?>0t$w}1ZF(hTu$G%bfjwQhMb_edMu;ExU8JJs zqo%;V2_BAXk&5l-Jt|R{on;fQMZ~2ROTK(inXSAl*BbNy)5MMO32)9y5A`+Xwap* zmPmkNe__Yt4?NELxYe56GxEd?XcCQ&X~cf)`Q^XOmJV$6#~1Z(Z_3i@b&}j#$kO#g z3XvsAMq{fUZ!fOrcWB%~yFLfx`?Pl?>M-nOTlJuUXNRXbd??WS#E4cHL&NIu)|R!z zH%UNnF+2;@05g{FI$1%^IuGs#sNJ$ptUS^AjV1phT9X7nLyEaMV7Rsr_k1QPX+me* zCvPAbVI|CYnCw&MbYa7ocAi?p1k*&R7CnHrda1jEh&L_3C8iC%QLU)!M66P(v#O)Y z$+Ok16Y6ZKVSa3`r;!KCvD6hYf{V728p4Kn;Tzb?O{ zHKW7$>!f_X;he6T6JNSOdi1<1jfC4v=e<@tm6JEg!kM)_c181Nl9=BxeI3l#Xbi`0 z4I$rlr^-h~`WSl8D003LE+?@LraXPrEuY|gP>pUS+i47@3#@?A{hYBr)#5=w_=#_M z-&kse5~pt9>DhTdfLdFNk=2Y}(}qFc#z~bLOQxk+P;+;Fua^nF_Np@#s#1utvLu{_ z2|>h)qKpw496BQ+29qA66wKCs;Km6p*~Ro46Q0v-cg4W!VkdfmSxknvtAa^C&=QER zqg092eH3hD%WT`x5pLxkuh&z;f9Dhp)Ht#+`y+~VXYo*--%y$Vio>3Az;XJ2o+zBov)+5;C zG|^_rWNfbnX5z^8Y2I(J%L*V?zb0NZ3X^$kaHfU9uOFR;D1?jNi0rm7yk&g$lg@I4 z6w8w|zV$$CnnW2@M8mRVS-HW{iAkhEzBkwuDhswo9asYbAV)7&9>zaiXsL;y$$T;4 zQ|(2Q=~y?0##rGL**pIMN$a*m&Bm~^048}!#5ioiuQztHR{-4}Mwr39f$AA3H;|t2 z%A?ySms0!a0762-f{@*gl^fUH+qa*W`9sdHQr>E=cQ=@4Xa8tePmS+BS9%LYwH${Z z{k+Xqf0pNy{mu+L7IH2$CcS#Y?$Ljmx#c8Db<}oBR)=yz~V}k`G7JRsrH1< znTVK}RYqn>YJcvNfT$=`%36}X^(A;INyMUMJX&C1CqDVMJ1K-QHr>d4UnHN*Dp9q+ zcia5_T7OJNlJI?E$KzY2(5Ee_9m^qp@204siF?Yyr;k6;caKN7;LOQO+tHtWGyq|P zErQ;B?0v_!z~O6zPN0vxho z)Es>VsE?#vi##%Y?JmoJ4K(tAFLS>Q#M66ezHHoaSYzHr+}Ir*Iq#Em@&3b48S)T> zvq$nWchE8r)^{;d740+*m|MoAaa$h7slu>Oybg?6rb);`^$qcEnLe3amWEjyn8F_< zhz|_lm z5|d#E8%$eSl!f7bG^+KWqlq7Bb~Ked84;59D4xL|CaKVzi9YF>n#tPq+*Hq}TjgUmgR;r5k*K)w`H57h=!b;R7%}5Y-=lu=Ktl-1 zT9I4FncfMN>-o9g*Uz{)$u4^zkjE_W7?wd zenUICKWBi0Q-Bd5WnxAc^|2e(2PsfcsZp6YOuhP|H*@V5n$nHL;sXwAVQAoZA;Zb0 zsZJg^u}io;|0Ac!^)1wA#UU((d=WNGt$cBHJm-EsEdF47|DBLjFZAsC*z-t~O_T88 zG_A}nF!7AWbNU3Dd3eFc@LaRtPg&GR3Fx&Ga$pf+V$2BhVB4cM3 zkL}^{(jwPXYAhd@X6*`H<{CP%yeceAwR=Rq=Hi&Nq^ylu}bGpM#6R7KI-xc=f?0Yz3q~>o) z1wP{})YZ65R~Qz!I89dw%oAuiy%Wq0CZI+`@K8qKNJ5QN4$ z_nj*s`-{@q=av8L0^GV;QR-i4vZVBS_^7eXY=LsQKc-vauo zha|(Aea!NR`|7Mvl@2kRXDyE(rZf52qioMNfp*Z?6rCByr%ZMGTTC%O(OrD91ypgl zpuX|CK@>iTk%))K3goFTH7UoeRp(@2Y5S0dy_o-uqVzS zrA;nR4gc8PPj|2?_7H!1jy-hE1WItu3L$MaU<$06RxqC$LXQ_plbju9lVjn^efpR( zGk?vbav-4cd+UBeTV!?Ayx0%F*DP7#XB1WmA-gl*nBXKZ$HYlYvjowlz3YAy>=b_g z@S0vh4GQIbu#b0_Z&DFR%s9r%8v%8H&^);=EyZ2UO`LZ4F}QsI{Xx~318IbEUlOh4 zh+Q(v>t7>3nmwf`tkx7e`1wD6fa3FE_=&A_B*p8TGH=mT!2`z1BzXj%ntKki7VJ53 zg)_{Qjhc2e9FY!v3uLYbBUGf*b432m@h^i3YUq;~0vJ0Pnh9kSH{E|-^bu=4UDJ$g zjPo}=-bTugqKEKi`Ix7nuJA@NWsx_Ny?t*@@%3=AB%9f-V(G2%l)|vucfXb=^y~7t zH)1FukkX6j!~t?8q2qc#K|6vW3MG24PMU3wZDHIy-z3I{#qpgm{jFl7b&P$?OG#HF zo&zM#5R;v3jh@UMkiY`q)xSQOlJ3pZGW+I*^^j+wsiekMBQ+63bBc(|kvIK~S-m}iuV!q)dqoV!OG@Y= zFIJ>eI+eo~cuVktz$~U*X-$}ccwF*-eC44skOfWqYZOQjO;*M#+ta828Jd%TrEd>RyTbIh} z!7#r`i^Wl-WD}e9gpV$;2p{S35kPRCcsmI{_J>%gS35G!nwV{A3rU|OImZ1hMi6+k zbxddwUE}5grR}O3un6yfm3wfKOz{CDi`QSpU8aU-^!r;^u<5+O2xoUy+kwPbjpIi} zOEGE}E#js!WAV3pg3pQ4KFrR}AWfS8QS50z2gZ&l>q>yP((e(h`>{L&gd6xa@b>1q z*-7hHmxwhoJBhvh0S+YX*L-bKZrs)iDi>4sthGxgj#VtsbhjGtP+BoAD&K`b&e>VVA(T0F_lpb6N~nZ*uwv1 zBMwD8sJZWy8AB?j3qsf%TEKO)->6wH^0&~%!q*SxQHh2g{%Av32K<W zbPsf`4QQ__EvFo{_!;#k1S!3aG~V4J@&Z(_KAWL!vsbBFwFsns;Dr)25v0Y$Mq!Zi z9=gAeVl^cmo3a(xC#)PiV@U9DK`#9i`GY=n>6B`gEdBVXpaGPat7FG~%9t1DZ!H)t zib_;2H)FZ>Z7F%|QLqxNI>8$gMtjD$$ccduNEI)zM=4UM_UpRPz}bR(@8c4DW~GE* zCLr5AaIHNBpg#FBsChIQss2yOMY;MJuGA^hVmaprd9*gn3&75AKV0Cnqo=&ZFF-fQ z#hU$QYa^M&_xg8ETBnDODtVF6SjfQR*sXc2#cy#=z5ByroX{Z-O$ykz8Fik zbGd`wp!P#=-)?U3uNJv5v_AtqU_h7S**Gsovz*EyNFo@Y`?G-6$Ie-?Vu^R*Nty}~ zw$$BzX3yA3Gr!`Pbn$&3Pa6m7%N@MwB@Fg7`%*d_u*%2X(n`NZ5B|!!C{vmrE6Kqz zh~=-rUmASutD;A(50Z zqMqfSEaeGt_SjV$nem?NaUv(R)ReP1L{4-AAQ$IHKD42*U%oD9NL@036KnMvPt?&H zH(FBCjQL#~Tj+$qv2Xzd{tP^3UYLLD-MF^^b1oNH-v-g&4ODe{2b*SfA-?T|X&2U% zxk%l*ut%h1M*P|7D2|se}=YAj6Klei}x;_7^5<9SXx4hJ`p(_L2sWb&evq^i)?u%5@i?b)}FhT8_TslKyL^!6M0rw5TR^wFoXB zzxe16Cs~hp%KqoL86#3s2FR9fVQ`_EO$fS2#8c7#zJT29 ztjG9w*2DO?U)%xrf7uoX7$^Hfi;=sR$eY26dH&84$Kz&(`DQn(lh@p4*|sW}SooI} zx+C0+8~(oCLGHr?U^tD_lRWQ?y%v)R3t6yx3%fiOqN7-hy%PTq=&9ghdm2A4uRd4w zm?a$KtjIXMd7%m^&v>f;*_!P23$r5&Li*?#_*HEcvHb3J(ko#4boAd~psZ1>HEMS; z>5sP2w`}2FG-O&AAypaM!<-C(q=I1^--!W$4b=?)gYb(dUl6~zeQ%X*H^0{%ZVXj>W7N93#abK4-&Wh? zl#=GBw041TkdFQ5`lt!A+6B6P24um;+_bVI>Cv{WiX!M2p)lBx5U%HD*OL>#=8h50 zc~L=`tSl7kLiuaobv!P>W;#oesL*s(oE@UYCBb-xfFS2b<3@i^>uGue&9u`?kIR^f zq$jCa%VR=J*RlXlDa!@pAlM}5$Suw}XAPbj;+;@!qJ3dorAjR>zWzm|o|nAi?Li}k z#{qDr+&;1g8>zwn<6+C;_lv5VY)Leko(6-ApBTRs=0e374?y;T_VgeR8G~_I$(Or3 z@kU2GTbe4Phi*>s0BK$0LE@i!*Mu+rF^kZ6f6jH^-6DEee67z#`);y*!Ipc9i8lQK z!H!NN$qSDb-}@&&u#fKWnplVZ4f_da$@E$BKH=D?d3|(t4GTYUPT)>)BaWxUnfT|L z=myzV98ayy^vMBDhycxavKX&Kn`o|J&?avkOWvXuj+gw`E^wurxNqD0cZ23Osxy0S zEEPJ<5gj$Z~)fohpm(F0X*%ccD>_kQY@|i5)*d9=5q_ar^J!v-t!%nI5>dn0Xm~>o3ZFJIzNLB&t$eZNPW7Byc_LnQ z|A2XtJgCqq_GEXBhKtDHZ%|P^DA3C9k!rfE_PTxo8nQHNAjxf@F>)epZtBgw`(EK( z=apv*Xu^qLo9LO(fvdTho&rcjL8EY-#URYwJia%%RN&2tQ& zH#@@F9q9h~QZW+&5tx^&dncRA6LlkKr|W>)&7&5V>XAVahd}BfNu7(h_D8Pl8_6Q9Ea=I;_lT>BfIh;qNBz5z>t71ShSe>;+oEBqIi9X zB9hE1jb^ZK*$i6&=0fCTe=dv9<8)NL!(U)|?SQ{V zGYwxy(5F?4`l=danG#L=mqvImk#^VW^t48(%^0__?kDCGO|N2N2a?*9ws|F|Gy`kN z=(Aq_UEL{u;8(G!i~lo~l2qxJjG&|%U4A-Ga&FgyCj~yWwA)ABnM8EHZ7(l?{3xE<9=h__Qtvv%6!!7X>6W4hqtFFGe)70%0Y2 z(60S9^CWnkKAvr6E4bfCFb(~mKzz93wX@&Bq;-&Az?wWdni*}UP%PT zQ%a0$7|VgAeL$r0Bqj$rwvvA4Ntaf{0m-BNiy21MLOFTSuoo8yR>}B{#z9IY0$F|`9lGqP7{NLthF;WV zC;!GE4+bsIW0P0fX>Ko6wL3}3Hq_)g+ena!zjIg5o#i}R$iA3qI}Fzm)G)>u{-Cve zUzsjsJw@lXC(iisc_2CL9W+a|aEkT#^v2|B3`pbeuGA>S-*L2gw=-LkkFc{H!mwK3 z6urf#7`|d00E%+`t?p!;Jq;_XG~rVmMv_4(%)T1K{HWyk)rlO{X3H|H3I)JI4r(UP z&&~YiL-8l9va;r$CnhBu{&!o(pv6#ah6ToPT%2Q@%_@_t!*KN$E5|5@HksgO_lLk- zMmL_(N&!MSl`lUI`mdq{cN%I93bON<`P%cih-JAl$_mMcGI$>Ir8kib)mKIyZxLk; zYI&$125$jn59{=qbb-cQsVwe)kxC4m<4!(7l73{EE1AOgT#LX-hNm}XKD21W-Y@J) zr^|~dFY{d1nlj!p?!hdnEMxaJ6W&3J%Z7D86k$3Pic_!-|XN3X_L-#Po+YnBjhT|n%ET0-@r z7M^k_M1h7yraeiFGkQJE^fuDgd@%`D%uw7}yFi9b30smU<79N+zim8GQghkWT%Xuf5*^L8$KWR}IA zJQC@ykxtECGU%c+zFD=iqx54*h}rZYr{Lhucv(=|^dK*XQB+GatDw+YwR0GriD!`n zC75MZbWQD)7BshYV$ zNRIHvx#0StI`O9kQ28DPQz>mQnWZ!4G|k-lUWv3gD%QahR`1o~Ya7LJG4eg7O}j+2 z>e*z`VivjG3%#mg7TlU~>kqCUX{8bg&S51fuSG6#1CUWJUEatg>S zy71t?ZV7g54If$1Cjp z@&Z1^TI*0~yikro>!MEr8*cjP?+LAMMPQ^RIo*pa(drCUz*#arh=CMV>8&y2(mO!Oe6Hx&TrHZj*EWmoyfhT*QaYx&; zfGRJL?xWSpJu>{`08_oQx!a|Vh%|(7(yf@9%^e zbu$k1S4fom*^Y_1-I zT?3^<$Wu(fxJq&u)%kIURyVF;>%-#mPEzvu)4o%vHpYNraJ6y*lw=d~iOF)0Gm2l078thHE3urfbS(&gmyfOsG-xniK* z-qXrR&x^?ss!XJ7x)uwc;s3gfK>d4C*ub9l`n8x-qa0>vf?6ff7YEPz$t@0FO|^6< za9icpdpcjAi!}T112UFYv9Zh`5dQ`14+}Zvpz9c|cvfa`K8a?Q;k(ZvGp*bcn5X@| z^owgjeWm`kL0&Gt3FJ^&_HUZiF(;UIwu4;pNIY%dS)FP{l0YX*tsRZB#v;iNi%X&YU;L$atN+7vC$e^R%;Vl6^j=AM$h;B{c7a6 z6RG7$d8EJTrv>8=`tkntKLIv5&NIdgoBam>`~53&OwYu5iR-nx7@ot(3Oze7)7LT)zQkeTnnS!N zscP~T%#r=q%9ra-4%MCIfX%oX4yBYO!O#A<3$t$78JzeK)%3oE-AC|hXFFJ{^@ts- zF6C_b`gdw3NJp}V@fbKn&-uAHBn+?$6uUsHo7fmobtS)>yS}-+d9+i*q`Y)r_42&* zy?-LRq6D^5xE%1pK%4tQeJq1~X*hGOwpW7;Ml1^ZT?;p$w|LE2ul8z`LpDlpilJuA zKiu+&Vd4eD6~Z)s$}hUG$Sc%XML9^p%~i z-bu_QDw28_8Hi3g9t`)>f)EuP?>k}-9{r*UO!6L+k?4Mh_P3o%e zxxk-QlC=bj+h-&R7c$Wd#@Zz$5#35S0OJpBi^jv&>z$}&Pn1UdL%bKRuGL#CP2v^= z8_s2{f?g``nF{k7R#D_}Yqh5{Qsa{a4nWOZ6P-S#pR4o7oE&h)ZJcxL954G)^FiQF zNn=$hNEY{ zOZc{RIz{r5WU|IQ`u$2C^9RUGbz(Yz+De=2?q3u`e}(GlqHr zLK`KjJD%%(*YAJRn#aTw)`8At`o_hsfBFp~qJ#Y+kF`2bJcRXaznnTAg>f*nU17Q|-ZTOqUA z)-85=xz6z%0#<5ansbs!cF-35iUnDU)7iS@$GGoC}bd2XdMOAj65PV5pjzP61(lUm6hdekj&wOpeL0S&` zEm%js_YJ-tA-8?6{hdRU9(ydM9O`;I0INf;-f^cxt(-{u{T2QJmXG*ZoDc+PG*wNB zo$})iD3-^VpAyAm$=fg>G5cif94SX@H02^63*i?@;?ZLCoiaMSl<)$6tUhm3NdZG% z`aJI89^4M}0IPL%C&__3^--$*_sSz_ewS(=HFIAe1FEW!B&RxGdFyjvEZ~e`{CU!2 z9x<}z22c9W6}y2kAw?hVPU08R-_`>~|H)9+>zA}yI*g*4s;TAdEYt$C6_y< zgU1_^WodI6>IHQ<|>v$=tI-{;qS|8bCCd3%;TzL6%NI&i%> zxz}nNj+m>;PjP0GnTGquQ<9P=CVKO5mSx<0!r|bN<=!NXmL}#tlP*YkNeXl0;d@HQ zgAb`BgYis-2Hq3|%PQOn2&y(dZ%tPouk5({TRyjc8R2d31+}klnTX@Q9$drCkT>KKbb**Zv*_kkB4FZ_WZFoa4E4b3N^WsLSc3UChneJ+Xo`W4i zIvd9x2QnDD*9?;&is(JH)JtHT63DHBNlK#SCEY=j0v-KjqLk=Opufi{{_$?J>A1tI zX0O6dj3au*!M@}o7V!xl)MjU5haoqmKr)y@dEXVORr8hh~> z_Eqr8T0bmFQOJF&qgU3c4fMJ+UD@{9|t$^DS!5E9o&h{n^o-NSwWOQJlMVQ*Fx?`J}QrGwX4VJwp?ekNsPGf;3|^twDKzOQsmfQkJCW0!a|mQq32w^FV~(1*i{5%4YqB5 z)tAkhZ*xsIUb=bZ+AwTvo(m^9lst#|!!fgt>Rs z_#JhpV~&>3H6A`!v8`#5ls!SmrDn4ymt_|W02U$JytRpwjm$Dt&mDtP*0P)`0aae= zyeFdSs+oNYaSw5+VaFOsjoi~W8MG0lY5twZv@;`5B9u{7|29%Adb54L9&1cMqs0!( zp?vM8)+6?KAzaGe(lzPEHw3=cK8!p@_t)VI;zIq*kmMM|h5P#5EC0hhOT!z}8tyw0 zl+89v|9j-qy3*$_hC0~S=mOa_AT(5b)cF3!ld6kC7}v0%=K>ZzySe6{ zvC_>1iZaPn0aL6kD~;<+galL}RGAD4)1||yubSFghKaVFm~ax+TB~$j!Kw31riv{> z*fbI1nOa6OWL+F2luM#))-)1tuk)Lvn^0f_fqdBvs*8x)`$%e|b##~L{Wm&@*rq=O+~2EMYn z2qOUqd9rFoUMZWJt=wlsU6m1EOJ`v|fU><4a84N(GH*PEN;`cT7>I1*ron2ehM%Fu zkFs=`HLe!zxz^g}b6;ccQSgRRc&R`r$e2SZsdz$Z$#}r0OYFZ@uFq6!?Isd@TqywM zzG0q;^8S{^`?X`{fg##2;ph7|bk!5@qPI$icHUWT>cpKq{PQ0iQr=p|7r77`9v{I5 z1>IbL>IOtMwa&izWQ=@?OAfUj%TT8__L9p&KG#gr`Bx(~XEoIb1$reRT&~S62bI^E zFIm%wjoNPj&f&3?k>xwy>BKhrCRMTbQSOj2-*|iD552U~QdQ5`oOv^U_pD>NwO#aKsApMFYQiCn66}|U4ng^){m|2!QusG*-S8fjhFR(!s`KJlyrRb(d18e>%3Z< zU}PD?3cSMLR_UBbNX zo@gyy&#=1jIt0i`nr1c^FAoL9jvnJ#`M)uX14{5KiO@WGdbl%f7Wx;)UphA(8**7~ z9*#Lb&1vKd^slRVYc};QKbYEb?>fnx!Xjjo%eeTWqT_)_?<8hlc7-Q12_GF=D)32G zy%*(rBintjb-0NnhjwT3pm5F{K&oc}KJAL@l<8$S-g)37wUd6lRG#E66TKhC1F^4@v zL0=5yZ@zst46hSmzvll*r&Rt>HJ`>|9V1F>YUDA@qEyy<{h7J>GFAy~Gx}St>;1Ge z6JsT0_Th(TuPbcR-rL8&>dx9RHp0Z|qlA#nQ5Dkyw_kZP@*{lNLcmr$x}uohiSwV7 z)^-+YC9Q8iIiRz42AO5F30)$V*5u09+`5^Lhb#YM<*sZ+f_0%j)D7 zrIqAs2?!-I2XIgFRBeU%p@KYXn_D+}&qmd4WL~ll4h*JsMzAkXWN?_swVD)LwZgx} z`m%xGCpMINi!6$#g^whj>h!wx2B(W0#7VCSqa)MyifI+JO&jferH&}??-I)Y`k;Ik zWpee7;@~j`C2U+5^zs|FE<$I2v{~^Jh(Z8_TI97i{hj6(NoMa+nPQ*RAf&9h4yPj zY+Vts5XQu&DfQ70c++fbm7$=VD!awHSXL{i!}J0Uq+M!?2shi`v3h&g8bkx5F|#w1 zRl|iliME3o-lvO+cmj4U?UY3(`38qKYMk4!*zlz4jSk91QV`_=eCpS|$AE7xz1Ip0 zmg)_OJv58WsAxZx>WCsf*bN>WH(M;v;J)JI(Cb>oeyBfGet1$|+$*mObH0X#dG}>^ z!gtLy*3_mEAj{IX0k7x#QmppKX0h6)-fdqoEqYHSy2z-d+mLh%%QEoUTX}z<@ zTgfnJrC-R3VaUag+7i9jJLuoqK>U~?xrTKX$SR|;!dPxcwOhM3v=@l=I`>nXrtr=s z@a!B+xPBby`~Uv4&EdQiIz#jmxZzRi4&JX2fWr3@e|}OJ^BB}XOWNAP)Ufc?jK$tj z*Z)vg2{^|HFV6$VYHTt=`dse8SSld_-LL@7x^k=6yADJCva)E$>p+*Q;Wb_2MuB1l zln3Wmo`GT=hb>PV0sNlIY5_Ye>cm>iVqnuX)~WqA&0lTmUVWE!ztB;Qr4#z zABoyFttmmrkcsb|jB6b~4bh`0bCn*Tef%Esn@IOs@ZoG<{V7b-Pxt7}pzt8fuMHt0 zv0nUsTWatodQ0Rj+gJkvHiqZr2w9qR;pX7w0GS4o`xpg>jx=_5&42T(34XRzR0{h9KXN-wyF9+X_{??3YgKr0e76Wwd`133 zQUu%is&=N)2N2`15Jt!H5u9%`+dP7?9bGzXO`2+LZij{((Lmu2$OjBi<{!=rPWUyXx-6)Tf4k$Y=6LlKVy`@0E8`h9ET$8)L&m3(&c(#A z>pZhj=VoNp&X^@y4$}zumPnVKpXSx3-)vw0;cB~TS9qYVX z&^QBqWGe}Jp3rsu!8V8u#`i8RE%%cDH49uD27Z0$ zTP+Mb?i`3-G%T%?l{{CvhL#0i3l?*F>W*rq(^!$AtmK#^voc`1sgk^?Q*|i(aQfFA z-|e0-ymK5i6~u6Pot?3wW3R-8tE8*-y1V3O{mLQV%1TfcAo}%R3ciNLMO=oT6|FX3 zh^$t59!&FJt2O(bDx~l`jSjY6BK}j8k~pwEZVuyl+n%fFUCOvt<}K@hok1udk=&-o z{dG+N%MT5;e{y)KIk)WZ$SGK?UqvP0w|9hiH_iw{n>kIuv;kyTY_M-)zYyk%n=~QB z#CCn3Ko)9Qu6C7d^+JbBK$@O4a`<#hGo+m*Fy*`;1cWP3TD)OZ-@`PJtekfQ=tZuO z(eFKj-Jz4Uy>|DRWL=cKHl5U?Z~RV-{~U*lMdzqLZ zNcvzV44L&~rCwXRxC#Z41M6}CDFtqPV3P_75nKM-qKITdc@@XTkY%P3Yy+TGbb6OZ zd;RM#xU|IAo5|To6@5c~dKDbkPTYq^d@!!;WoLGliQ}wnfZtDcy~h|Kj_2c+`THA8 z(?MY$uwW+rgu`*Z`$_e(0=?d6<^h!Kxnwk^PKODW|K%v|Z8hbLOw6U6cB!5jbp{@? zZAPg}X4Gu$nDWtX7X>c_{K5_Ra~67@>s)qY1S$DckiCQO4u9L4kxK8rl1RM!*6o|P zHe#w}>2-{+3o+#laQ1i2YNaIEzz@_y4DIk0Uj^40G|^0$;FtUwIs7392>CUKzFW}= z30AGV*##B$KLTAk+|;9k-4|$-;4Q*_{{_{MbIks3b?g!y8Xs-LHC>5t)sPluNK)5G z3hu4kpdO7`v{p0I*2r%z*WIr+tu7xs^zu8ttDf>9gZ6(V+HwS>({>+GlE0w~5m^ z(-;>12i2hbt;v(x#PkH~ign1Q6Z42t)ah1AT;k@HzgpkcQo}?rs5$I3?c2n*KBiUj zj2%i*(>|9Obi=^IN)gaREYH`a617vt$D8GyLSmEXabK<9mA6o%>@~@$5c|Au>cbm? zY~ozMt<~*P`uIiNJ%=(eYOA9*0cVy@GF5uq_223^9;>)dT|*1g2axV8XUlZG%_Upx z(LJkm&_5_UVG>9kq_t_X->uSh9}!xzLIa2w8=ilOoj=)X=T;u^wu&7_cEzI&4dUcr z8A^+#WEej2LjXZ8-$Tf@n=-qv5`1HPl^JKWj}mUIJmzXJlmq8Ym)SEe6{7L*3^G=1 z6`bZ?1o1-=T~@uDgD>W3Ojiu5|7lCU0_uhre+P?vxOx`at&VS94BG9q<%Dd(ty1_* zDwjYc`TB7LTpd`g1X;seQCfW!%EfT*9tUz!@AnOcZ>h8~XtyFY@rYOih7u;9ebyfIVFR+DsEb2v#Ya{#dK0M5M>5qugo|m#`~O7Zs=m_h5u3tM9D7i(b1wrgHEf)%slyxxX=^Z7-=?i8eL( zXLbUGizlh=4LfPUR1q$@<9WUOXj8B6aicix_Cr+2T!ZuUl$}6_D%$h`2I3AAE z7DR-)-}U(sF_ANb3>O`}5nftak|wZ)t}ibZE9(*YLbAJUC}VB z5HD|U;DcEjp2Kh? zkjPTTLgziOcHRc}F-=J!i8m5|d$t6(Fha=>xRvr_u3t~$N=1d0eW<;=KE+8tmT4lJ zgU8oiNgC!X20#Dea}&=fVCkF;7G|6@S8!C!I;dRpiQ5Co6C9kY{56wvl-2hs%|m8e zq1hID(rHyRLWSMl)0;#x%|5g`QbyYbINWFVO};zz%1o%ihdgmNUNT@15q zWr4l=#&ba|M0HF`{tLHXXI*w1bPwu+N`}iMX}V(gPE6VjBl2sw#m9f49eIK1 zi7tZixPShbYmAu(({&$$%U+SvMomL>7m?}M=zTU5;1D1+fhF^rE|xd^A-bWDlq zFk^PMWCm}N{lWE`f`EE4xu-i0aRkb7vNHo!weoNxXM`vieExgP6D1KTV97@S(BV1+ z{nPC4sumB@G4C%ZrQF>E%)QI*EWMJki?DXPKiz&NgFb$>RI6eR3M|l_b$zEuwsiaX zt^wvxGo5P;W;N;4r>>J#ueWNSSrP%ZW54Hl49ahA7lq`_%{1>O8l4wOeU1Cp1;D%g ze+OaXU%O`9KYXZi%jhtm=ZJY5Q39@oH8|c-YQRmJdFKgKZLjg`ESi;a9vNlWvx)2~ zhWgK4qh|BlkJ2bTp&W8AC4RV*t(bSWvcHoBzvWRsOi%hS8!Q&D)=5TWUXXRU(%3(d zLv-T|O6=hFC>}AwwRV-Z@dLg-@&EbyfxN>Yv?0qs?MP1vYu%iFJ$SacFQzh*+Mpn) zE|R8>qf>L1k~N;#1SSu?Af9yBYU}6{Zd^&_D|_(J%b-UP9Zl8~as{SU6Z+ka)W)9c zgR(g4ef`x!-aXRxZHDXCj_T(?GCxdh$bSH;@qhPnLAJqgnqR`cUXj8#G~YFxz6@Pc zN=XsCn(FMlj;&PTcM#UpgeVMRj&u%BYwxCf4D-7zr2m3%k^dppN-+KXYZ0q`F3c0X zD(-H()Tsf2^xC;Rf(ul(6ba3;Dh1q5a+g&4mBE5pYb5fE0 z@9*bfpOdm2>+dd}G_{h`ko zSb=}vyG&xtvZ~5+q>2JEG~ry&`Nv`6IaUT*ei!R*Y`op?*!3>7%B+RbRP66` zfpNJ1{|}SVd@m5zb+8vEUCdfsOxc*qd6ruGc&NU~IdtQm?8tEMcLrGz_g2F#eVg7a z7ydS{cn^%6|1%S6O9y7(zY%5Veiy1)sptP%w7V_#Lj2I}3qx>llROl6gS(vXsf4@i z)p~Sv*6Y2As{|}x^wIu4yb91;%h(@S)r^VTr-qeN*!P360=lW_ZNV3$Po01r9@}O; zGcLOFo_e6>-+eIB2DYf(KbKX*Gc}BIojF@>3q}yZn*njip@!Frl*_kVoxhSO-VG8YAVN$LBm0Q7B>Ptsa z4~=Kpg%sYm~bbYME2OwX?K(#W$Wt<(>W7x&%X zBuFN$2G8{$C89%%bRJiAaYMebk2C%MgT)r{IC?MTc+}^8tRj1I&KvkZ4e$<~t-WV> zy|7y9nkoYeX8`5LuN*%YTN!TWAO7BU`oW((zb|PZ&AP!(z5$N7|9glqnq5(<=qB~m zeDqYO|6o0H3o!nxF9?&{Qh@LH7C@K%Tp4S>*NY^-Y1aTJHU0H>88-PG^H=VDJQaE0 zVIrWFm-h-9DWXtS^5`MIJsL^oqYoYagNfnY;WgrvL<5sy40ro4A~H=!U0*5@qx-`? z>-q8vwvJn8zxTeP3vR%GK#6?Kus+>+*A4EATDybs(zJbM&{BGm|646!3jgjtCgSuI zSgq|np0T%8dMe1aZ@P6-#W{e@ma!rfy+lXppm1vEfH^xT{eS+~^y5*cp>C0Qnrw!n zR1~GWf`*E^zEj&9XVT!Y=HPgbu1M*p>K7F7lpwEW{9d5gH|77q49WhDg3fe#QegQ{ zmXS&HtF-zI2m~NKpR^ouT)^Ex-MLbhl)r`SQzWU|NPmPA`)-Cwt!AtQoxm$ZqR#rl z5Br+T=V-qC^RfbEJafw#=^i*Wtx09PSAdsJES0>$4PQ{?`kj`5oN8;7seL7oca%*x zh^yY$7Z=vm<0>T@M)I?rtAD(W!ujteMSLi|MjeGACOw|~)5g8)3`$Hg(FTpWw!NGc zXnGrl=__^&JoAyYg2zCu<@+Ok1AAM3%*lvTS+a@HSX{<84k@K{>lPiiK1ri9Z-)U&1i zYK|Yr*Uk9~oj3Tp4k>8**UmOG#^lXIqxEmlzdpx3_;G-OZPgu#?-ZA-0vYm8W^ z4m`MVYPYqHPHyr?6;5{n%>=j}lT&qkl;xYO6J}?7y^W}e%*Z4K`8ZT3>+5LQM2sdi zH0W69^jy^w>Gz83IyyA=tl`_71^R3hAsA z5xo=q@r!mIaAO9$O)9vKaP<$AsO1aa0?#OHjrNb$9_u|RKB#3cJa&sGj$Woh59tl+ z*aqnz?cZi8A89Gq!%2K)eo*8y(bvKQ&&{IPE>~Y_vX*>i^mthWfA%8d%h992QMQnw zENA4`H_oDCXQFRFuvAaPmgO-Km`JsOJBAxUD^~q)9K=zwG3|qyALR(&y#C)n}`8 z+}iZQ#75~jriv)ZO@~WWC!97L*Uk-Ne@@R?(J5HFILxRx#Hjp>TKO04V%$0vK5d2R zfRfyiQQ%zx=c`|wqF1spcvXL*IN!b{HR;Kf>^k|FvNwGm*g!34qBo$Vq|}LRX;O;b z5CtuJFFM>8LrSHPXv6yF6)q7UbK|zzJ3Su)Q_!0Pu<0o~Ljop`0^CzTM+`?sMuw9l zX57Yo+_q6N2i-?R-JPK44=Sg&LExc2pT5oTNf)9`#k19^zm7hBmd%;fogumIfJ!5L zDtWf}ahOwl?h=%v%XWXay4$k<)=yl!E6leD4Dxz^_zh}V~{R{IMkG=C^Juf zRbEY?#(57v7&7!7k{_yHt#6o;E+-@N8}L`?RSme&SoS^YyxO3A3}5Z^-2i+q=TLmU z=ACDAciumg{cjfFc{tzCoD!)JnTjAAkQPc)T3(h!uyM!ly^Q%Q?bb zw%t?OO2Y+Y21$88(!RL3hUNAH9(rulgAPkg_`~wkm}LC#6~=81BK5C~dnQiHHno`u zq?fLWhJ3pb#$2~rPcxfJnHzI1JLuvb2-ht{odA({*s(32OpDFOLTFg2iYzu5k6ly!JiStfNf+oN6MN(=G zHs~&=ZNH+4+r@$0B!VI=G!&I9o8M*KeUC!KN3%n2qn@cyjqT6wy;cfEb?Tq8mKpgD zpT`+;rF$@i!gr@>n|)dd^(tf^Ny$LnzCZ})7d6(Tc~^Ok$i7iycZyi*>%OdR9S^vU zfKGs_CV$ck-2~GlXAYqyBhEtAS}=mNo9t080@eFg%I{MLt=MbRZL_Y&rVN+APQfQHG{=DL5#p0f_T}kYA@^N80uE!t!T|;bhN$Ulz%}^0W3!G=E=3xf zL1+{qN93GR8hbBbme8{|Kd)=ULvrMjY)&rbZ&*=W)l&6P<@96=y#`pXyA&d~`DF5j znjVUF!6YxxRd4|1<}A2?V>M?_IjFjf_e~MeYQvu zKK=VGYf|}tS3rMghABgjkc{Z97+O+V&X+0N18mzYcink;a$W{1dg7SyaIET?2_zZt z8*c6%!A_rx(?j#+78kPxzz4q9P9|RLcK=Q7-QD%ss{770uBtFq>f>ozH(s13-Tm!R zH@r#;-^#wRD!<^bk`Iu*q#5ygH_E~{{V0w|i(?i66O9d&%{ z4A>^&058Z(n$XDw@izM7`jb>ecVfQw_w)>iVG_87qLiF5XDit*DkPp?+%~3Ix3pBH z(;t!mr>k^C`74;~1?M=CQ?@V!+?S;xbAhqQoiMxl4y}Rfxxugx>&5?y}?`1Yhu}Rv+3Tws~7Wl>aYU;xjX9}8>oN1|i_p98j2y<%(Pd9LZK7`?2 z`fF)70Dp0MB4XZB%7kOjNz@~F)Cte=q=bGti_!Ji97s@fRoR%CaaGY+y#w3{`vJB? zctr&()nkL0%y$lx)Z);5t{6@U~uR`T4OaoF|XN$hsGrAhWp(2ZRMM*=z7Rac@S%Ni} z&nbt`Juz>W_O#1p28C*$oShX^{*!od-P!Q z7cnKLNN=F%&N6hr?N-=3$%@!Jj&hodbnus5@~bE4&aHbkCU+=4#v4|{vym5`s=+KY z=$)h8r6z9Xzg>rjiJ>1cNU9R~kXrUI$IXZI@ACCYF&j=2n7K_We4_9~MYL0B1f@bF zM44SxY@xLIJgaSO0|G{`CIs2^8(LH#ep8G10>xZAJvFAkdiI_OAbBNZ%44!IIa98R zd^(PQy)W|xoRm6mHEv3z4`=!VHWTE6<;%COJ^H~cOy|>hT-8m)>x-8?$g6lJM80{xPjYO(Op<);5bZap?+|T7`txn4Q5|IQ=HG=crNo4VR(v^Ed$oNufPJq8 zMYeXtNCiEm5vGQCJ_YU5TA}kXY0^KM{TSYP7OX8{UT9!LdwQg)@-<6F`9uu$!iv=L z-E9n;0;zv@xmHOx04r6yel7@*ZI+S0!b=mDH9p=ACNTh!2ZsiT0hEVLk*2oXp$11y z8+IWf1L0h;0RKXFKN{H3C12UBv7w=1>7Q)m z7^&s8^RB8zAm@tu=_BtfId%IPHG0TIAgjqoCi`oR`kOINoSG`v94!ABRaa-|i>_OB1#D_ce1Tn?M zA9nka_??+`qNTb&@_n0k3?8-r+$*g$y)_Mr^qTT=$MNh`)kxhDw%yfqgIG-e`bbU8 zx0V_70{Z!^Rlnk0`{!{whT}i;EAeerU#4we2DVrCbWT^No&ZPsv%kYigX60m;e|}} zO`wVs69J(?u550POeJfTJD0V$^81?#WS@4AgW&W;=Ud6C!U^AlrDnqFlstP)+w4@; zthb}>>@q(d0i02|=^W&w1mv;sHwCD zGx@|R>gMMkwGmLYVp0Xzd`{%m17!01f4{k&&rS&|5g{D+98}=R%!);lrIXriCs%J) zj&E}kcRsp6K5QhJWMILb^dla=cKG>sRy)0CBOqfcX>Ivo$+Q!SM^T3A{-YU|~a$d*mOqt@*jPwXoipc^LpcD0S6M#Z( z(_sgOQrKO_=|QGvZ(KN+T;(F&5#Q;z%RvfNwZ(V4P}RqHkq_%BsQmaOck7AOtJ#1r z6*3J%DtPdH9a6LgH^c_`4MOcr67QD~({GS=68H=A?)qD9p*mm$R zkdBRx^ocQ8tjp2FiIaT7n*6LO-+3v%TTiOqosCRVgFuDQL{)M`FW)&k$cL;8v3vQ? z31n=%Yzc0r`D9FQRlyG<@0Tak^Arl{WbT;ok1Lr>cPRruY-c(7pf&S;oJI!x&rXUO z9{~&y-dP^Y#_(9S5JTP}9$Ua}X>ko;e3-EP0Jzi-3%$&6B_ml_e`}^z{LatuFKit z7fUuf!dPLF@xD&M)1GevZ=9qCzGfq%ti*gB?~F$*7DrVM;W_@R;EQcMB6%pD58mXO zo4i!46zcG2yi^pX{op$vdBK7oa#ZL7&zbJ93XX%2F_z+df2v~%dr<}=9)$9IKq~Mz zpt>*>sCM}0nEK9fRg}pvXZ$`=L22y6pSyQpP30+2GxJ~28|&-eyG<-QRXtC3_KF*I zPM1V{H_KX`N4NUeA1!Yh$H&K+HK0ySszW?>UHu=ptkXmNRCF+v^M($A0$1A7GrRB# zsc->qc!cj1_lf1U_z`VgjrZa2;*i}dRQzQoSn0lL@Ko5Dk2VgB6hbb3JkIk1Qz&}v zw;Yx*H8~Yc9F2t=qNJ=f)#eoIi#Uk%aS1Lmg*Vv`npi5ED>Zp3Of-SC(ETtTe!W%7 zgS4#<$Oi#yqEORa-Tsn?Yz+tuUuOsXUH_pVf3j3FlKx}p|4cMSFycg^ z*t8)c4wDwwK1>!yE>p%^R8Az!fFQMP^lLn|u!cR(ZdSV|Zt7X+%fBxS+;~DO-?*%&da{<3 z!#gbsZ@`Cf>p|MxeDb>8il~TvOlTK0iiv-DK3bROJ-uVaj*kSCf3t?sXPJ^}{BF0{ z2sm#|`uNa5oXn@R^c*5lhC#y0g@T0-jz3&iYyR0PcODSem+p!*Mw)816q)tHW1weS z?HEyJM)!u(g#8z9gua-j-$*D!6q32S*|k_uBMI<_e}x@hwjOA23YS2wxOy|xD@D9& zK8|=_U6@0HWG!EN@!$syOOBV2#tC4OQ0v%XVr&IEyGLl69OkqlG^hoK`)(*ACh9_yQ?Y%*Z_mJ{&yN@)afd<3Tz0v@dz! zt$pw`h^F8$i|XQd1hkqK`IF8>2P?L$QA5kab{<7u>dkBP(PT=w9Pt6h#540b!r;Ab z)f`v{-)JsGqjU!U84GO4957tlc+L-e3cjkJDj~i$BWvKJv5k)rh7vj%82xbJp(mY- zw-r$*(4Q8j^V~G&?64eb+77*d!j8(_AgnuXA*SdI{Mh@8^fQzbs$QXB2+ndHoXp zN-2nk6Fr+C=gX)SLS(jh1g>^DI!Qjhr4D)CMBU3lZ%IgJ!r70~`J7S-moMb>^)jhv z#s<2Oz0aRhKtEj?Ux^UtPf$Su9Tl|Qlz|UQ-xo=sM$mBgMRuIhBR+N+}F>WCEHCygJzzQB9PR+84T1*9oo*|}00k6k5 zg$w#o<;m0XcPcEB`71dy-VL;T?IoSUSpPP0dwg|;@TZd>4fkzs5RaEw!55_C*^VSe zcIWRqHNPC|O@|=DcXqqBca}eG8w}i@5jGI$Dl99Zak^c+lt;jzwy??6fiU4XHPwLU z=$R2+q_@G#zZPrgu(-qW2%@`c233YBkK{G(Dt~n^ZJM zmcS3lnw6ffV8w5B7}9#I-bcp2(jW}cdEQwnMgzJn>DIYuamn1a+KvR}tt};x*)>j_ zet#UXOxdQ_;qYu<@rUdA-MR?fm+jVH55p{gY6v&pb=8zu_LC=`3%Mo-hcEZ4uqxMovut>oja$hp`ZmI^+y9o&VBj}sTf4JCW#jb~bX1!#Ez5AL^aCY+w z?Pk=EZ>BJ(Jh!|*WlDH!V_n76-u~@r^o1wH^#Y!dl@$eKGG5Sp8v(WV?>_@3gLy42 z^X;Q6vO7zFKmG}j7?Kea6Q`s?na?wl9l~A_2Nvi0wE&g;*Pkgou8W1v-=*<4gvcPr04nPGimaDYh?Of{w0_{!{JS$^>BR9bzTHOy zsN=6IUgDJP1uOrnq7bJB13v`0dm~w=mSF#>IP)Zo2M|0+gR#(aaj* zxT;`ZYP8ZXi>+f7yiYw7l35$RmSkD%_X$qx%jq}Z@(4mOadJ1iBS7kNa#>Z+KX5Es z+4b*UT>s8zkWJLJ7>kkfpl+ft-wzCMd(bu}YRkmW1Qs2amj?qqW}?q7y4 zy32kNbq925zu91jdbH6_A2SVfcL$K7GO*wB;Q|Q`-o{AE#UAJf--Wdgz!(e(XIWQz zA}&e#H#nh+oUG&P*ErKsuq`)5dA zz!SOiQdLiNg|D!?D_8Fh!z|Bo*1E2nHziq4-A>yrK)f`&=3q>J zJWy}uZ3*~JAFY=XA>66(F2)M3X>rs^OR5g{BdWQJ4hOea2___KD{p=}=6YqYmjEh8 zSYU;Df_-RUnuamb@GJ)vBE^*f4i71fb-BuU^c7w{h>#Ic!*Zwx7rvjBfc10YEy_OL zG}1?sE=2WqujM6oD#^AN{_WLE)becRTo%7gPl)v(izb%4ebj`WD*7qqjwH;6Uh?n1O<4DrFvpo$gyy70 z*vVkv36c-Anf0mmfj{$bOGWcxl4OVYijCs9c5T*Z%ilFpGrYC55qbHWSq?&ZtDd9C zRsf}cabX3Z;+3xq97 z+0yb?sjRp14X^!zK^ZIn{dgvho};-P&>^5dcW|13*VwzXmu? zy@Eh-&I|mYqad9kM>4Fk2S0*RntVEWnV6KsazVw+jQZq=dMVe6&FO#V^@J2#-N3~N zLjW}W?|?6J?_O|lFd%qOd<+5FTvWosWB@y>XDEfG^Y*g@G_HZONZTc z^sxfUpP3Vawo&jj2l?c{J9(P=*XV+=Fst0qUaPPfE5eDh93{vVUU1UZozq4ll{=tw z&dJKJ%t`Jxp{h6I|NqpA-UO+nemfnJJB4HvRQ1kwmU=SKwjB`K{#d)J9Jqd4uKosH z>Lqg9eG>zM4G3tMY~DS_7#cioc6h(~sRyaP&A2UMg=c$^RbwoM_5FfxqB- zmO*?1Q#1B0;U*tGi5Za>55Pqg?w&Fxy>UL4D|Xb$HC~|yw2T1d0UydBQha|s<>d$D zk5S@DXKRP2!Z0V5#h?s>x|1-|rPmjWw3~Nduzg)N!5ASJ?cra=_zw6Bj7y2?cERbt zz%Q0R)PC!)i}G44x4*kPv*(Eti;EI0ZxJvs{PSHe=4JQQIb#8ps|3Fcq*VV_Ndsd& z?YGcL*zcv2_G7jysd?GEk~uhXUKV1dF2pJ9h5_b_Dot|zQYI2@#eYJ2v*Z0C;jn;) z*wo@tJlOLcUYvwzwI1Z?Zp<(P)nO;ruw&ebg9Kf**Y}rtNC7;@=M_-&nID5+739`x zDVjPlPMnhB2LrIMTQRDoC$>{(f;k|a3KYxpD8R%XPB6)UrYI|oz$O08uQ1Fg%j(Wc zl4x1)5i!8>uG-k_8sH`N)ETvVXes70SdC`VrG^9tuU(yNtZo4$x$jQ|ylM*udV1|d z5H%}ng?a-2IuHV8#4`2xPY}mc8CluLIob_86dh4g8Po8Edh005EIy}8$0YzaB6hosbg{mwC zC?xVh8nwJMChuykIQ5*>%zIjegVNg$&MnD#d}vm|HMr_wbn*HO&YO2{OFUggjp$&m z%&kitB*5C6wC2060yb(xT}!<*EoYK?lY8>x0Ah!39v?i}q))|2GS8)HwiwRMBn?nUCd& zgg7%#RWS`i0V*#QT&1{v(g%R)l{HYvx(O( zpLFUdcUw&D-enHa(9r0$6MUpI;VO(8Q2N*G(c3KV-xGXYbTZEHiOo-x44 z(>7uv+SSxQ$Si2J$Gf$le#Su>suFY-g$CT`Mhv<;M&@-G8^T?VK~@#Qz(6M#Gqf|W7=mZhAx$khEwiq zj%ox2Yk}?u>q;J=l8qYdQ$pA*5r_~#5o~P!r7?b2AJmekQ#AN-eriH1WmaX991l;k zlpicuncZihajvg$tDVZ{u!#4>k^E3QAcd6Ucd18p+kkm(t+7pxt6~c@8w0xKH8jeIBjHnFoSB*7t{S-R@ILElo2J|n(wLk(J3H&;&P2*lzu(c_-B1^^>4e@NdamBP zd=vt*9v>j<;eu7GE=EP{sZx84>9LA=VY}Qy%)ckH-dW*|x^!Xw_>B~7frKvd%61-G zJ}7pLFW363-km`7LNZ!@Fa0*JSd!BZsf{;O7edjLF^XY4$rgaF4bLCE!~K(*9un)q zFwS*JB-leH6ISxerhDymn>6`%DbqbF_v_fHMbb8~r-_Y>L1rfF59V!327xgSmyJ(f z&(YU6hret9PFWk5hAFRc0GrUxG4pEaW#fRk=o!pi~@7nu-Y`%P!-Thk=fjT@U zFS2dgjQX#KS(JE+%JS&pICH zVFfC2D%}Y1)TJLBsBqEL#}1x40Oy1B@jAamOfbcs`Frvs)XpZ2Rw#}?ppeB z&h=BjWLptVRzE9MS@UA5IaPl~GNyoy?N;h;Us=>v57r5IVC&eG?5~bn8>Jegj>&$J z9o%tpta$?N%i+63zG?aux>xkh?vHC#Y$|+jP8HeO-oAqG-jEIE(cY;jP%LSu23FCs)Mg}nb@+`S7vTcmF?y}Hm>u^tUJ9Rk5h*~ z4I3Gk!&r*PBy0~N!F-O(MAcn4W#LUz=z#!@JPP%E$m7LI{8vVA0EJjCLN7N$9BRwy zy6T5tkYk6nY%L;r<0$RiJ&L^Bjs61xn5c5&3zURQl7OEx{VgHO;|WmuyqG+8dlADQ z8$UtZ;`vJXTk4nN#J-`}P25|JIIK+q7um2ckha+DyQ%d#2QTgQkW#@Op!9Keg?wyQJGOsX$C z=;ugb+n4U{_IsvALp0-i;p`T;FNH5aA&<>Jo{}Gt&tY?Y|}n_67Rm9d*jWxn3HRDHlv1->F#^ za<{5i#L$|IJ`rD2Idp5j3q53gMcsP@Mqgyy-I+&SERs3+d$7-7P~B z7sC+{d(05t8CdBz@s-aWe!(VD%~1n|kjn1ypXUBvVHi(JCJ#R`&Yn%%wls~El>3?Y<9ZO5{P63+C=9Ngj*Y8d zdCWqERdK3$VkuZk96Xmi_HCkIvHS>(@Y9vDhQh_Z02Isj1FRk*FA zi{&-IDrS~}n46g~EB}@??C?^HX47Hrol17nyoxba1@lj|Q=q(!K`Y*>?tW6?r_L+=6%2%QD`YK zx*nuZP4Dy*Oe$`8IINJyt4Hn5W-}qO!2^&OB)<=PH~&AT-ZCueF5DhgK^l>6kOt{y zNa+$#P&yQ(dxlOyx;uxE6cG@mhDN%ERJsR-uA%e)<2mQN*ZXO%Yrf3vz3+YJT5B`o z8un(4F)Rj`0B)ywE^@w{2kM@(3Qf)$l*x)f4Bk@x8VDnM3(m`~h6+B`tWbtaFt66% zrdf3L&@jv~FEB4pl5psWT2f+*OO>XayI*$OP`f`r*FJ$VA#3k`*NInkeB$h=9XHBW zZ)6xux>}9W9sv4B;2fbUvo_Nrcg~5_F|k7cyyjCl71cKa1?i~Ni~E~oW<&q6JuW%t zP;B|Wrqh)ZlGX#`G=EJa;qh7l_E2J*mOIlWi0KHsDbA)+KTWaH)T#A9?7&=YXz@KK zBmGC3vS+&s(!f6l9;p1VIr^0`sttH)*=>J*riphpR{-nV1@&n(B2NJO4j2_j%0UJw ze1jKDH>DL1Y$XN+@<+;Z2|qvNL?aXUhH#Ex>En=Q=pwxbiS`A9O74HVClBo26tH{U z8Dr9m!SSkaPjAG$>2_pp(@lZ*`n+jWN4bN|ej=&_=)!&0$p-V3sM-^UkJ6_9EPbSM zhE-n`p%?J%0OfxM-t|mb%dfyw6Q6B2exX9y1W!a6UVE3Dac=DknNSV;Yg!C$ zfrJe;kq&7k+ifznT`wBJ_ZffI zUTis~H!Ioix}=sMd3g(GL7-so`jRX#D>{~`>`;Pp_QTi! zv|E`g;wFyh5WL9JADjlPHTw5*A(jpch>by-vum0jn7xEo)(q# zOB-|~7E>Squ5cF(77QMrk(ZZO{3@PhzSn_CPr|VSAU4k`2mcK7y*B9poBD#KJ?CtX zavJCBMQ&ot$iYrZgEqvo-I-G0{q}W6axe+M=Y=w{C@u}SO-|2nj7xhT)ryr;v{h^fP(W7T_ z*jYB8H^lS3O(|#fgWBL0z(hDui8ko|>!}#$7hoXoDt)}ucbRVVqV^Qk=UwhK^bzFT z0A2+3$B>P&*2D^~bz%{-en&Jj`UIS3b0Lch%)aOvj!iNl>ACqnk_>`~+b1)ju{Yqxz6|B*_d^>{dt6Sy7~s05h-*j%!S5c}5iBH$V8DkNAY8Fj$m zZpN`BWRo6&plR9UB@MHr%7A*R2^D&$U^|)*U%0dq5_^cdmi5I>c#wl>_UG(9yk$xq zNg~^ZZ)X<7)?C(VhGT;`9T;<3xKBZULK5x8fLh0RTL7#H*3_nRcjE)N64v&zi5d@o z%GKd^IqFCsn9W<}ZE?}7T+W_wp&rRLnqfb~iFtvVx;F?(jX0<$EVj=(nj(Z-eDyYF zfaIgFBhO!xPk|TVBMkT++dHV7(H?l-dRGc+yjB6m|U$s@U%n~~S8giAQTnTa5 z&$c1mNs`R_)#$Rg)f4(eR5y>SXC5I}M6DKjqJs`yteQR_rj;l2^`3kDa2PpPH?kqv zOk!X-I-ffO@?Av^&Y!oFenFLZRb24is@DDv=T@mTThPVK6sX~TPVnML^nESO&8I(< zA%1Dtw)&0Mt?(178VmF5y!gSc&g7Bh5n{+;dZe}8B5VJ(m)$?mF6_Rd^MBqs4_ zPn6pqi!=H!Fs)nDIx(zVR~mO-aBI57{4UU_A-!$=ZSnT%eYKh8{W1UKbz?_038+31 zYIHRA1U5piK*4Vw``0%;GB7gpRUr?;Z#sZGX}6t`del9gbi5rvYkZQW$~72M`j)FV zrIZtLVk=TKRzGHQoyf+3-xRsF@Van>e-r&q@EUr4z1HRnKqA9nO&3kjYo$@_mKKU+ zk-QEav%R26U$mmi9R~kj*xK|_xd(E6%l=r{>au;KXjqh4Vap%G359mCf|ZJx`Z$pW zSU@W~|7|7~mHQJ}0Q=(A#UB!*qTF}biA9+fwtRS-fC1pg>Zu&Da?{~fZ>9EY zioLTfi}RP@Moc6F@Rt=WV(^>Qq=U~W8NUDBuisEJ(I!Q2?_#yh8p+!zT0sxfF5lnr zs~uzgSkD&l%cgi#TFzf>tDEQG*uJ4E>Ii2D)p-Z~g~Op;i7qMd0$=JG2LP^FW=g=I z3!c1xRMZL#;n>#hzLt4ipoD5-2jx~}@!=E*yjM?)Z7RpcgSe3N%2Z`P0HWB3B-D-)W~lsj$LN>5^Y=5)HYAEY^} zKQex{Ji11yxmLf@G#aV<7O(fWS_QSp;v%>*l%7Ow7mU>RqSDlX zS0dDbc`30-E97!#17x%IhN#DRu=gsN3_%g~#!VwGfs?%#xy552|2Z9X_7dd0n}|X^ zb%MS_H&CeH93R~RVPC37tH0AK-8@f&uw1z1$Sd@4$nA5k4?q}pbEkleL{UV6mi-TQ z@}RH$GQzz#C2x?>17>!cW~35KLJkpM5OHBv8J&IN8xcZsbNjg|Fr4!7e`Xz8f66)hCo;%m3T#LAqv_OL2 zE6iY4{j{*ut)|e?{H4|V=94j~P7ltVy54-`#}wv@0JKoIoUEllWq%bBP`WQoT)~s# zRN4pJ2|b9ur|R2kS&G|@FHIIvS^wd(K2l)>u_EpKs@Td#2JrX`V4Ni`EwI~u#wsVZ z$3{y%lRS@kk$Xf#7wqE^Wc;i`S=L=%Ns!bR*0}s>>lxYuwrVxRO62C_QKR;6yBm(P z2~pe3IZ-`{^YL?=U2!Ouf^X2IKl@UxNbj`n(AL2U^B0+Q`${)9COaZ>;t*0=N{`5sEx37DkG{{Z_2M* zVzg2nEvG#lGM|_kTc&mEf3@| z?lKqHdkKR1t!Szks=jBzaJsY~37z`I?t8|j0Dbu_+;+gq`EUlW<`zExvnP!ge+*`N*vE3sX05}<#Z`yZ>iHm2mk%;202PqhWRQa*J(-g_v zefj9g8!s4*2A(Rcyi<9s$ss!*S7Ym%#IG zbLj4$PD5k%M*^=&{l#n(uZz=A=NpvIff5OW1NmDww|7Fya|IY%jk^DN>mc?#BZ=+} zxKi^YeHKHSuu)|Pl?F##`Xi2K%Pxcox>$axR=s&_d8Egt*=76xx61%S>FyO!&rzpB z+jcmS1^s1+ldG5(yecJ=V4-((dTJ1I8S(Wevpv}%MV=amD&mpe!AaYugbaFv5{iQ| z2Ac{8-Oq$FGh;EYs)8p)Fto!J#$NWrN(rK%=cZP&YnQpwODrHgb$U|Prm<;ar`ikJ zlvLlRemzkTuj+f#W*M-IDS-yrNK=3>3H2FfS`lUlw7hV=Q?MJf+=k1;aQu#NJUZyh z)8}tdpL{`QGXvElrg0YC?av^Tjm|>L$`-#BXMG)ufKJ~7*DU59Kqg+G1@R(XmG}di zfM|Cx^mi?~;u*i9AqMtCF(mhzyVo>KPX*p7_*@Rc8lS{Fx%KB7HlsHR6i2zK+6(LK zu&NYKjhe#qq$f3z%`t8GKrhFHtH|RBYrFZmtQgn@|9~}Pq%G-KX2V9If zCaqsTyt+8|w9bVDCO3!mJiz8)t-hmPUN0A^)1s6*b)4C^?ygYUSMRs0z?BZlqKH2N zhu7HFd>(E1n9%mb@?R;*F*=iy2B#{6=Mq5{EbZg!EM9{AdSurhGk<-U2(TYz`~Tb7 zi{nKYJ|~W1>>tJW5o9+qSwU znG>zxC3AJ1T-C((K=sm4YAEXDqWGb%JT|D}Z+-c0uM;m$AFh|L;uUq9r8#J2b}ng) zMEK36t)kM(kyZd=^XMlT658%+p-8VXZcA04O9vtsCE3{KNAUE)1g1WM!?5L%)~nGM zIuz8|!+X#CF!Q>taQP1|P6?a&#S9`~<0_+D)%_8-t4D1u9%~gpNgOmMd;`yK-^0}x zEWI2qs1g;y&Qpq5TfYuP<`_fVqT2S#CcBn=^f64;209I?e-0y*?1ULBod$mSdwgg^ zn|9{ze91X_uFc!IU^tR}+^YFiq^JxQsWpk#`EU$sA53gnI5w7cd#NX47?GVOWUlJh6xKe3Z z-4|YCNRSVnfje^jMw31omJ3^~IlDFGDE$WNzrejcE7&T`+ zi_nTr{dg#S+f1J~sfEt^QIQax6q?NaF&-Tq%t*89G={TEcfcQU^W!C$$B8UsJ;2?} zj%;hUU<7hP;YW@z25Dw0|7ci%9elqOCk&Qp&!Pt!mAN_}?&_eGp*-o;HK zoX1Vw@YnS6UX5(tZW4Pw_ctfV_ry1<%J@P7{YgLaS~XmVky^IUnHPL#u1!Za7eqnK zXJ!_6sb{QT=E^Xm%LDYK2_Z?~q?1m~58?f>&OGS_>z1Nfi1fl*Y_Hh=JxJPz1->Cb z%zr-L*g29dqI32_K$)RM86g+wk=0`?rg^{aK0~j`5(;{?v}7L`6~6Hq$tj*UUtND; zt4J1g8(_K&y&1zya@)r2znp`(gY%G^&tD>L;N&4XalL!+0L+1Rc7l4jo$6?3Q4ui4 z^~4>XKi`tThI*I&=eEWe zM6-5OEQaVavCr=?6@+rMr^ykp(aQpL6>mY>QJ~_jh-okXpG&E806?a1f`CZ9!H>}_ zp%s2kzIk-T#VtMOP@Vu8vwMMjsY-CUE407WLZ%&lVZ?6EGI&X;2#ZAo5-ExzRMa*>m!9JTAL#Ss)B!JIAR=n?C1SmF0etnV$$@+ zZs}gfDS4b0e^8(*3zA<6)}0w-L(C^S65GHD{(cS4!M){8Tm8}u_WOZmXcM%N1r{nt z1M#}PzoT4I)2XunOpT(1tkU}3y-wR}vCzRr#)De?&Uua}rts`;ibJ?0Z5G$)TCIU$ z%#1@P2hc@NOz>qyGH*=&vVVc>$jebusS-|@?OjNMvUwpS7l=XWOqWh$RZi-!y&!SO zus;s8gWhOzQqmGWoh+|!_kYwA`R1#7yHDzd$(A8md&nj5gwr8c)Fo}Fhj`7+>}Gx5 zDQfUkGW#b5C+it0J8~0)PJa1i3E*N5Fc8W1j*WK=&6bAt7QpjBc8 za(?qIblcF@pe5;ecB5|HxQszIaOX?>T4<3Z7k9$M%o=y95%j6P0~%}AX99LZr$<>^ zob^E4D)p%sOUs+w1LL9n7bAx*>akbCLTCgUeMeS9lOzCX7`VV?v=4a@5T9<=Z0wH@ zsQ(WO0AN7N<=3!iO>@e+OS2sq4hLra;~=MELf8S}Wtw8x~5 zy8tP7?O4CB>QNI}SKwarUUJdFp4y^29K}7IV8GQ%4&4u@Q=a+GjdhOpAMPV6qdtyUr;=M$ zbTy@x@xg^s%&)p5yOo+Z5+U1=ds5({ucK&>J@B*wOHnS*_)q?jqw}43}vZFmU!Q}^ z-s92Hw>(BAqna5jPh=%U8x-5bxdo!!JhoATg0-7%P{7dwlVWA7$TBit=9y4Doofcr z`MFZk;>&^HZ=Vd{w)*JG|EXSVoc-s!d|n{Mo28YbJ(2cDt9bZIPBn~!+F_l{2oioa z)w3EVADFpMTm{XbCfRE3<{BmgQD4y{Te}hePpyfK#Ti(Dpx^4*^W9Y9&vzEe^ z(=vY4ZC}5sr_6w)8bhXA*^O!`z%4Hrvp)jeb!u;fu|m1CfWZEUMj}e(e@kEC|GPF{ zRA^1}Dmo-kDsU7(_zP*~h5*e!SO`1)*r2xN-nT{WH2U$z1WO+Ik}|xBv$@0bGHXSW z93F(C!mScmlVdk0Ss~f5o3Q~xG|Q{fd4>w@b4^HTa#b4(~R7B zLOT%oaD^Rd6(F0DGw}~hGn!)F$BGtk)XiZquI$}Yc-`my1{sP~LULB;nlRh7Qe5Tv z;pi=s=3LNPk3f;F+WX!^sa9opEsvZ2!++Td`vXLMJmSMdNYb&>$-H1(vwe;-yn^OG z+E#{P$U5{0^T9DIY#(kr;4fyCcB3Xx{PRr6@b4**R3YDjXMcM1EM7Qj zNFExQKW5O-t5YG>p!1~Y85rl9;{|1~*v9z-whUICE&jxD9Nr-q4)cN@m|x!Gy)AE|%z@uf3pAc@wR2$Dne zW?lkMS4e*=E`txajBCeDTWCrOo(E?Tm{7T^0Z4gCBAG21$t_vlD>c(d3cY|nrt{Qu zgW)5E!1)5Ry<|MboZAX-DoIf(`j&Lcmb_S?&M)yOubt4~i?XSY|D7+-T^Dym<`z+( zZ&^eMVE!`#4quAw%x9!%GcVecT9U<^w-`z~m7?Qj@$gyPwLWS4_OCLRDB)_=BRyLA zCV3mZFPx%wj$y(*9 zk0hI;If+p&wYf@f8oro7pb~t3^n_2Ge{igmR{<%u6WAeaU_SIRZZzu@dr5d+AC+?F zkz3v`K}cNPurfzbphcywirbF9;OwNFJ+-w`lbE2PLguKzYz)6qj(*-j&sWCfSC12P z(SN{J|3t)Udgriv`_ER2#4SlL#t<;R&S;vFK^U(9j|G+_dHNJh4b zPZJ>02iE?!%+2reOM)w1zg8qbwDL-kU&#UYQ36NY)PksLVr%miYp}U3?4~8>l9ol| z1y(w-n64^nKE8zu({QB}`{h;8^q+x`R0hw;AGxxrkFH5@N<*DVdQ120=P|XIWSu2P zB4&J{0Sw4(ivvl!uDVVP8Yzp;%bO<@jw~)DX$3`-+)mPWqi6rM(d+;7SdY>nqKnb_ zTYbkzS2Vy{gb7x5xB$y0COoXkj)I!eE6N}uUFb*q*180Tp8w$~x-DT^PtB(3_lypx zRTYXjIw}~MI3C@Rh=U5gHtE0Q{EZrW*%MOoR!OM*qdlD^$((4e^{a3NK`Z*!m(0)t z1T$h%o93|2Ki85(W66=D7jdg-Lu*YmCwjVDRpJk%51y5e1wF6hp*r*t?|o56ZX+gM zoLoU)y z258fvQwSL^e29Fk8mK2k)36g+88jyv?MGkMv1C>@S7jX}Gj7&Y{foo^E`V0wr6919 z2~H%9qIt$_8f4&yEuCXmcp`@1yH2IKap~6S^)9RLI<9}2R&s4XQhLlLw^@r??yr8d zi7bVV8PV9#SvFL(3Pp>o4$t_!URpl{CQhk4gt)&BkK6rigt*>gU1iHBJ#Z6EX&VT5 z@u#B!{A2eZ+Sy9ke$!7@#U0VSARuF~o&N9YOC>Wk6dF5_q5!-z%)iUaCibqw?x^@k>?jQz*ozO92ptvR8CY9iX1Ie4O#g&`vh&# z>_dAK8LmlrSeZpx3=N;E?wY7K;Oh29N!EP z)fx8y^=L$mN0J+n+xpQS-{nzK<(nVnJUG1%DUc`b|3xd`RwZMT)@Fw-oI)KK)ya4@up?K%pE zro!Z1pJvh86RQccv`I3H+pt>xVxrkBEYEailzca;5r!ua;HTW=nQF3jVFyRqXZuTqTg+j67*Uc{oQPGK+>_A^0D&XNTvi9Wl&}{ zw0K}!CNpny3&R|;$;t2J{4L}KsnmU4?ol3F=j$DYVeyQdq5m17RJ<@6?#X%t9AZ%c zk9`NU-ja*v7(Pi2o5mGi37m{@cVb?yU+Cl3^zka=5|4V75?5&r_EMwl{?QYiSeI<9 zW$)cFAwzsBA*+YBoTj)MXC-xR@x|!B9?!8}=WS^!W%G=6Gjt4!~Z=+t|F)XnJod4Nb@ zL!T8zV7o-xrN}*NSf%h9Wzq_as#*N{A2n&VpR13w^l6`W%~xYAAE315e@xgYz`W5^ zJ)<9X8wYj}Nd4%LORa$#IC`uKBeSXGP!^1biq5Wdaq6HB4e_t#0X8m&_k~aP-zr4& zOWC~UlNk0#%3FKP$afSx!V_GKNdFSr&NwDgQC5!GN{5lR4<*os=Gp<+G&Sa0bt=P< z87PEeVV7Q3hL!9%=l8589WUacr5i8>oJ*7V3}4_WO28uvM^8nP_%(h#yk|9&onm=N zx1$Y5RnBZAQNtosRh2OY^gjneN=pYiRlb3*D?|odyqyf`l*$p3{}7S+$wk>gSWM3g zIpmY-@Mw<*4l6NiDxVrArEm^2ArJOlmb*E?8?QC4IEQeu? z2JzT&I_7ToMc2flyL+0sU#b_Cg~e~z?iXf{%DPQkWd1AZ?4QWOQ!9-*)3Xo2tcTb@ zKIjhwBnfW4cb@{x$4T*w9hDj@@ycL;EncJM1I7{T(x>INP|cVx#nd=juUmt8fF)ZG9xCJPD-lPV%3F z3nt(5|MMGhWx{|<23-uNw`}b({>>=_5I}rGw9L;D^QbD)LFlU=#^nE+V#?@&yr4~- zjo?1h=jq6BU-ud@uSL6^VBGdV0=Sz+K}>jRk4>ZXZ9>w{pI%o(NzkQ0ZgysLFPn)H zvRnMX8dVzY5LQM2UbGG+jMr)mwzfvBWNyLg=kG-2=>!BBE8g^Z$Eet27R0ohLqK)2 zpWi8io{0@qhZHU7$DtD+#y;Ns!QC59m0wmuGmIJ>8^>#|U5G1XGwr#z`u9Ty5W(nV zBChmC6q4ziR#q2#L({7g=SNYq(oyG{s(>|NQae zwUDK^MxgTFwzV3WyBy%_FEWELB3eBKzoe%ZL#P97gAL4uPj(M&8@&w#FhF!$$%zis zXpXsyf+lBNzeNBLSo&@|V0t%>)QkpIAec^3sCnrL<^z>gc=VRqHn-siRKJc|E5+jx;H?SByH;U}`&BIVl=%>E+(h53O= z^~bM~DhDbDEgXLr2@{^-6vS)FVKFmEB`>O&-Pgm1T0sy&;wQ`rAuSir1kdLoBcO9D zt9=ves+RLCJPNRM5ZIG|uT#ey%MXdZ{5Hvr9suj`(N3L2x4UDC(f3R@{B%Z_zT0pgd$A7s$7<)tbqHz=-lQDk>4g6l!?FOMU74IK~0sx0y2LS3^#bp}NNmejg z^=c!z(M!hRH2?noe*K>x9&2kiS5nm#`vDx!=^EQ)oxp}Fi!JL~3S_P?51luH@V8?* z%NhAG<;*Wj-0jJ-FMym*rsH>vhn-V~FMOKr36oq-6l)xOT;aEVKL4vX{cPpFX$ZnE z>w!iRd2Y^|pM53`x^3n8(zidD-Bys6jJg<-n->8SR8o=HB67*kquY1Pe17s&QVbU2 z;1imv${FJi;4B%ZDU%gk7Kt%gw*}ac@a)qEmBy?a_rv7g8YEET9L+<$CU;tJ(+Q&5 zi_y;8j79jKq~lG;QY$Nbz23&-(!%_Q{PWyxU`Edpy$(ukzOUY-1iJZlVJ=zEhp$Hv zD|AcSXRKdIK@ueBvoF_6pY&ZARA0?YwAT_er+YG9m;GnrXmL}6LO=#D6BKHxakn{EA<525+IB_j@Xi$Yc50TKk>0j&NgW zM}7YGJ^IQUEvL^;SuF2U&oN+I}0Xq>=Jy{B6NJVMzov#C7ni7e*!KbeboqxY( zz!XT$Z9g$&X~cCGqcIKzKSACgO6@>;?BAoHGJ+D%tj}Qh0kV91hhZu`J#%MhZKt)+9Fa>ZiZq1{dOfHLSHuWq3y)aPc*f*?F!GAJ82V zc6Z33t8ftC}ZOn@&#L9;AA` zZcnFZL)-84JHI&2JwFCHFG@mMr0;Ja)=RrHzcm&f*p0?AXWcujQ+%VBP2|{5y3u|j zS#M_`mel{6M)bDVZ?{VG;sEW*rJ1zqg}^Vba7jHR3}P;Js@jG?S{ZyDFzF6ya%lLR zqq*7IzqBRn(1N;TNMamup4Fo&W-sgusWDS+IHN8kxlS0~6#v-9OohDz0NgpJ#iz_g z470aKEzD%xS--@nI?TWS#f)dX-!XYwrcad{n}K)WOe6c{=3fiT1w%P{;l$^7Nfs~e zYILnG-`>On@+SQR zVe3;H#mKCF*ZVr33xj9Bj^R%e%tR4~L4Ci9YK0bn`!AD!o4Xpu=QRAh=YdWgv=3h1 zdaeXNX5N%=)je>L6qk97D#SUQGIf5_@ZOdsBlTVI5jIOk?rzK1GpylWTER6JtC5^< zQxfwufs3;6wLg!WT)_2w&G#ne6->w{*(%e>-gdCR48E}J+8>KwIcFYkVDj$V^JyqT zCZ&r#dVx<7WqAUYQD)b#p5O0(a7pg{@O+ix+kF%bFPU@41GlmHWL*96HI7gijfDS| z+XKeCY$<)+xbG4Qg50QVFL*BPiPrmfIO!~&KKrzuX0fm~t*|8JnFR3nv#(Dn9pTztv=^tF5J+=CCV)f2qntNnnxET9?nxb{8*^AMsQJ+D%L_zK1o9M)PFLjBC(Ck66c?w-jgE$PcH2P6V< z@fYA@FF9C$`m`L$mdkfqQz>fy56`*vlJ(awzcIf@H+R*y)NbP z-&TL$7Ufg3h0-%;;-0ohlr<;k8nHU}}6eT;BYUbE25>N^C(P%w^ zdt+~+{3bL+S28T(It;9HEU|0~m0};rUTv-OpLF`p08!F-9WAN9&So!yH(8MnYNsx60U>!l72cKTKC!glFQ8Btxjh-M&E<_MT#>9dkrF&-^O3ohnu*2dLs=YZJZM zftVmqgZYcAovN|#?>W1Ad#__hTD_d)vK|D|E)pRyo}H;GWq@lo`tzfF^TlekeybmB zsrLEJ#ag`A%~9_#jn{t6`}Upowkj!}+x@;8z!AxyYRoJ4tOSG#%a>DDj(OGf!J>)Zn638H_z1HJ~=C1Bo8>mVKhROBgIp zpWx@*%}?*x+13=g+_ldjR;K)|%B$XB8PA_Ta3fY%z`x9I$rC&O~^Qp?-YzN#J7Q64StDto2*o z`QPS)Hfr({iHiUOn=Pa$D)L*2TQzq@LiyZN4_IB8}f-v=C4%U z*ec@IxmeLKr}pF6#bvK>?QOaksI|5AHK8WaCq`D*9*3zaGx|~}7>B3jMB6yvavcj7 zSMXZ326o>?tfc^O^xssOO8wSYRZ@j5|H0)I3dyWut`=SNDzx=nPt~JJ8aUT8aVG^t zfhAFQv7(EIOg`i$ZiG$^D;V>BsNo&~cy*VSDc6*s)exVqeG&w|2JpoJLOb^W3$mH+ z^77Iu+X74j)9miS_c>QA4>w*Xc& z?7l_=lLBM~kYWWorJ5zV0Xy}JKIPtl!@^S+mr&wkQA@lp0NH06Dq0V4=8QvRd63iPxYZVc^ zSL?h)^UQ~18Xd;-%;7(lfdLk@+s+~7u{@bu96C487IGW1&v1bm>C-g)P2vS!eC2Ta z>XR2g9!=}KF~5QTj#7X9vVPb6#ji&hi7TnFXe~)({om_}&At;-%F z!0;(fnaFlhOhdn3v*|!l_}1^a1Z%QZq!{Mk`l>$TnJJjR$I4h{H5|4ztn>AoF?i9( zO6cxj?G1EH0`Gq7{pEU$^hdEWBX-U0Iy7?pnONecu+_@O8^`LqmU# zFIEeEiA=WLR-?Rl_#x9t17r->bob5gf^H(lxj?ed{RhI>jypP5QzM<^JQ3WSc~iAtZM$XB)txZOSt#JVqcBB%(dM_aaJDU;<2HU?KK9E z0b6pM9fE=;w!-bu{zZmnEw7^O++C~@l*WbRpkId&Yre9x`(9RmYk;%!}4x z+=&he_`@YTKzuMdSpCCG=X< z*>aHY&eq~Sh-!@0JDZip;ZizGeU-;-bA=6GjgG!)+xhl}{ktfLKxwl339Z@YDawfy zbC-vmWg}R`td~mr_8dM(^GoB{q`veVw0dGH;i`0yBbHnof1Wy5dXCd++wup`fbFeJ zwJ*Eo<_gPJuiJdE$5PkvhM_qB0kyU3=MM=D=|s&**VpEsJIX9?NB%R0hI=PtE3AY4 zFFeffngkgUCaSy&^*=F7$%k9B%&!e|A}}lp@od|glX_O=+ChLlBvPuuNu$0qA}g#E z)*5&iF0^>>->(jEm7dN9HMV#x7wv!DsC6QZbN4ymqgeow|Vsn7cS;@wUG0c6ieNYO7S!mxw#*CBr94(<`st>fYDB zb*{vb^{|k$8%5C!7TPynF9rWUr`HiK-i{n9F zS3rsYcQoaAkjiF$Ml7;_obOvi9Hy78 zLDo1RUk16#DWG0BV1K%+NK+tH?dU+Bn^n>Ip6VF0ESCZ5 z{#|~~ANdUfsVx2a;tw`bD<;$e3`h9nTNO{0MJ0&Udtc6%2vnKoxyb+Pe&OO*|7P;i zMdEHo7~NfZKB`BDF#)QBK!OitLEVp7>lGNYx!(COZrBswF4*C?!No4ax~}0Hp&~A;-I)UOyG321qdpNZ+p%TtQ8QOC6mAgd*STX@Fgi>K6k1!NN-)JAoR*5G^*fm(y2p=Lq~ zcpeZz{YS}30g8`^;6uV&z3+_QSY5jn`Dp zTeYB%ums7}DpT-KQN%6cT2xzE3osImSDS}pw(Y6OYswm&v+^&pkv!4r@kQ&AB0bQk zRr*$Qi^YJPdRAQQn+`l@K=8MNB!F+rDBv=G0V`5dURRe@dt6{yb@ zPS=_IFZ4U+OX|n@lgs-e(2BUbENHYj1FxDGiF5 z(r83DCtn~i=Co^tmr;vmYjA0K^Oy5Wev{?is?oj^Q&*DiQDL0;0QxRC*@OWpXUy`H z!nK#ylY+8Nzd`kftyDd-{ew%LqCp=>6=0hJIzy*ck|5I}Acy_6$J=(Zs)BMH^v<6& z-#-rqG*LamJIw(=#}I3x-H-qxF)EIMT!V7-S|5V24V7V%>z65`gEtQ9 zumnhfYBFm{oE_fT@HtJdrXyRxRr9l&jHcdH#Tcd|Pr0w(xDoR?=8nDr*mcKjqoVb6 z(ZsRc#t|Tv-I~u+s6=pb)L-P?V1}MvAW@_}#&3m8JtwnE)uPB~#i=>S)&8H@PS3SK z!m?K%MR35s6S8w6S6dAF28Ld#7?%{goGNQa6=$JWnnehZc)CHB@38F@BLAadMF8eU+gnZ4fdAq`^qdqnzrcYNogqQ12e_8T?? zbgXA#YVOvQ>sBu9T=WGr0fG^h+>K$lI3l)&r>yHVIEzU@w`;uf3SsQms95|#*@fI& zJf4uxCQE;x(q#{s7btQX<2BNar?vpgQ<#!A69rjciPaS*y?Bmj_C4FRogLHGj6md$ z{oM;i<4K7qe|*mqv&r+z=laI?WOD^+WV$$w6Ix)E3~9T(Fb#Y*-1Ro6KFbCrSN@&|3U)0@eb3@y9=I~+$RWY2GH0C`F_*x2&3UGbSxHF?Cy zh1B`{+6}E`b&%~e!e?;x@fvZT@Otng|AjqCq$IE55X`G{jZ!fWmojB`j>z3`VEv$1 zk@PXS?`Amf+V(+++}q#%pp_h);$3Oa%3yP2X}3~uIB15=Ql^t}qb6-a`);YjR4xGZ z(`H^=lIm2sYuUrB?11E;HTp_%wbr{@N%-|M1^HryhlI*?GG&_qPgqp)uN;T5t}EjAiW&X!tZMuWB#D$K6^q{p7F8mQfo}3Hh#?8 z@lowtQl4~f_nKRK!>Kn_@gB?F-maE(?4&r|9k)smA+SA9*Keo|3}VGkrl(d95W!`ZA|=psIpK-U``+$#1Im%?Q2BGlLjeKP9||k0>|2R zuz#tu{8#fCLa);{7OX{NA8)iFfh;dwYEbazJ!l-LHD8vD>ulZ+>HEpSTaVThxocD~ zr7m$vJ^glX3&mSDX%objxZ#U2LA>+ZpN@zl*L&&dug#CjAJXkRVJK&}DHG33N6&W9 zz(3eeS@I`Nkm^^T_R#Njf0W9-8sCUqUpQkUkCeIJubIRRDh&=s5%c|N>RSTwn9w&2 zr`Y)=)X78xw-O^6vSj~tB-MWU`zI#rbQ0T`2GbG6AzQ*W+i5!B5HUPsV!BNV&0WvG zi3!n|7*fSVb;in@;~TVDdI^>MILl`qu`hBXhIr?t<;F>I+)YK?t!s6m5p?@hAj@0o znR*}UO*6|RIg}zG4AHUCr zkR&vyz5)?7>Y4ff)wk?kwZn8^S}8isVa_4bXUJbSVXEJ4X|D6guLqvcnd^6SYC?bW zi(A0M@H5BzQpFk8^P{7q64N^j@Eu-(vc2H-hI_u{Vot8c#vvfkpJmVBKNw(GUFH!H z6T^k_Tr+P6a;A;RLD={b6g?Z4PC^4!V#%3y?v zXkr+W=IlX{ZECLlEwadjv0;v5g`_qCig`tJgqCHduU%H0-`Doe;~x6EUGu!~&HrQS zE7+p!y0#TiKtPa232BiA1%~bp2|(y&`Rp#p^rF2E|$TS^Zec_Vgjm6^?j(}6oTBpv9%o9K6;xf2q>}R3& zDYwCunDZ?7362N9+l#DNd%tzb66t#`-)9-0c6XZeN=e7KG@q{Tll`zc*EUQ6_nnK=}%u5Uc^$LE?2AHyjj zlc<=)@94Z*8av;=!hf3%?Q1aq8^J_vB9!SK)MMaPs|53k9_)guIHx2o^3jo>hbssl z9|pUA5~b2olo#zomoa6he1=h#gXES(w?K&8WORvEjbld%=lX1tvO4Ctb9R|#Gr-86 zbMu!cLk6?-Kf`&Q;2KPd1s~3#rv)FPCZWtAU$(Adn0nJZ&jT6?p;d1S}jYl48zV( zlN_!yG)|t+9Ai4_o?XUI!gN@b_t(DE4z}!TX8q{a#<+ey1J{eL>G|b8C+sbCzW8yb zyOQixWWi>d`yK==g6PC?o)|_Ir0rN$w!dp6fanE?Dm(Pl$cr5%H{FZzZ#ho+j(!PP zK3Rw>-+yCQ{mR7TplUmCuf?j3SznN)CF)2WFLvn5Z`4QynasIbC0;tL*vdUWfR7n*DW(b#2c@(kQ*x#lhc*``X@df{@azcg?Q)%1vRJQstbug3>FoAzPOpR$w z@~oe8StbUPll>Udh!?4uQ!XY5L_PXMUFtd9mQ_Av)=GGC`v#vODL0hid(w+r+qN`s z1)+qQC)fMGS(nM=i@?d4(?jTKsJ?h8QtICDKN*BYwD%pKFRi(`4;kvyz_#mN5S>S| zMNgAVg{wPUC93w`Es3+Tdz+gB0Y!8OeSTiniiVk&%IPiT`W$SaTR1Y2|4qb0@=!*R zl|)>RR9c7A^tvp4U?+U{3jf+|B~RNCB$Lvy5%su~whDj5+@cwdu|RJ5h4o5l>+X04 zrT(oY^*Sqaxc|V9>bKJ&)`Lk$W>tUDI*3Xm+|M?y+Tuo#qQ)Vo;Uj z6*?V8RS#Rk-iX2YJdz06aIYJK+$4?ZjyOlyX^dH)cg5o#vzz5Qe0SSc8oLt#b93f9 zwFhTnY}vw`x1*~)vElWey>Sd{ggq&R*dyZ~w^%fVIUJs_8F~>fP&AOdXdCs50o3m& z7PTw4!WtHc^9%6W*DTfnHF+QI9Ii(qW<9xvKQs(;g+6VaF)o){2%~O!bl6qKusde8Gme&pT9glID+#BTa!z1l1AXl5+`~I=o>8#x zTk*Ha`u2ZRR(=K8MEnrXjJRgEqW9?N=-;56tPXBQXFJo0Tvo|4MTQh5*yH1<*1rwp z=DGcI|L*>M!S1S?NV$WW@~DU%qS~?3K_cE2jCon3L5v ztRm@uV1C<=yE2BSc<|7Q*ZjrBVCiX+GVaM`X-zzwSdogd9fgY{a>%Wko;7k>l61ez zIih3Jd@{@2sv`y0%VbC8+xxj1hcf++C;8Zav?lNi+&9~%>T`!(4K^O%l+rb-KMqrU1%~od8o2*lMEv># z1%vv{-i0t$QT03=xQef${eYS&-^z$W&KaL%d*-yO77Xw3z%UXWNlO2&e(($IoJp?A zD1(L-&kdrBq?>1GLM6n9w`sPyQP#C>u)v##5}E(t^uD*lrp*18swYJvpv`{VTrdvt z`wIvVMwy{okE;LyVGkC)?*+U7I`QN?z_y>3m6aJ?so87Xe!636lzQ^mFSbcpR#NcF zlQ1Nb&bp&`YUiuST3inJMqW}-NIGP!yZ)9Y_dqJ9VL4a<)99ZqKfizHUiyyw^SXk~>DD-i zF{4E6Nc{WTbeM`s^EZl}ot;tfA0S{LV6z^GH>@D5d4^yad|Q`C<~GLPZCTeDOkGy| zn&l?|Eh2A5ZM4Hjn4fr9t}{A7hB z42vh8eq>qO!K`2Z{-?y)c_Ic^oYv$rXV>|F>%My%^W>YaJkvsQNM7eTKY9_pyS(h% z7)MDP_;z-zfaDph*)F`3W@&Huu-eKLvzUT7;7Tud=}v&wm5tn#?4z$#jL)ow@$za~ z=8xkR<2fPfUiVa{KS|5YnXB613;2F`(bm>hWf$E?3=nChbNZD%QHqnX zHzh)D>FEO17KTXV(2W5%MMj9+FButA2s=7!T zW!DDxpFe*#>QoydhAg8E(OKTw<8eqvV8Y z&H=5z3-UL$%mh`DL7apq#w)l(9v)fIwa4E@R+7Wyngyu!?IycFyt!h>!j>e=cETNc zU5kS4{*|yU#P)KG7&=FbLVt?ZQAT0Yb?-DAF_go<@1)Y_mMi@4lDPO3i!VE9Hf{X8 z;^*tk%*;;S5{3zLYiq}{nEe6P)^64|g{?nMv*BjhTC0)?gM`D4p4Z4!8zZPhh2Nf% znKK=LO!F>)haHY<=utu-(jBDhY6Jl z1Il}U(%^8z<&t@`Y6~@{=MfQY4F=1@K}LmDbDSJcvA%U={WT$wSOjeQ`Ex19si+3i=s|yebKISG+bS`k~-hYCX~G=&s#QVn%e#_EMd$)GrjvW%-}b%C3zNxRo}b6+H-z6+J5%NC z{#QK+d)>QBe(Fd5`q86uKGO7;o6#VA>^9}S-TUWku*DcX(u$~0a(vO%dbTZ(Bq@Kh z`m(Ivj4BDe?ee_4gd3+|d-jXlg2mNKK%~U2qM~xcv_Z4QQQY5RrBk&%e_YW-eby+v z3vKc#Dfwg|+u)*{+l_ziSz0sgnK0QoAL&&>5R&Y-B?;pU`D(J#uzEAh^RoAvb2l_- z?|aqpJ(`kwlCW*%pqs4LAI(y8rk+()Xgrcxq~HhZ-oBZpyDYdp6(PcH1m@-2 z^Tq3NZ?M`RFU<_O0j?qY4p{`Pn7IbqQ4lyBQ)m(Zf3cWeGaZWEf~c)O+NqFDc?iI)n%NH?nqG@i}|hczrL z3iCCFEHC|6R`Ek*tFjVe>V(8p{|U_wC3x(QLtek-uJyFk(s~{f6SH*a>KpIxk7Dpw zQ9(fhh+D{f6}^u>`Hw2Vn(nSF%2QLe$3K8ZYIKa#D8nk?9;lQKx~U^G0Bu|i_M9Gq zx0{-U1y%wAU8NC{8Xh1xwF7z5*+5>^IYaGwHf?eU_$O+5dQ^Z7K{vN+w~O0AMtCY0 zhe-q<5H-xXu7vX(_jB5{VR2{JEH1!+n`YHrO}S>5n{(q+Dmr(Qi%N917~t?_PrLyrWwHkq$974+#fEk8<9lz{ zW8H83#`5l1y^=CLACOuv=D+>l{RSR{Z+idSTBP7g!YgVg6UX!3nA+K{Ij2dTZ#4H{ zEjXLkU!Pt;`-V5&?%J%Mf;9a6f)#eez7#S3d36JB$URVvK|SR$x@+ZFYTBa=AkHnm zkMi!o{3;PiE`aH?#udy)gdViBV_DAD>;{-$0&r=x0)5R!l=pJ{M{9O}=;}pQC<}++ zF-_|ZNKe*#YTkV}+CL$-#b~P*mTaGq6WeWUhU$aZ`5%+in5S3%D}{}JFO*8ypfe7E8odhwC{mu)Sl;)hFB+$ob%dR? zYqWiq;u0xH|NC`|D)+GEOrI|qqV|3kbX=D(@p4?3A)2D`@zW+^u9A-ny1NFP-$p^d8_;S%HXQhdZy|&-?)k5}J4x z?2R?QGY|+HUjwlO8^&X#B#dA|BBm+qGM9Z_gI&5!dQ&^1ga=y9>8{J@fojJ$|F%vi z)X>E(aOw-t9%*YYL4LED3ltryA?M`h^Lc_UV=EF-B6u{*Z)snck|OgHZ-1{Ijk%L1 zJ&>dt>QVJoBxg1A7HmXe&3iDIgQS#;q(p}tNrMzghZK3}i95t!_@IheuB#(93y2-b zcus)|Wr7nk|CEjryNy90k#0b)H;H9;KeZ}T5!gESgHor6t0w#NoBt#-9h1d9X}`Kz zwhFG(zt8yB1>=Xj_V*S)9jP#X6wpC@d&*MJ?uhI)yG7gOle0%aX5f9W4jpjVqs>Fo$4Y)s1T=q3WAAOlw-7kD`zzH)N^AZarI1q`@lRajBZ6T%|`Gac|0} zxbb`bL#NW2W_NB>)O}rwMk_36C;#k8OVoFAc;nhdVqFmAb|pO&wesR-nYp7*U+Dj? zIHcYmlHNI zo-IUr9-p_SbPFddW=6LZEPmtIY$lvKtoOG7QK5B23dn~YC1M^uF-bVn-vn-qS~Es< zE3G!OvUq1wF=^i0IkQxvP~O>IkF|O?GU?v|6@5JIF|=}F;^`|5H%EPWEL2pjmYj*H zo)JtHKSS4agMk{s;hheMXk^JPv9FV6MWV|^W~Fnbu67Tu(b!w?6&Y|D@^9tn&@i1NMzZZ9pVDFU&A`J&3#?gwIcS_ z%F0&gRSmqy%8cR%>)xVXXfQy2UYUETou^OIC4RNjV>(}t5SXg4Q2fFz_mc$zV|Ym9 zy?Yeo_R?OlfR_?Qt-BE=F1sUXHm(s>tW?;HW~3qmW3S;{*C04v0_EI|ZMine)mpCD z$)|q}ye}NHG=ns7eafmHNY^uaa=3aCB`9%r^#%~loL!?OGjwn&c9Vm*J{RSqlhAm(%C$r=$vPCy(_Df* zLu`f##~-S!Qz7d~CTVt8(T+B8-@)>rIq43d&^Osi=CazV$X-izqB)L(#^#jh)J=3x zYYy@rLQI6~fX^a<5`KR**+dFLU}!kwEcT(Y!OS*zTr#ZiSNO`FMl%T)GQ*Gf^PxBTVy)+d_^A9+3odhZ z?{Y|xN9;sC>Czqevl3mm+3EHOpkGxxesUnkQT+j4B4$Vx1&3?wnu7Ap!rYzFdo1@t zkj&4AD^#@xD_-Vi~<_+?YTvTqMF z_J!xc9Gl@femvzNK@A-8%+bN&uo)Sf6o;MOKa69E)@=yX%B(LyFv2l-ybX{9yTsrNK2e|omSIK#SI?|*m zC&)nU3g|V)Axf~M>%_%5LDa|X2JQv)5EDMhY@&46v~-k=*lTX)qdEFr>4VKc#p#BA zh5a>vSP47Pz{P|6uys6!Lkm?bzpGGJ-3xsC?fBRM!&&fmdWp|`uIzPM3VMC=gEt`$ z_l_U^uvnwspn+p$H3%NHsW!xW+2{o>n~!^pq1m%gJgIqDWjQ@Ehne(KLZx_WQx>VG z4|#n0y4QlJ`!$ObV74k(u<0G5Qn)lmAcK#wp7V6+04qqIHIV$|DF#(vl*~JhDj30} zz&YaoAu=M*V2Tm6F#}eI`-?ga-$tRD;Wb@+5)!k*0eQ|&_klYchdK(aXqllTT!o15 zp1;(KIMnSa;l7}~$573aXZKnK&7kwvniuTE3UiAKEW}!wwPLjn)lM~A2s+)KG-&ll ze>xpsGYuDRRZMqb3F8zUWW}PP{MvqB`uFuI01V2;_YY zB`Y@>WsTAb@%9fs^Fo8z1;0E zGFkqQN2}V__QS)&bS1Bjf5!BW*fVq6U;EK>D=kFPvW3+ra9N8E2wuG2_q+6(#YWOz zGf`7_(G!J{eEJrO?pyS+D%nj=P18;I3lLS`-oSk?R~P_xnRO;Y{*6?~uFpOinijdh zDR%I;y=aOcg=~yM00-$=J96-(O4|1O49_)Yw*5Y$k9*T7`=_F?zLeO8uM|I;gjXJ= zNV!dRkZ0Gk?>0JRg29gD$G~d+Z@;nW1pkSv2=bCgsS`&6k6I#C$3P6suZ=j**me~{ zsbEc@3BJiOAgDp#Q;Lcg#uhx|!F=qn63+dY79Q6jBl!NR_8HilkOo059>I(2v8w~y zcGmLBl>y4z5l!dBN}FL(Nw*luB)(HSRS{*@;Pl(S%o(0n$`EwLt|aFH>f!d~OXU0sJy`8(q`Erf>XWc8{Hf9;bvu-8-u`sWZar|_`S}&9i z9LtqR&t+q=lBO@qgx%wS?DpFiCAQsTvm71}No9=gnrlf<;CFdp@rT)lC^&Ua4XhDD z3~I#a^LYv>Vc%=P7)V!&nd?&7Q46*Pxt@+|3=oGg^?Ce|XKnk+LZJD6kqDB%pA_W~ zJ!$84G8o&;Q8i~DI_)fXa+-&rN2KU>+#eKi#k78#IO-9SBrdofxu@N&8f&0yZj7`S zK}c)=EY~9d?{6{3w>9c_?I{JLx-t)(FE~nq_v`KqCspiOfQNTUx)Ur8G_5olNH(h6 z95jyAyMnUw$lYF@D!hCyX=4qtY}E&!eM7!0o|9zUG7z6<}l zMFnl3Q7SQ?b0LWs(mIey7lct{ATGiP%c*lD`YtBVKX~>BhpSc_f>wOXG3SphRY!X) zORtL&G|8z=MDGlveZvhp4|P@k1;1xp;-tR4bzi;v>6TH@&e`s4s%a@r6RxeoOh-ot zNe>_G8ZLtl(&)hj*qa4$sz@#`R!HIUG8mb$&eyZM25cgvDzf4BY<7Sc)71d57cWC* z9#`GGrzza?473+KdVO4G^e50l_esZ{?0mw_cYha2%l4fPcW7Q?I61vhwUn&hrt;BVAUlZ=mR>Hg}ext6mXmLMY0tP7`4b8W?$ zJ2H3Oq$bmmLVUyEZv>{Ws89nTnD(iy2sarXlKZUJZh zncA~kP||bl(iU_5vys||I_%pcMsqyN^l{|y`*+h0Wlg{KZns!}{y0-PUsfr&UZ|%E zyW=I@kC^cn5>>It9s37DKR>fEtSXp|f=26Ixo^v{)7c4DKQPC0=5`ZO@p7>8w!eR4 z9q6R>_W5`kG}OszzSikmcjNV5U8laH-qF<5hm{wwD}=`c5+S%T5?|_9&kw)|V~R-H zG>;=0yjvZW95nR&Cf-^0bY9BUh1-B^QbA%!zRe{=;Oy!^Zh3M_|gddrCfH-qKJf(|FzGcVjbc0@4?ue zmYrXx`J$4O^uAEc1Es~208u+bpwoOeES+TQ_rFi9&E3f+lvkX`D~)6r!JG@m*A=8kS%GCFR@(Gnblc>=49s@!Yq>sMrY3)*LI;UWE1^@6W?6-$rble(##+o*RW zf3;gIJYf;VGnYX0LLs-6xYOhk9a>^&hPl?PvWiO?_UM4M;e)cPHx&G4U^7S<4nCIOU^~WMAYwEo? z#WRJGyFJBp!FlB(Fb2Lj?FU-}gJ!Mku)q^I+Fncx3|ph?KSN5b__MuRGj-}b-x9VW zsL>J4;6$Y7panA%u@SNkE&H*rWbFueJRaO<8&%OceEf8??dOSqOtHTWECzd}2Ru)A zlwGWoU-$;Yt4bbmUZtbI3%n=*nv-w9m4VY>*n@kFqk!)I66^&WG`{bm@V2a!`6AP* zIa%vuAucKTT<$WIwb67-GJK4Hq@ct2D$$AlP>SBq<7}cu$mAU&?9&iS5Sgpy^b5~?fiZ}Lo2a(;;Q0{u zqAvf^93V}t5sX2OKf(&8Kyk#E-9#eAbyK)uO!@Pq<)~c3{h)IQ>%vG!^j$$6;u`8@ z*^_&qc$Y`W@uE|(aQVvp=Z`vlyDYbZ+|y+2`BXj^uIt8| zBRRYS8JY@z{*2gHHwX{P{^s$(%?e;|8;+OA`eosC_}Xj;ZFy1~*ybqT%zOR;N_3t7 zW>HXG*Zcm~g;VKq491&0QRUH3v7dj6UNqc*J9B`mXnihkEH!F>4j3g;JD6GG*%jOW z!GqONl_pANy{{e{(ufFL@01TP&dXng6QQX21i&F0q2J8E?exKt zoxFK6m$}bqZ&%c8KOzbAw!ZL|s^+l;&097wmgg~nnW(+`rB7wo`UgyLH_&_Vcp@f= z6FE&!U_L(tR=vbs$?)e8t7FjIsz=b}g`j2VT4=$<#lh8# zMM;uXuc*Y6eLBnv{<0$e_VP$!k;JpNQj|BOQXc)tmvz1TJ1e^mOJ3heUYjFZ8eyPA zi2B7xSgNnKe%bXh8&xR#LP6{8%?D1y9sG zR}W}Q*B>~&!4sB!PtA4dSkX!_BKK?J0;o}=**(F<{2C40pr-rHp({*MH0iTE+J zz8F6IX-BRPM+M`#VM{H33Vba3?_S^U=X@r%IMFMpyE@5Gdj3!h!S z($t7MZ@{E+x_7hkMJMLAF*CzaeJrSQFN65PuK=w}0AW=U+_TPCu@A^Hr}R%Qz?9*q za9w&B`y~n`Zp4Z3u^G)@<9C(S)jXqV*VAbehA(I;t*HhrF|9T0cZ-)J^sPUZBX@*k zKvTQs9R>xAQ!r5G;!U?(n!zHogc@{_cy{8KDu&zgo6G4|7v0K~k+sn3V-q|C4F$3W8mbjrFr ziqb3IUeX5OdO$4MN7rL~;yl4#lmsq{$9WN6@Do;g8Ny#~ZYc2+8vx&a((vV!!)Im# zkAjfUcqI4m!_{}Z!u+tb_LSlInhh!6{*4U;$_;jv$@6K|kbF?_*s`jr33CIs;wnauVA2P3gD)*Siu9Tq53OA4N~Vr+@9hb%T>IRwI`BAz zw2o*6UQjp7E%8J!Kb)6_X-Zu-LbI9QRuM6LpgB!SJ%^6C1z*7IwkC=!7F6|F;iC3l zW6DyQz>{LsNttwyAW6caQ^b~dMUs{5U&2NAQD;gv}SZ!i+8)pSI>w^jpk zDt(J%c$hG&`lWF6O3h2*bjJuzyJc|)<{i5auRKjqT-`}2_u~3Em1^s}1G2IfHILnx zSZ;S{OjZq=3&}P(%w9NmxeU?G7itpSO@>xows?Mu!4QAZ%I4-rZXi9cUI z_@3`f`yueWi!Z3ZAIxIsAV00xG+-W#p5c~4wOU>&?;0Y~Bbul{;iPQb3 z3gU9&(-)|DI6HPWsOy%)-}D2eX&=`1!zjtsK=HMf(#eufnM}2}C9YP)amF}GWP$tC zd-CKZk-kIn(LEnK8RP442m^5g6P|_h+NI{i_kYs0U4MjtOnEo=;M)A-Y_PCVHTEm~ z@61m3r-S145KCl19pbgI;a)|Hy6dSKlxyg!cJNNUzQ>)$(}kq;ttiWw1EBv}2`&I` z>6VZ>vPcsl=1%t^XRp_d7kW=Q&g1W}43(Q360<;U3Q0!)_wcluneSN!rF6DlEmqRb zNzJqsTqhUIy|NKk~ld7?YQ*nm;<{|J0A?L1}p|`*9HB9oC0-0-Iiy4Ikw`{sE|6Mfr|6@-xf8gA# zP{;jlwt7Zl%I(bgxPQSXuKz~G`&0{;4YnJI?Yg)x!`!U*Ow)CZ6MK&W&Q559yCp@Z z;l(cQDa%2A6zzCbEJZ`a>!9L`fO+0h{FBxo?lS+ehB!y>29Ak;X0AHDhbz91^a~?? zK5RLwZU{Oxe96d(yxS-**@sTf&pYiGNdMl-%@Co5J|u2(Y`izKC|LBia$!K6(aclC z86j%<*$b|RZxAvLPKPIlJ#%b`+4O%h4pi#DT$e?PdnA>tRX7f5CX0F;Y?gK$@1la^ zD&*_0-8i(u&STaB>FM9QnF&C8q!&Lb$%>Yj1t*sBGNOqH$rC9e?g~_t|NE%d0-xs6 z!7?P96S}Yq4pNYf^|ZwaE_t31ns8>(M9xwje7WGO?|3PQRfQ$fzL8t-s)rgf$hHcN zx74bXNyL8tOzumzaxif{5A{nCM5iJ#;PdabvUsO^XA8=ot9|z14eb*v5N*&Zgo`9Q zkAA3de^}+ZBXiPQVTJ3>v!web!{dHse>5z&c5{j*OgqLm#)B1N!zV;?9!dTAY$;Pr zGtqZIYA7X4vAtO$7e&YW9#M=vGbpFWJ;9IZ2%eF zu!AI8t^7>L4}a1oF40yz)$`@zO2#1*|q(=GMY!F z-EIzIFp{F%1N`3keX^t=8NuMXYBV*}^9P7$qlSri^c|V8^@N1+JvxfG1NsyWr3Ud; zI!S01)$fupkba`P8&gAxtt|lv!YuG&Zz)ATWk|S1U<WI;UP*~Uk1QT$aWX7`%FW+e% zU>m;I-kkXI9_f|2fwg1IAwY0Al;<_96uF#!anrAyRjFr~1-|Y4awmE(L^k5}qo~O_ zif{Qmh<}co?pYBX?cbJQnQspkIbT0(W46CL^T1AypcPRR?Q2u7UlC#xd=@D$BkHJS zmi+b_V4R?|c>d!u)zFcm$5v=nQBO&vteW=Czx(mJ=-jB zy!tfUDDli8XOTT{xqSbOb8zZo7?GfyWVv1Nx5O&w()TEC(oHJ(F@LpUq!OtOHRBis#iy3%Hbl37%rx?~>7m=gA)*qX9znIaldH{W9-XOvY9slL}LKxSvj&+uzv7XC63}QLOHU|cFX*oI0 z>zuT%o+%~|z*B`FRT?$yI54?1I)kqEmok#&T=lRe91iE}s)MiZwe2xZosOZpbYwB?5f4B5CD0RI5@)hB}IRUdcj_d*5)bB-*lpq||^nA9fuhV>n;Hh-_bQRA`gOd1A4 zn7Fsu$5rw{*II&ujTib_&&Wr%5#VXaf1+V!BOG5@D#hObzhX@=i%m<5Kqwn_ z?h)}^F4`}DRehwg5tH5q-j5q5IG3e9>5qHimB-~9v!`IDY$O?F9>@N61QWaZzAc<2 zc&%IKT(o$&HZZWaNKQX=s`l!shB=4U;76~M6tT{L#KlRkx7OYk5TL4`h7|uKiq5e} z`Y0{>FgaYV85!>eBY1zF#r|%+p5{%_UbE=`X#q%LzY-Q#2L7^*{(5Z2$x7lDFivk% zk0_44f&c0YZE4RwINLzPFh)7(L3=n?H)%1h`P(p1Xu#*~S9Y%|KT%9*2+lg$a~r_%Pj!bto& zLW%f1?F$hqa*)l_3Va3w&_~ck;3Eq_-22c$0IFCNw}IwE*Z8jK_Ur{1?M<#OfEeNS z>g9(n2kL6mmc!PzHsrK?QRVlxu{&o5RT+05`|R`rRrL38PM-d>*ok>xuE&A}=#)42|g>79+5DKP_W%O}PTudzVx{A4kpRMIJ0l%^L6L`-h z>(G)d;>T(5Po$xS?vHQ1P(O{+2ga-c%TS|fFJTi~o~ukfK+?eu0sqt~L!avJygQS$ zqA7T_L0xVGaEj``-(JYivv-ji=nnF3Gx__wsWDn>gqjRGRZQmV6dMDn3zw21MJL9gfSzI6mUhyxg zgU38Tte7e|wGU?8fivJ*hjTmcFEo%r?hq(cr@<`{gPG27S0P^RpeSr@ zo1dU!{*|F(pPo=nh!&O-NKmL)O8;jF$4Uz(4EXoPGRy zC-Rnygdnn$x*>Nn5oKS}-; zk{z+ELUZ++yt=!)+2RdY9zX&7>_vD3CP^YV)FEP1%To|R!SA6K-yY>sq{H2^WBA_YjOdgBC?IZT91AHD*a|6!7=vL)jx`K>wmY8KUC$nf=V~q zmUBZ2{>YU(z~Wx`$|8(yq*acaDotJ;xC%(-e&X-Df1~(Q7sT74QG(ar`jon1IfW%B zp*`_b>>DQgEFc8m&c+SbQFhe>xpZE*^zs@%gmC-gAM-UA?NZcLV0r1N$dA)&mUwur z)xF$0D0FYRC37%e4KgA71#9Tko=x@ocn*kV{&><2FUlw--kg3Ebh=7K^BNBrlgn01 zXgum(!^F)f?m84t5*xuxJm;_R_|~Wp4ssQ-gFap5Iyyw#Um4s#`yTkLkK^DB-s5U& zPxU-JqN@R%UJI?JH7~4bPDt>f3y8vrk^nu#DI~bsM6JH;3pbX^lwP*79XPc&VPUUY z#|)Q7F+WK@XA>+YPW2vS&uQq{STfkRIfT7^s_J#j^5tmml&=KQU=aOlcPPQ$b5Zw~ zeKkwI7%Xf7goeKAdRlG{4h{pAV;F~bAk_nUHzxvXhifDC2isiJr+eswb!fU)l@c6E z7Q9AVaXE6I8>B~9R#wP1uYu-zmy~q{uOh@jhr*JSiHm;t!_0u)!-&u6=MiCTe4(6JB{<>elBb`l!siv~}peSd_B*&|hszEI}( zY)|+Wl@U5PpS^szezW>@lNkX|DmVOJL1j~IRZzLu`c)Et2qChUZtdo(WPM9rH<}Jr zv6|Ilg7lNLXm>_;ukV@eV#Y|KP#4mQG!hQu9lOsk;=WQr>1*mKHee5s{okg=hotmfZlq3Gtg_zauDszy6+Mog;0k| zIA!BTVS)oe)q~(}*dsLS{poW04L1h-@qh|cM(32HEBky)YQn(v<>7+hA3{tp>B)fA zTJ(d)Cq0)|ZOex}RFPsZy;P9VMBT6o!O67dp?az_34eYh@cJhja~@4rODT^Slp(Xp zL7IuTu$N2y6#bCa)lvO7<>>%DUDl;PN`9%pbryx21YFMTCY5^mz}^46N+fsp|7(y* z+P^Rf&LYAFk^;?f755Bi9AaFimE`!43x@R-&>ltx2ea>zJb-nAFh}{9Jo>=5m^T3E zv;In)2>|0=UM6x%lU7l9o(yw1n8aPKt{NVvwQTITsfNH-Olt^(25V4?@Yvoy-!==b zlHnWt-d+#>%4?TauXfjh6$97W+fJDlkpj=r zbHdQ;PWSDUuuxE;R));K#6(w*)(WyzPDNb|yUSu!2~8`1)$o#T6UbhE&9B!xYx9CI zba2AtQitf@s3r7Iqx&AI$|=>loSa z$vxmeQ$IKNyZb*9^gjO#1y2}c_{FWM&#zsgsEd9cMe-|#Ntdf;vo2L@> z#bBF$ii~x2rD-Co3m3HWCn85W(U%LnZwSQ_z7X*qhn^_~XCS(IG1PzPitM}BjLft? zm`~3^Cg2jnQ~(-e6RHr-cgfxJGFk!EK)5lPBOmLzhbk?p1wWJznE0Vg9UCv-{K+ms z+df!Ku|sRXfaeT=yEp5dSAd*;?(2TJmXMNR1870ptJ!M1Y)z)6vWkP)2eFQQ&_{7K zsy`JM?apN0slyQIz1tO4wD;O<)ypA_@=fv-QVe$k+>1NXM^+FaA}pRRpsu+bxCf$D z(O5c#{y$3>+O|=zc7IQ?1Cds&)WXVP++`gASIBN;Yl3U&>(ELDkBNV=qyLOr@?)kM z=dSC%>?^Myv7=V4nvFb+n6)(|y|NkX+F{C=)vKvC2uk=6x^@HmT;{5uHc9Upg1T|n zr0u938;>v1#;if-&t<#53Kj9qUEY!UT_|a+=W( z4I~%4-zi4zuA@GvdUp+dc$1p=)hF|^lSA>6S4Loa|M?y>mSp>KrIP-^)SMySS}M)m zFr>$rVm6*zZ!drK4j3&j%nd4K$P!{!DDvA-mQ>|xL8T>i{_j8VB<0fxs*Z|lu)M$r zSyswUpPYmdqR6UihW#nwQ=cpSXe+qSVsd_i(9mU%$Bx6yq+9hh)*BaP0_ppf;(!x_ zNWF!(%CF3McYf@MdwmWrOI+lqc^uxwLFG=mN224Fdzi>G_l9PI65bqKQYaV3$l=~^rm&z_ZOdc-|9v8yiF9v(&fBW>GSTJejX!S-|NATXk{MsOIU_gtZ=T(r8emcy+@yV;U|2FI06PF#v(=EtjN2;tQ!ZrvP zTgmX-rWfy=QXHWhC=&~#@-jsuzPdYS$^{&EsmOnCigR5XlJTx4}{5qaE{yjz+R?{X*%PB`KW! z0!G60R9D+hRT1pRpQy_FSt3TdUf7@ zbGU(jTkoh|dys6pAnxd$Nuk=nRnm1sFMZ;Q>@F?y-I(&(pkEjHu^I^ zN2-&DNoT1DCDyHL(~TR~gR_-|KV zn-~kqXIJr=Z}qz4W;ND&Q%m4IPIBcZ+&VjpRD}uJA5)~$=W3W#DV!g;4+RI^@nXWo z#l_ValXQ=B>gS(_)hWpgXG|NI%l;wgUzGg%RsimH8DQU+^~Ma+0*9?URj*Z)RH_L(icIMf~-E|OcguBvvT(2gB_hKubto8w!y027HIx}zF-2?#y zwqJA|;JhSQ8F|#B5X#Y>(3GzRP9bZn%~lb7i9aDps8*sYV@gkQta6mZ%+49cKiWo| z4Ko}q{UZ78@G;twktHUfsVBNFA-C_A)uh`~q_=@Jx9@woYOYQkUN55qj9fLfbbjy& ze3H2@1~x1>PqZ;*kzm~O|BCz4cqpT{|3YP7l4Q?PDr@$ANs+Ro1=&R+%}k7S#!|9H zkrt9cp(wI5wlSd^S;oE&CRxThBg;(o|C#iAp5NR5tEX2!e7f&*-}gD!xvuMcmxA8V zn!%A@Azj!zxr;pycFxsbBL^!xYrSF$*#;KlItB&?Vyhw1+FwVVq!MCdKOOoz2m8)6 z$DEY9_3HP|N_WM8;Qlhg%a@muujgN1P}kr>G!i#X+qhenEDTvmAuIWRkd;^?LVP_Q zJ_(Y!)&vmSah*@NpJv?W#q-o_Us(6c43(QnHyO-0MyjiX)WC{H){HY&(^KSAI$ED? zj-7s{Ig6hkT*9yA^WVNi+DGH$61Ow?GnuOH%TWLOAsCYAzaa$_ts}9p7ivjAxYiB!`BWwnp0b7%?g>7O_+(cAa7YeMsLh9qe{g=8wv4>G2ht+43E`BK@9nu`?G7&I@>4TWg9 zgoMPWit&)IH4b3nu%s{#@ttPQ5Ru-+NE3RqvJ#z4N~1 z!TQ72ij`~ciFMbjAwF-mP%jlq^KJWalcu#Zap^b|o8i_O85GU=`Dy+OCpP=K{O`s& zHMwQ-wYCz@8}HUNX&$6~XC1AYj%}}XznOG#ut{%;`{#uvxw7^boTx+Mn=K@+$d$0m zN}y>(l(e<*f0>3FT|lAN~rrO$o75y7dxk0dE$1MGE}Vf4xZN`BA#8EscVi8EeqIfTVQoi>WwL z&*=TI_UC4pqqR@dbN}S5{gGPlI247@4Vrm??D3IcsIhFl&;_O$BF@ZwIDbHUIaj1x z@6632@xgVgLx^r4?Ai{TtjS|l@o$jd*u8cYJ!(}le==QX>MUhFcd~JTuj5Wt(b1+` zUWp-gQh`P7FQl=u$(jOqs^;uw4kf~mqm;Q$(Bbnf2G|9*;emT0r3HickD~&Ou6*Ay z{lRw^Noba;4td9i=f5+<$i&exJ^0+aZZ3*F9kz2WquEG|?^0TlBjt_2agd`c~wsBW43t?}O^I!GGu4vJHn z0)m;WP{Bn%<9; zldcv95dE{7o`(A%LaDMCkKC3gFGDxtN*antmsX0;bCd~=NpDUzmOR#y5~du)AsfVY zaxU&{TD{w%NroK#@ks1wiwJDeH21Wg7w_X|zzVFPBR*6_W$R?CX7=H=(3w)SEq`>A zBCS8kqFJGg6N0t0Gm-jn+gbyA*C$%;zki4m(Y?|oIDeaU@?1E z`NpuZ=2}YN?)^kNvy0C=epSw6?ohzA=yThfy8?Ia@@W6(~p!Ncd0}@ zJC&kvDJ5zsYpVNzSGQL8P>h@Wn@M5ir9N2^&IH%-nc&L!=n;lH@`04)dzk zjkE6P-^_4&A&-?JwI+PNuz{$Er76G1mAiX=*k8LvMaNxyd71x1_4wAM$d%ah+mMe2 zx~Fdr8%xhJMyAjC$w}Yx*9ImdP)a5*Bo~wrAdZ3S4bJzJqc`-qfV6Fw2g?q$#h>>T`sst z9;H?>36F*q9dw9XwbG|_-XD{1*6c!#DoL1bv@QLxDG4qOzf*E>C7lrGzS%D^gf6>y ztA};6|J%pza`(ItLSByVFeP&3LbXI_re%tU>*MZ|XG{s>Hi|eMCDVCt6$y(%r|07F z%P7t1z@pX{-z6t1EFj2da0FO7QnFTL=ZufEjQB<#o*TM*0r=H;2$lc9Fg#(5s5Wz& z)9$(I*UGe)1-dxqfez^4$@WIx_unGVbCkB@v@_k*$i3U+SjuyG<*PaKX1RW8sS@cQ z*8TD{&dE0-GlRXleIt0Gi!0;E{EO^p%VY}g^6g!`1jlr0byTUio@L0*1A^UwFoov} zb?0W)^)JYyell$vESiLRl1{L3uA;btG%sseteMhoS`Z>$$@g6I+o_h{XLDTX&r=6X zf56n3@@_=syOiA;Qjhz9C}{_7MPIVt_Li)#VUl~xUl=K#276osW*baa8_RaY*805v5dP+|6@O%I3St_C;3hISz z{lt#;5rt6x_m|6lv)G|S<=Ic;j2_MGw)Tp`S6^Z`AeF1Bv+U{=r4y!EI||oW+1M@w z9Bob0pSt+s_hVpGLh#9mC*{uGf-=w8RzVRhPN`PL4PnB$GH8-^(c^g~#v*^2S)Zcw z4ZqvLeQE5ON63Tuw9Ii-Y~J0ybni1eNhm}+M*vZu{XFMY@>iux&w0xwd2i3q2!5QJ z(M28YgGDlE$)#V~5j5WU`1ir47^p`2YbpLGm!H}9PkIKKXkb^@RXE!5>**^$VJJpJ zq5RU;!dvSzXa}#u8K=y|jMh|FkWgIwP@aA&;Log(`rJ?{29m*toCCI{Zy z1>gBPr@}!xCYb2f>HQRJq&k@ZTs^n7VxN-3gyHOcyt4Wx)b*rXVf#nnPT%XmO3nwo<>e?BvqeZCX;&dB#>-deOJQcRq$61#9 zJ2Z!p3Ge2`b~9v@_1j;n>Q7&EGJFTcW%;CkkTRXPuj9fT0_{y)@k2MRn}Y~4{#pZK zw13+;=A%Qj>q@k@uE~O5Y9;0U!cPJ2J$~|YMRuTUw#=axbz4u;yZuYXgQJ0(*vY-jl1G~NSV17`SR7)eLcpuHV7|Jvi z%jBgbUf*)LCzk1>`mjEVhRQti^!qojpo&DyzRZ)o35hB2?_XP|;4f}Br+F#OHn9t= z1=NfzMOX?(fAj8AlVm=mO^amGwN#fu5v#Q9=Mo?H1l+)_4Ifz4M*5+)fO+N^>>wP} zYYlr*Ie}%`BWTS&El$-y>tWSg>jB=wlOt9ozPgRUNxXQ#bANTvZ6e21fOPNbG6CT> zG4&+L=W5_}RG36@TIf`|(AtEKBMCgJdI3Hekv(x{b@NkIzzD$~3-zaHBHJJTr|Jbqfa3q$iGcM5wg&R^F(hJPFzn`f=` zF2vcC670JJ_2PMToa4LZ!}YqQsxvZql!c_TzN2xB<8=T4@=^Vyb(8;%j8^+A`uLvU zO3D``-sUz)qi%iL(!w0z;=>;ku)iy=K?RllfA--Dvx_S6P2%S>R7*`TZ}#mIX*E4> zcwNgH&t~HJG{qP!zf)Ts{7jR|CSRK?tTiZV3K#Hlnl{;YW%I;Hv+kg;0Pb*8GW`%^ zpYNDrE^cQvsl=Dm>P5zQ6oE`@&2=x+YUOXjh*bSMfq(3jVs| zb@O#cH1GHosIV5jfiua_FY6h#@-V-2vMuRscOGj|7X{HYF*#D=)2*as_zNC5AA0-m zrEmSc^rI}=2)R$BRPC~bVC4m)lZ_sad`h~H%l5BtF&@~z{$$+V{fl=hRD+^4Ablq> zPV-8*Dj$6ny?(X8kDpH%%v$_Z3V$^s#@lHrCLrk@E=Ltg{W87)f^^K^AkCSvPMSV3 zBeA?XEUOv(=jca$JWJZz_iuB=@EjHP#1}m=u%sREVsyaepT;E}bPX@S#~lTCaG<{& zYmXlSl9d-9Eq&7FA}3ZSYf0X6CvSvc`-U`gK*5LjyX$rt3U%g&yvf!5V*72BWya?! z?A{dHxp}W9Zaf#eH$?>}?Qim3qaonl8hzyDnW?fXWn7VP9qQA(`BS z*reG^QZ=nxG}YjLhtJxa5MZDlj;#&OFV93Bm*yl~?ba5SAtl7Wg5#{~db#3XX*H=` z*3T!6u#ha$UwzCkJpg9Eg$6$dX`ry}c~3Mf3E_Isq$Fq9B=D7>pRlf-VOJ7es5`UNvH)% zv;?uu>2G-P>!iMY#4@uK+!#{Xr7nv%6_sDF`3kR7lc z-^v=&(}a9Is)=-%oyaeJtE*v2cz(aWTnd$`bg!&3Uuo^9Yb13wG5%GL@wes@BlXJ@ zEIu`j+m()+Bb(n`EY7bE?3D|To?{*eM0=*$JWPESMvU{*3_pj8UUiFx_KU97WV=lk zoSnHTFR>F8z7mu(SN()I`@scqoW0PpEonZu!}7cWO?sHOjRey=iT$L<;$O z^rM*QR^Z9V%xj0+d?2|A_Y2p)j@#$-ePv?VJZ=(l0H5FerQT%WENIAi@68pW#M`vM zcZ7G!7|%r6-I6P0=OlT|9ceFaJ;)J6pL%PTQCNy(=C)%(5H(QqZZpNV*)1(+^SR1R z+v7?nAXt?8X)Dg)WgAW3Sh$%#eLnTZ$EC~)Y~BvbxLCyBGxZFo?p52eAx9P49Pto2 z{a}0{J@-U2qfF23_U{s>M@j}TTw`Ot^skTIYy51>8P$wlzQ=F|k%YZ8ET6A_^hXwq z&3(*fU1=g-Cw1+1hS|1?jngJ1f2-7Sax-hY^iRyWRBCWx+m=$%aO-LZ{B~Dh)N<(W zu=U`y<`yFQM{kF3g=0h5qX30JTFy@EC0F!fhtB-2u64(eL*Ew;6+k<#Se!qyx6`bm zIcAwZFy~S0R#|(mLKZh=@Gw!c*L{NmEstaG9{gKuY!H??r_|s*s>Kxl7HGbPZaZE$ zU<*RYC*ML;`|QMJ*cXW#977kSgOqIVe}EQI7Wdz;4t|ngJ^S}E{|CR!61)wt%H-VB zRO+K~#NFkJ1Kve}nY3d_ZBR-5>${yg`4c#?SOSSHNnj@Q`PF!bke!d**j3)5cI2*A zM3hfCu{JX{cIVjNhxl}M!R6RJXUI>E;@clh_hXc3*g6X6vTE+_+u5%bH^G6P=|G|5 z6kxS-97A|3!4yp>%iZrUPkl&(J_kU;AK&<$Wq}t$!PaVzCHc4NiMMHL*$`k-k_=R! zOac}L#|JFnZ2;|&1I~}H+S<;LK_gW^dN1W=f-MOtx;zl}Z5Fo!hko3}DFVc6h~ez99;^#4Q-KU$FUkfwUfqCn5IW;h2mzqQFkITo>zYUD z%2#S5D%z1^EuVO~4%2ny(1h;**)*%B962fsAN3!Y4a@8xWyJjN^Ybh`Ks|phVImrw zbMPy0>bN8tzU4CtJ?9#%Ti(C`f4fLkqj3AdTq=fe!~TN5~aF7mDq|m&) zyuhpAA4LO&RqVVnXr{Ow3R;~ z^C6U~37{4MnhV8EQmU#qu*zT)-uBsh>ym@-U;xu7XvfRTTamI(caLMtWqAOY-rzv} zC~$%81Fdx+&`Iv+7ZXz$8X6)8t{{aoU%Z&e&{kKsNj$J6x*51QJQb*i(g)6Ey7(~? z$M&>VR(AH97H}IRK}~c|N==9}QPI%R;cYnvUA&vSyZcNa47mW$YM*&$X<-q79%>M! zbvY-54&+4Bxp)cP-=i2~5ez>+L@1(SS73nE7IY@%i(w_I3dA zJ6z+@;H#0T<0T#xeW2!LJqY_s;Fq)Kp)<@oEJ} zl?6oev&SWQtQ%6#v~>~ICU@{5V#K$c|$PX9{( zQYBVR?PFPoj=3u8=!`hNW+wv81*dK3Il1Vo*x%oU1U>WtZBGCI6h&!3`KPAoJI~-* zh2b4XAl3!lrG)R!^WQiBg5%zpYE5YcXX*Y#095oRGIHr0r*m;bfH-H(TcOHOpnUfp z@zGpS;mn!tcjN7ERN-g>yw)6<0jTqz=K+wXk8*4$fdZKJS=2nY=1oBH&1-5c*#?YU zzYhQ1ZQ6XLRkpF$Yuv~ZKsY)VHLYezUX(1Jr~>pdW59lqq~l5bfMOWzBF2l$SkPs& ziA%qLXvJ0U?B92TT%q(|4}6;P>~5qHwE;5Jk+0(`+N{x99@jVQaU@s&fX+R4uv{5D<^^A&^5k1Q0WcK-Q9D`Qdb6 z+Wgbl*iLv_%zoy86#H-RzTj3@#4j$y~=r=5IlL1VgaFXeo;EN>T`LF5$C`Xi!@ zq~62si+Azv2sP$Xjt-DC6Pgh>VKzxb1dne4AGV&2vsj_Fx-~%Kwne**X*Vj(og2gv zsSZArmYI{NZ1ZDTuu8)CNckZyrYrpT+c7J^nrEO$E=bV_kcPeNUGOMP#0Z~_!#g8d zS0O-p%nJM5Q*T=L83XDoRa`8JBw-S6Bw+Qsv+s`+WZkF?U1STm?R?eqH+%6G1 zptbj(feR|k*CgMB=MiNMq|$8YTcX>$et2SFR9^SxT&YT{#Y8g0e@clfJ@=J4Up5sg}BH*D*)w zGL7lzgr&qyeYfxIwDALYS9ChP-ksY!wrEb>7q>pf{E)eVIpNf6G>}rfGoRmA{5n;@ zj>GoGsJ05BH!8zh_Pa-PMVhE*NLHZ%9N(@3;ngc)PRBGm9DnH4;lZ#3mhF5Y0*d%t z(h(V6egHELP?q<@cLBI`2DZd9Gp^<;H~>S`v_}Nd8U59evZm^4{bRQuY{o0X0PFp3 zdKJ-@8$S{*YkcSEbGozV$b5Ec%A%$WODLiNkUHNy zz~ZvJ2NQ$G4n~&|nz}jIXK?{#jao+^GzHeN>2a?pGIUOeo_>Sea)_P-r@L2Mu#52T#v>$-DkQ}(3t?A z5NB{w_CTw4dXI~Su380p>WmQ+`UcOsW8oxxSo~lxBSxS!;nf82J^cjW{G&{+if|&O zmZG;fwYyN(;g`34fZyS5ehUG7EXIRy(bcbRR@w!_oW!ur&;3!SnW#R~?~Du$9~M#* zYTBC_WMgqOdI;h8n{BWP)TRiC^BF3Li#Ndh;mFrj!H#yWuJ39;)dpj$Pc#3bwoIM; zlJ#?RmQE?2Bq^E>$$(o|#wVt&AfKxQ8<|>fj;h9!{DI znQreND3oJsP-D{QBQP*g^Q?4D?c#%2SRS0Qkzbd zl>G&hb%_@Bmz|7uf-!AwZEamO8+Z}0l%7Xw<{C=X?P9P~XMT;LTDRD+@?|g(gK#>M5+f{QN zwO-sSOGidq#caW;lEcY*5xbPf> z5?)nx1BtXh?EqWt!*dE@cbu+Sr;4r{h^t9Un?!So$qT7N{RpnZcVhhEcmv2MybGD5 zXKZtN)Y_3ICoXOX5@4QksDy;?zo-~&8`+ZkMBNl0)TT%~T7XKQd(d;zh`Cc@o$goOX?c zyoXf|NMyZ<_?K4m$^*4?HxA5v|7VafG{DE|g1d7q#Xj1j3+af@ZlFgKJohoTJf=U= z{WXA{({n4t$Pekvf^cV_Z+xWzi*_^=!1BKp4j6Q;;qq7Y>@;B8(OWF zd5Aq3gyLs_m#|(Tk}P{u0zlJW0xrl6JV2FJ16KbOu)OxC0fNmuA{3z4me@AKBh_aR zNbF(&J>+dr7iEC_M)Fu9J(-Y9)RDmM?$i=P0qbF)eDstCCvE&!yh8utcL1>`jj0P? z1NOS~%-abLbZ94MvJ;aF9c$-GfV5)Q{AlUzcw(rOghcq?1p0`tN380IZ>yZ(h8e7I zDC5nWGoS4$y5Gi(4T8ML!AJrzSimj_|8hE(%s%u9KV>K52^v?iS9Ib z`+W$YC4L4(pJzbD)*?Y`?o=??z$(Zf(>e#R_iu4ox+fQagKDB(2cWO4X;-@K0?BcI z?Fb4;%0Bz6!^RxYsR+8d?e@-hA=nHJ2xue=Cc|RxDc>WmCJ=NdJV299EwX5l?KQwc<6%#9O8%H%y@Rtqlo7u>w|DFLYc1_J>>DLuWh(daKvC(ILTK&yI(dHfH74Kofz zl%2k)4Rf(_pTlr5awAIQWMvC_DP8fq;7e^pt=!1Bex-%ime#ovTVYiFaBNQAv4*Lj z2-pZC^j2q(eEj_Lp&Rrq81h2evVQ`6Z)Bhl!!CV8A-s70{8Aqr7#KGLw#$)K1Y&LO z#8W}l<=TT163hRoD$b42tBCze?^B$*DIVfyS3}%3vo6GsbSB(Fyo8+W%%%xp*MV~L z$OA4e-PLDMb$ERj91_-3?se@Wprc1ym9r2S>5QvT>N8Un-Go_A+>u;`D~lX3eJT_eSntm8Rz;Z*w{FloO#wQ zIz#5nnM>Gg-4Bk_Z!om>w)eYWLmu77HKU3~Pt?ELhnNOQWYB3@+1|>uO^+Q2NLzEU zszx)rGdf6#tVaU>z6uMRTR`BWIA_!MhE;AiJ>}_yw28WSRI2N5>QWFd-khtLg>>x0 z(?$L5)fdEPe%h!*g14XyCmK(O!4Qr6nm7R>;GU_HXa;La620^vU)e$C9F1e z;3?7Nh#_F@_=`mk({-Ucu_f(H!R#WqN_v*gsk?ebmy<|$^c_uH|3j~WCrPADN28lG zIa>|&{X*UhuWQUgf$~h}eJHu48ur1WZ3PEA`}AiAzD!y|%XaUW@M7u`>ua$D?v&)@ zX|ko6SuX_8s`IWCuTW9AuV4QxDzjyYZ$;>y*Rj<=Kbi*eEbm+gnVjgZw_Dawn!M

    ;6vu$n!k18l_kIeGNwLwX%10vBhPW-iHgQ~p`%KjL5jZ2;v+=;5T+0Ec zpMY65e-1+Zb??JKRo#7P{lxbC%wtiVpyDv~rkeIE!9AD7JTLh2UT-fBC{To@0qq>< zF5vf#!VmeJfG``Vhm+dv%#1wDs>+;I0P58Z|fN*P0cn{p53C4gmJ!%WMH4 zgL?%Q$aPTHWf!Te$Xk0fRy!8wp#YKV?Ic+|%O;&j7bTJ6C|IC~%(`K70)ry@oyzGR5TL2CHrUj+-A z62h;8nlc4((S?76cg3Jd)cgv4yood4Omx*p?mu$2kByz#x7TK}(j^`9O%T1yXoTh& zH217bR9_NQiPW!ywktLbcU^nBcuWvF7+-h;43ayJ7MYzg7NTybp`+u_u?B{*3E@&9 zjYO`1DrWOQh-|A%T4B+gZIrOc?3r93DRjbRq5lH>uokBDp*VyJf8(8j9m9p5G}b&DdE$G7Os zj71#EBYxMb z4IH80+l=jqd^_>vB-p#mY@*Rk)q#9)N}caDE(T?)TNMVNWfUB1dSMj-$|0kX@V3Xe zfJk4Y%9kSS&RqYa3%?XeqivGvi(eU|K*uIUOW~aE^s8*qI^T8~EiF45NHIS4f_d_3 z;g33V0C9o#8i-*(Hnj0pkN=r!Gf97iul7;L)r z$QOL_`8mqLLNS?MH+`qiRM0`14}rIh zRtA4ZpXnzO6o!}f-ukoBrn63L(cFQ6jIkc2%{yHzs2w57pb(+OYAu9gxC1tYQ)`zL z3ob$%TxB{dh{xZ=I7pQz`*N(%^P)S7N_n+X6SVRL#(J<|F zhID3r+L#@f8J|6H%nqxUSnd;+YH7`o zwyH!Z5-e*HX2=XoQdLF|alU&P0V_5*=7|-l=_-4=PPSc@9rW(5TP&Mz{Ow_X^P%n) z?InW23Xw5gy;CB94+Co;8{TZ$eCjp7wZ5`OKTv=3bfB_MA7QTM7_>z){pbTcQ8e^k`h_nduOf2OH^9|;oOj@ z@)m(}nmv^d8~%_i&GVm0uX^q#JkN+{=8Se*+9jek^0&^I_J1M^0EhEL9s9f2fS;^l k|31Y3(JzxC`*#`nTDi$bei=#c1OH4fSe!3B=MwS%0C{B-D*ylh literal 0 HcmV?d00001 From 90e2c485761dedc72caf5610ac25ee98ee01ca60 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 11:02:24 +0800 Subject: [PATCH 020/112] adjust catalog item name --- Environments/AKS/manifest.yaml | 2 +- Environments/APIM/manifest.yaml | 2 +- Environments/AppVNet/manifest.yaml | 2 +- Environments/ContainerApp/manifest.yaml | 2 +- Environments/OpenAISearch/manifest.yaml | 2 +- Environments/OpenAISummarization/manifest.yaml | 2 +- Environments/StaticWeb/manifest.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml index b73c1ac7..91bd0f7a 100644 --- a/Environments/AKS/manifest.yaml +++ b/Environments/AKS/manifest.yaml @@ -1,4 +1,4 @@ -name: todo-nodejs-mongo-aks +name: AKS version: 1.0.0 summary: ToDo application on AKS description: ToDo Application with a Node.js API and Azure Cosmos DB API for MongoDB on Azure Kubernetes Service (AKS) diff --git a/Environments/APIM/manifest.yaml b/Environments/APIM/manifest.yaml index 7074e2af..196afc20 100644 --- a/Environments/APIM/manifest.yaml +++ b/Environments/APIM/manifest.yaml @@ -1,4 +1,4 @@ -name: fastapi-azure-function-apim +name: APIM version: 1.0.0 summary: API Management description: Deploy a secured Azure Function with an API Management service in front diff --git a/Environments/AppVNet/manifest.yaml b/Environments/AppVNet/manifest.yaml index 2558bf93..f763e402 100644 --- a/Environments/AppVNet/manifest.yaml +++ b/Environments/AppVNet/manifest.yaml @@ -1,4 +1,4 @@ -name: msdocs-django-postgresql-sample-app +name: APPVNet version: 1.0.0 summary: App Service + VNet description: Deploy App service in VNet diff --git a/Environments/ContainerApp/manifest.yaml b/Environments/ContainerApp/manifest.yaml index e2c73f37..6d323f5e 100644 --- a/Environments/ContainerApp/manifest.yaml +++ b/Environments/ContainerApp/manifest.yaml @@ -1,4 +1,4 @@ -name: simple-flask-api-container +name: ContainerApp version: 1.0.0 summary: simple flask app with Container Apps description: Deploy flask app to Container Apps diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index 144d06d3..303f889f 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -1,4 +1,4 @@ -name: azure-search-openai-demo +name: OpenAISearch version: 1.0.0 summary: OpenAI search app environment description: Deploys an OpenAI search demo application diff --git a/Environments/OpenAISummarization/manifest.yaml b/Environments/OpenAISummarization/manifest.yaml index 522212ab..8b085e4d 100644 --- a/Environments/OpenAISummarization/manifest.yaml +++ b/Environments/OpenAISummarization/manifest.yaml @@ -1,4 +1,4 @@ -name: summarization-python-openai +name: OpenAISummarization version: 1.0.0 summary: OpenAI summarization demo description: Deploy OpenAI summarization application diff --git a/Environments/StaticWeb/manifest.yaml b/Environments/StaticWeb/manifest.yaml index ed4f2356..80d48f92 100644 --- a/Environments/StaticWeb/manifest.yaml +++ b/Environments/StaticWeb/manifest.yaml @@ -1,4 +1,4 @@ -name: todo-nodejs-mongo-swa-func +name: StaticWeb version: 1.0.0 summary: Static Web description: Deploy static web application From 68e9310a781b6b97f2fc06b999306b84b291cc6a Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 11:18:39 +0800 Subject: [PATCH 021/112] add more parameter for AKS --- Environments/AKS/README.md | 4 --- Environments/AKS/manifest.yaml | 46 +++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) delete mode 100644 Environments/AKS/README.md diff --git a/Environments/AKS/README.md b/Environments/AKS/README.md deleted file mode 100644 index 184975c1..00000000 --- a/Environments/AKS/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# AKS -This template helps the developer quickly set up the infrastructure about AKS. - -This repo is just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/todo-nodejs-mongo-aks.git) \ No newline at end of file diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml index 91bd0f7a..34765b30 100644 --- a/Environments/AKS/manifest.yaml +++ b/Environments/AKS/manifest.yaml @@ -18,4 +18,48 @@ parameters: description: 'The resource name of the AKS cluster' type: string required: false - default: '' \ No newline at end of file + default: '' + + - id: containerRegistryName + name: containerRegistryName + description: 'Name of Azure Container Registry' + type: string + required: false + default: '' + + - id: applicationInsightsName + name: applicationInsightsName + description: 'Name of Application Insights' + type: string + required: false + default: '' + + - id: cosmosAccountName + name: cosmosAccountName + description: 'Name of Azure CosmosDB account' + type: string + required: false + default: '' + + - id: cosmosDatabaseName + name: cosmosDatabaseName + description: 'Name of Azure CosmosDB database' + type: string + required: false + default: '' + + - id: keyVaultName + name: keyVaultName + description: 'Name of Azure Keyvault' + type: string + required: false + default: '' + + - id: logAnalyticsName + name: logAnalyticsName + description: 'Name of Log Analytics' + type: string + required: false + default: '' + + From 06d62b776388fb9f67d9d215851047124a0be6f3 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 11:29:32 +0800 Subject: [PATCH 022/112] add more parameter for Container App and Static Web --- Environments/ContainerApp/README.md | 33 ------------ Environments/ContainerApp/manifest.yaml | 7 +++ Environments/StaticWeb/manifest.yaml | 72 ++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 34 deletions(-) delete mode 100644 Environments/ContainerApp/README.md diff --git a/Environments/ContainerApp/README.md b/Environments/ContainerApp/README.md deleted file mode 100644 index ae7f20cc..00000000 --- a/Environments/ContainerApp/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Container App -This template helps the developer quickly set up the infrastructure about Container App. - -This repo is just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/simple-flask-api-container.git) - -## Prerequisites -* Docker -* Azure Deployment Environment has provisioned the environment - -## QuickStart -1. Clone this repo - -2. Go to the folder "src", ensure you have login to docker server. -``` -docker login -``` ->NOTE: You can go to Azure Portal and find the "Access Keys" info in Azure Container Registry which is provisioned by ADE. - -use docker build to create the image. -``` -docker build -t /simple-flask-api-container:0.0.1 . -``` - -After the build completed, use docker push to upload to container registry. -``` -docker push /simple-flask-api-container:0.0.1 -``` - -3. Go to Container Apps in Azure Portal, select the Container App provisioned, click "Containers", click "Edit and Deploy", select the container image and click "Edit". -4. Select "Azure Container Registry" for "Image Source", select "Registry", "Image" and "Image tag" and click "Save", click "Create". Wait a while to let it take effect. - -## How to verify -Go to your Container App's overview, click "Application Url" and add "/generate_name" in the URL, there will be a generated name. \ No newline at end of file diff --git a/Environments/ContainerApp/manifest.yaml b/Environments/ContainerApp/manifest.yaml index 6d323f5e..613cbad3 100644 --- a/Environments/ContainerApp/manifest.yaml +++ b/Environments/ContainerApp/manifest.yaml @@ -12,3 +12,10 @@ parameters: type: string required: false default: 'test' + + - id: principalId + name: principalId + description: 'Id of the user or app to assign application roles' + type: string + required: false + default: "" \ No newline at end of file diff --git a/Environments/StaticWeb/manifest.yaml b/Environments/StaticWeb/manifest.yaml index 80d48f92..68fec170 100644 --- a/Environments/StaticWeb/manifest.yaml +++ b/Environments/StaticWeb/manifest.yaml @@ -11,4 +11,74 @@ parameters: description: 'Name of the Environment' type: string required: false - default: 'test' \ No newline at end of file + default: 'test' + + - id: apiServiceName + name: apiServiceName + description: 'Name of API service' + type: string + required: false + default: '' + + - id: applicationInsightsName + name: applicationInsightsName + description: 'Name of Application Insights' + type: string + required: false + default: '' + + - id: appServicePlanName + name: appServicePlanName + description: 'Name of App service plan' + type: string + required: false + default: '' + + - id: cosmosAccountName + name: cosmosAccountName + description: 'Name of CosmosDB account' + type: string + required: false + default: '' + + - id: cosmosDatabaseName + name: cosmosDatabaseName + description: 'Name of CosmosDB database' + type: string + required: false + default: '' + + - id: keyVaultName + name: keyVaultName + description: 'Name of Azure Keyvault' + type: string + required: false + default: '' + + - id: logAnalyticsName + name: logAnalyticsName + description: 'Name of Azure Log Analytics' + type: string + required: false + default: '' + + - id: storageAccountName + name: storageAccountName + description: 'Name of Azure Storage Account' + type: string + required: false + default: '' + + - id: webServiceName + name: webServiceName + description: 'Name of App service' + type: string + required: false + default: '' + + - id: apimServiceName + name: apimServiceName + description: 'Name of API Management' + type: string + required: false + default: '' From 995cca0bb0abf138afad47f2d0ca0daae5c7a7e0 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 11:49:36 +0800 Subject: [PATCH 023/112] fix typo for location --- Environments/AKS/azuredeploy.json | 66 +++++++++++++++---------------- Environments/AKS/main.bicep | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index ed582901..721075bb 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "15381851435357335500" + "version": "0.16.2.56959", + "templateHash": "10187938421055250167" } }, "parameters": { @@ -19,7 +19,7 @@ }, "location": { "type": "string", - "defaultValue": "[resourceGroup().id]", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Primary location for all resources" }, @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "5858231189521761249" + "version": "0.16.2.56959", + "templateHash": "2584495159523716195" } }, "parameters": { @@ -444,8 +444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "12367137306066387435" + "version": "0.16.2.56959", + "templateHash": "12976609032349838131" } }, "parameters": { @@ -724,8 +724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "1069227136590891661" + "version": "0.16.2.56959", + "templateHash": "7351126758431238881" } }, "parameters": { @@ -786,8 +786,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14794375381139164570" + "version": "0.16.2.56959", + "templateHash": "13010360132212368737" } }, "parameters": { @@ -931,8 +931,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "5378345674008955659" + "version": "0.16.2.56959", + "templateHash": "13019144734616357723" } }, "parameters": { @@ -989,8 +989,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "18301876310392488097" + "version": "0.16.2.56959", + "templateHash": "5134893917065565335" } }, "parameters": { @@ -1104,8 +1104,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10658647303664219792" + "version": "0.16.2.56959", + "templateHash": "6363297596017193433" } }, "parameters": { @@ -1185,8 +1185,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "6962395895547155086" + "version": "0.16.2.56959", + "templateHash": "8513104519886724643" } }, "parameters": { @@ -1292,8 +1292,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "6947634037528798693" + "version": "0.16.2.56959", + "templateHash": "16707878505366027072" } }, "parameters": { @@ -1352,8 +1352,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "13620265882151180505" + "version": "0.16.2.56959", + "templateHash": "6129206474152081358" } }, "parameters": { @@ -1531,8 +1531,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "15428045515587502988" + "version": "0.16.2.56959", + "templateHash": "10766203656109589284" } }, "parameters": { @@ -1608,8 +1608,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14795379806004450628" + "version": "0.16.2.56959", + "templateHash": "6189478021468152767" } }, "parameters": { @@ -1658,8 +1658,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10301535207560739586" + "version": "0.16.2.56959", + "templateHash": "10960702046792783086" } }, "parameters": { @@ -1738,8 +1738,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11997294327656983299" + "version": "0.16.2.56959", + "templateHash": "4618248294119261512" } }, "parameters": { @@ -1800,8 +1800,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "13636254352620323826" + "version": "0.16.2.56959", + "templateHash": "11251127820136102381" } }, "parameters": { diff --git a/Environments/AKS/main.bicep b/Environments/AKS/main.bicep index 2da2a04e..a42117a0 100644 --- a/Environments/AKS/main.bicep +++ b/Environments/AKS/main.bicep @@ -5,7 +5,7 @@ param environmentName string @minLength(1) @description('Primary location for all resources') -param location string = resourceGroup().id +param location string = resourceGroup().location @description('The resource name of the AKS cluster') param clusterName string = '' From 0c636679aa65decffe318f9da3c35d62cf36d616 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 03:50:39 +0000 Subject: [PATCH 024/112] Rebuild ARM templates --- Environments/AKS/azuredeploy.json | 64 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index 721075bb..659d35d3 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10187938421055250167" + "version": "0.17.1.54307", + "templateHash": "4285256461868317407" } }, "parameters": { @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "2584495159523716195" + "version": "0.17.1.54307", + "templateHash": "5858231189521761249" } }, "parameters": { @@ -444,8 +444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "12976609032349838131" + "version": "0.17.1.54307", + "templateHash": "12367137306066387435" } }, "parameters": { @@ -724,8 +724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "7351126758431238881" + "version": "0.17.1.54307", + "templateHash": "1069227136590891661" } }, "parameters": { @@ -786,8 +786,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13010360132212368737" + "version": "0.17.1.54307", + "templateHash": "14794375381139164570" } }, "parameters": { @@ -931,8 +931,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "13019144734616357723" + "version": "0.17.1.54307", + "templateHash": "5378345674008955659" } }, "parameters": { @@ -989,8 +989,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5134893917065565335" + "version": "0.17.1.54307", + "templateHash": "18301876310392488097" } }, "parameters": { @@ -1104,8 +1104,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6363297596017193433" + "version": "0.17.1.54307", + "templateHash": "10658647303664219792" } }, "parameters": { @@ -1185,8 +1185,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "8513104519886724643" + "version": "0.17.1.54307", + "templateHash": "6962395895547155086" } }, "parameters": { @@ -1292,8 +1292,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "16707878505366027072" + "version": "0.17.1.54307", + "templateHash": "6947634037528798693" } }, "parameters": { @@ -1352,8 +1352,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6129206474152081358" + "version": "0.17.1.54307", + "templateHash": "13620265882151180505" } }, "parameters": { @@ -1531,8 +1531,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10766203656109589284" + "version": "0.17.1.54307", + "templateHash": "15428045515587502988" } }, "parameters": { @@ -1608,8 +1608,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "6189478021468152767" + "version": "0.17.1.54307", + "templateHash": "14795379806004450628" } }, "parameters": { @@ -1658,8 +1658,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "10960702046792783086" + "version": "0.17.1.54307", + "templateHash": "10301535207560739586" } }, "parameters": { @@ -1738,8 +1738,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "4618248294119261512" + "version": "0.17.1.54307", + "templateHash": "11997294327656983299" } }, "parameters": { @@ -1800,8 +1800,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "11251127820136102381" + "version": "0.17.1.54307", + "templateHash": "13636254352620323826" } }, "parameters": { From 83d3e743ca6fa232d912efedc297daff54d8ac8e Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 12:13:23 +0800 Subject: [PATCH 025/112] adjust default value --- Environments/AKS/manifest.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml index 34765b30..a24e29ec 100644 --- a/Environments/AKS/manifest.yaml +++ b/Environments/AKS/manifest.yaml @@ -8,58 +8,58 @@ templatePath: azuredeploy.json parameters: - id: environmentName name: environmentName - description: 'Name of the Environment' + description: "Name of the Environment" type: string required: false - default: 'test' + default: "test" - id: clusterName name: clusterName description: 'The resource name of the AKS cluster' type: string required: false - default: '' + default: "" - id: containerRegistryName name: containerRegistryName description: 'Name of Azure Container Registry' type: string required: false - default: '' + default: "" - id: applicationInsightsName name: applicationInsightsName description: 'Name of Application Insights' type: string required: false - default: '' + default: "" - id: cosmosAccountName name: cosmosAccountName description: 'Name of Azure CosmosDB account' type: string required: false - default: '' + default: "" - id: cosmosDatabaseName name: cosmosDatabaseName description: 'Name of Azure CosmosDB database' type: string required: false - default: '' + default: "" - id: keyVaultName name: keyVaultName description: 'Name of Azure Keyvault' type: string required: false - default: '' + default: "" - id: logAnalyticsName name: logAnalyticsName description: 'Name of Log Analytics' type: string required: false - default: '' + default: "" From fded7da483e546c96ac2a3ecc45e6aba53cd8422 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 12:30:16 +0800 Subject: [PATCH 026/112] test yaml --- Environments/AKS/manifest.yaml | 104 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml index a24e29ec..f8b3320e 100644 --- a/Environments/AKS/manifest.yaml +++ b/Environments/AKS/manifest.yaml @@ -6,60 +6,58 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: "Name of the Environment" - type: string - required: false - default: "test" +- id: "environmentName" + name: "Environment Name" + description: "Name of the Environment" + type: "string" + required: false + default: "test" - - id: clusterName - name: clusterName - description: 'The resource name of the AKS cluster' - type: string - required: false - default: "" +- id: clusterName + name: clusterName + description: 'The resource name of the AKS cluster' + type: string + required: false + default: "" - - id: containerRegistryName - name: containerRegistryName - description: 'Name of Azure Container Registry' - type: string - required: false - default: "" +- id: containerRegistryName + name: containerRegistryName + description: 'Name of Azure Container Registry' + type: string + required: false + default: "" - - id: applicationInsightsName - name: applicationInsightsName - description: 'Name of Application Insights' - type: string - required: false - default: "" - - - id: cosmosAccountName - name: cosmosAccountName - description: 'Name of Azure CosmosDB account' - type: string - required: false - default: "" - - - id: cosmosDatabaseName - name: cosmosDatabaseName - description: 'Name of Azure CosmosDB database' - type: string - required: false - default: "" - - - id: keyVaultName - name: keyVaultName - description: 'Name of Azure Keyvault' - type: string - required: false - default: "" - - - id: logAnalyticsName - name: logAnalyticsName - description: 'Name of Log Analytics' - type: string - required: false - default: "" - +- id: applicationInsightsName + name: applicationInsightsName + description: 'Name of Application Insights' + type: string + required: false + default: "" +- id: cosmosAccountName + name: cosmosAccountName + description: 'Name of Azure CosmosDB account' + type: string + required: false + default: "" + +- id: cosmosDatabaseName + name: cosmosDatabaseName + description: 'Name of Azure CosmosDB database' + type: string + required: false + default: "" + +- id: keyVaultName + name: keyVaultName + description: 'Name of Azure Keyvault' + type: string + required: false + default: "" + +- id: logAnalyticsName + name: logAnalyticsName + description: 'Name of Log Analytics' + type: string + required: false + default: "" \ No newline at end of file From 7ccef52b14f420fb058bcbb50f976867aa8ee435 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 12:37:21 +0800 Subject: [PATCH 027/112] keep parameter simple --- Environments/AKS/manifest.yaml | 57 +++------------------------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml index f8b3320e..4ad803ea 100644 --- a/Environments/AKS/manifest.yaml +++ b/Environments/AKS/manifest.yaml @@ -6,58 +6,9 @@ runner: ARM templatePath: azuredeploy.json parameters: -- id: "environmentName" - name: "Environment Name" - description: "Name of the Environment" - type: "string" - required: false - default: "test" - -- id: clusterName - name: clusterName - description: 'The resource name of the AKS cluster' - type: string - required: false - default: "" - -- id: containerRegistryName - name: containerRegistryName - description: 'Name of Azure Container Registry' - type: string - required: false - default: "" - -- id: applicationInsightsName - name: applicationInsightsName - description: 'Name of Application Insights' - type: string - required: false - default: "" - -- id: cosmosAccountName - name: cosmosAccountName - description: 'Name of Azure CosmosDB account' - type: string - required: false - default: "" - -- id: cosmosDatabaseName - name: cosmosDatabaseName - description: 'Name of Azure CosmosDB database' - type: string - required: false - default: "" - -- id: keyVaultName - name: keyVaultName - description: 'Name of Azure Keyvault' - type: string - required: false - default: "" - -- id: logAnalyticsName - name: logAnalyticsName - description: 'Name of Log Analytics' +- id: environmentName + name: environmentName + description: 'Name of the Environment' type: string required: false - default: "" \ No newline at end of file + default: 'test' From e31493ded4cea9c748da3c140f6805211bd6d3d5 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 12:47:48 +0800 Subject: [PATCH 028/112] change parameter --- Environments/AKS/main.bicep | 2 +- Environments/AKS/manifest.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Environments/AKS/main.bicep b/Environments/AKS/main.bicep index a42117a0..e27309bf 100644 --- a/Environments/AKS/main.bicep +++ b/Environments/AKS/main.bicep @@ -1,7 +1,7 @@ @minLength(1) @maxLength(64) @description('Name of the the environment which is used to generate a short unique hash used in all resources.') -param environmentName string +param environmentName string = 'test' @minLength(1) @description('Primary location for all resources') diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml index 4ad803ea..2b2bc6f3 100644 --- a/Environments/AKS/manifest.yaml +++ b/Environments/AKS/manifest.yaml @@ -6,9 +6,9 @@ runner: ARM templatePath: azuredeploy.json parameters: -- id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" required: false - default: 'test' + default: "test" \ No newline at end of file From a356dbf08031399720ca6dc1f3f5882703b93891 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 04:49:05 +0000 Subject: [PATCH 029/112] Rebuild ARM templates --- Environments/AKS/azuredeploy.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index 659d35d3..d718baeb 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -5,12 +5,13 @@ "_generator": { "name": "bicep", "version": "0.17.1.54307", - "templateHash": "4285256461868317407" + "templateHash": "4959868895440606389" } }, "parameters": { "environmentName": { "type": "string", + "defaultValue": "test", "metadata": { "description": "Name of the the environment which is used to generate a short unique hash used in all resources." }, From bd1e58266088ba3cb1273aba87aa5eb759ba61e6 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 13:12:03 +0800 Subject: [PATCH 030/112] refine yaml for apim --- Environments/APIM/manifest.yaml | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Environments/APIM/manifest.yaml b/Environments/APIM/manifest.yaml index 196afc20..9e4ae541 100644 --- a/Environments/APIM/manifest.yaml +++ b/Environments/APIM/manifest.yaml @@ -6,21 +6,21 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string - required: false - default: 'test' +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" + required: false + default: "test" - - id: publisherEmail - name: publisherEmail - description: 'Eamil of the publisher' - type: string - required: true - - - id: publisherName - name: publisherName - description: 'Name of the publisher' - type: string - required: true \ No newline at end of file +- id: "publisherEmail" + name: "publisherEmail" + description: "Eamil of the publisher" + type: "string" + required: true + +- id: "publisherName" + name: "publisherName" + description: "Name of the publisher" + type: "string" + required: true \ No newline at end of file From e822b8f63d05871600ee02ce33711c6a82af9156 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 13:29:32 +0800 Subject: [PATCH 031/112] refine yaml --- Environments/AppVNet/manifest.yaml | 32 ++--- Environments/ContainerApp/manifest.yaml | 24 ++-- Environments/OpenAISearch/manifest.yaml | 70 +++++----- .../OpenAISummarization/manifest.yaml | 58 ++++---- Environments/StaticWeb/manifest.yaml | 132 +++++++++--------- 5 files changed, 158 insertions(+), 158 deletions(-) diff --git a/Environments/AppVNet/manifest.yaml b/Environments/AppVNet/manifest.yaml index f763e402..46809bed 100644 --- a/Environments/AppVNet/manifest.yaml +++ b/Environments/AppVNet/manifest.yaml @@ -6,21 +6,21 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string - required: false - default: 'test' +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" + required: false + default: "test" - - id: databasePassword - name: databasePassword - description: 'Password of PostgreSQL' - type: string - required: true +- id: "databasePassword" + name: "databasePassword" + description: "Password of PostgreSQL" + type: "string" + required: true - - id: secretKey - name: secretKey - description: 'Django SECRET_KEY for securing signed data' - type: string - required: true \ No newline at end of file +- id: "secretKey" + name: "secretKey" + description: "Django SECRET_KEY for securing signed data" + type: "string" + required: true \ No newline at end of file diff --git a/Environments/ContainerApp/manifest.yaml b/Environments/ContainerApp/manifest.yaml index 613cbad3..c07d3524 100644 --- a/Environments/ContainerApp/manifest.yaml +++ b/Environments/ContainerApp/manifest.yaml @@ -6,16 +6,16 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string - required: false - default: 'test' +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" + required: false + default: "test" - - id: principalId - name: principalId - description: 'Id of the user or app to assign application roles' - type: string - required: false - default: "" \ No newline at end of file +- id: "principalId" + name: "principalId" + description: "Id of the user or app to assign application roles" + type: "string" + required: false + default: "" \ No newline at end of file diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index 303f889f..6322e2cb 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -6,43 +6,43 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string - required: false - default: 'test' +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" + required: false + default: "test" - - id: principalId - name: principalId - description: 'Id of the user or app to assign application roles' - type: string - required: true +- id: "principalId" + name: "principalId" + description: "Id of the user or app to assign application roles" + type: "string" + required: true - - id: backendServiceName - name: backendServiceName - description: 'Name of app service' - type: string - required: false - default: "" +- id: "backendServiceName" + name: "backendServiceName" + description: "Name of app service" + type: "string" + required: false + default: "" - - id: storageAccountName - name: storageAccountName - description: 'Name of storage account' - type: string - required: false - default: "" +- id: "storageAccountName" + name: "storageAccountName" + description: "Name of storage account" + type: "string" + required: false + default: "" - - id: searchServiceName - name: searchServiceName - description: 'Name of search service' - type: string - required: false - default: "" +- id: "searchServiceName" + name: "searchServiceName" + description: "Name of search service" + type: "string" + required: false + default: "" - - id: formRecognizerServiceName - name: formRecognizerServiceName - description: 'Name of form recognizer service' - type: string - required: false - default: "" \ No newline at end of file +- id: "formRecognizerServiceName" + name: "formRecognizerServiceName" + description: "Name of form recognizer service" + type: "string" + required: false + default: "" \ No newline at end of file diff --git a/Environments/OpenAISummarization/manifest.yaml b/Environments/OpenAISummarization/manifest.yaml index 8b085e4d..2d66772e 100644 --- a/Environments/OpenAISummarization/manifest.yaml +++ b/Environments/OpenAISummarization/manifest.yaml @@ -6,36 +6,36 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string - required: false - default: 'test' +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" + required: false + default: "test" - - id: principalId - name: principalId - description: 'Id of the user or app to assign application roles' - type: string - required: true +- id: "principalId" + name: "principalId" + description: "Id of the user or app to assign application roles" + type: "string" + required: true - - id: openAiAccountName - name: openAiAccountName - description: 'Name of Open AI account' - type: string - required: false - default: "" +- id: "openAiAccountName" + name: "openAiAccountName" + description: "Name of Open AI account" + type: "string" + required: false + default: "" - - id: storageAccountName - name: storageAccountName - description: 'Name of storage account' - type: string - required: false - default: "" +- id: "storageAccountName" + name: "storageAccountName" + description: "Name of storage account" + type: "string" + required: false + default: "" - - id: searchServicesName - name: searchServicesName - description: 'Name of search service' - type: string - required: false - default: "" \ No newline at end of file +- id: "searchServicesName" + name: "searchServicesName" + description: "Name of search service" + type: "string" + required: false + default: "" \ No newline at end of file diff --git a/Environments/StaticWeb/manifest.yaml b/Environments/StaticWeb/manifest.yaml index 68fec170..9a16b842 100644 --- a/Environments/StaticWeb/manifest.yaml +++ b/Environments/StaticWeb/manifest.yaml @@ -6,79 +6,79 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string - required: false - default: 'test' +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" + required: false + default: "test" - - id: apiServiceName - name: apiServiceName - description: 'Name of API service' - type: string - required: false - default: '' +- id: "apiServiceName" + name: "apiServiceName" + description: "Name of API service" + type: "string" + required: false + default: "" - - id: applicationInsightsName - name: applicationInsightsName - description: 'Name of Application Insights' - type: string - required: false - default: '' +- id: "applicationInsightsName" + name: "applicationInsightsName" + description: "Name of Application Insights" + type: "string" + required: false + default: "" - - id: appServicePlanName - name: appServicePlanName - description: 'Name of App service plan' - type: string - required: false - default: '' +- id: "appServicePlanName" + name: "appServicePlanName" + description: "Name of App service plan" + type: "string" + required: false + default: "" - - id: cosmosAccountName - name: cosmosAccountName - description: 'Name of CosmosDB account' - type: string - required: false - default: '' +- id: "cosmosAccountName" + name: "cosmosAccountName" + description: "Name of CosmosDB account" + type: "string" + required: false + default: "" - - id: cosmosDatabaseName - name: cosmosDatabaseName - description: 'Name of CosmosDB database' - type: string - required: false - default: '' +- id: "cosmosDatabaseName" + name: "cosmosDatabaseName" + description: "Name of CosmosDB database" + type: "string" + required: false + default: "" - - id: keyVaultName - name: keyVaultName - description: 'Name of Azure Keyvault' - type: string - required: false - default: '' +- id: "keyVaultName" + name: "keyVaultName" + description: "Name of Azure Keyvault" + type: "string" + required: false + default: "" - - id: logAnalyticsName - name: logAnalyticsName - description: 'Name of Azure Log Analytics' - type: string - required: false - default: '' +- id: "logAnalyticsName" + name: "logAnalyticsName" + description: "Name of Azure Log Analytics" + type: "string" + required: false + default: "" - - id: storageAccountName - name: storageAccountName - description: 'Name of Azure Storage Account' - type: string - required: false - default: '' +- id: "storageAccountName" + name: "storageAccountName" + description: "Name of Azure Storage Account" + type: "string" + required: false + default: "" - - id: webServiceName - name: webServiceName - description: 'Name of App service' - type: string - required: false - default: '' +- id: "webServiceName" + name: "webServiceName" + description: "Name of App service" + type: "string" + required: false + default: "" - - id: apimServiceName - name: apimServiceName - description: 'Name of API Management' - type: string - required: false - default: '' +- id: "apimServiceName" + name: "apimServiceName" + description: "Name of API Management" + type: "string" + required: false + default: "" \ No newline at end of file From 7a8594d9ade6c3926d86b2af763d2988e971b7c3 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 14:32:12 +0800 Subject: [PATCH 032/112] refine yaml for spring --- Environments/Spring/manifest.yaml | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Environments/Spring/manifest.yaml b/Environments/Spring/manifest.yaml index 05e4d884..953ebbf8 100644 --- a/Environments/Spring/manifest.yaml +++ b/Environments/Spring/manifest.yaml @@ -1,4 +1,4 @@ -name: spring-petclinic-java-mysql +name: Spring version: 1.0.0 summary: Spring Petclinic description: Deploy Spring Petlinic Java app with MySQL @@ -6,16 +6,15 @@ runner: ARM templatePath: azuredeploy.json parameters: - - id: environmentName - name: environmentName - description: 'Name of the Environment' - type: string - required: false - default: 'test' - - - id: mySqlServerAdminPassword - name: mySqlServerAdminPassword - description: 'MySQL Admin Password' - type: string - required: true +- id: "environmentName" + name: "environmentName" + description: "Name of the Environment" + type: "string" + required: false + default: "test" +- id: "mySqlServerAdminPassword" + name: "mySqlServerAdminPassword" + description: "MySQL Admin Password" + type: "string" + required: true \ No newline at end of file From 4b0435c409b12e3221a9ad52b064a44580f40544 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 15:48:11 +0800 Subject: [PATCH 033/112] keep consistency yaml for openaisearch --- Environments/OpenAISearch/azuredeploy.json | 65 +++++++++++----------- Environments/OpenAISearch/main.bicep | 2 +- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index fad08eb4..e96cca85 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17837155476675764237" + "version": "0.16.2.56959", + "templateHash": "352231860580581119" } }, "parameters": { @@ -103,7 +103,6 @@ }, "principalId": { "type": "string", - "defaultValue": "", "metadata": { "description": "Id of the user or app to assign application roles" } @@ -285,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "8694592921587637446" + "version": "0.16.2.56959", + "templateHash": "1837336361730027989" } }, "parameters": { @@ -390,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3317056714714509123" + "version": "0.16.2.56959", + "templateHash": "935190995104945414" } }, "parameters": { @@ -650,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11076527127870690555" + "version": "0.16.2.56959", + "templateHash": "15292359723056804408" } }, "parameters": { @@ -772,8 +771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11076527127870690555" + "version": "0.16.2.56959", + "templateHash": "15292359723056804408" } }, "parameters": { @@ -901,8 +900,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2612000844202476215" + "version": "0.16.2.56959", + "templateHash": "11912420961517980450" } }, "parameters": { @@ -1025,8 +1024,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17856846537035111526" + "version": "0.16.2.56959", + "templateHash": "14234446244824387754" } }, "parameters": { @@ -1197,8 +1196,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1261,8 +1260,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1325,8 +1324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1389,8 +1388,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1453,8 +1452,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1517,8 +1516,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1581,8 +1580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1648,8 +1647,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { @@ -1715,8 +1714,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.16.2.56959", + "templateHash": "5435237477105157257" } }, "parameters": { diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep index 37a430ca..ab44b177 100644 --- a/Environments/OpenAISearch/main.bicep +++ b/Environments/OpenAISearch/main.bicep @@ -35,7 +35,7 @@ param chatGptDeploymentName string = 'chat' param chatGptModelName string = 'gpt-35-turbo' @description('Id of the user or app to assign application roles') -param principalId string = '' +param principalId string var abbrs = loadJsonContent('abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) From b2a5ccb2c14ca558d7da881bad57ae9ffe74ae4d Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 07:49:22 +0000 Subject: [PATCH 034/112] Rebuild ARM templates --- Environments/OpenAISearch/azuredeploy.json | 64 +++++++++++----------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index e96cca85..e101c8d8 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "352231860580581119" + "version": "0.17.1.54307", + "templateHash": "8522222895600300721" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "1837336361730027989" + "version": "0.17.1.54307", + "templateHash": "8694592921587637446" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "935190995104945414" + "version": "0.17.1.54307", + "templateHash": "3317056714714509123" } }, "parameters": { @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "15292359723056804408" + "version": "0.17.1.54307", + "templateHash": "11076527127870690555" } }, "parameters": { @@ -771,8 +771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "15292359723056804408" + "version": "0.17.1.54307", + "templateHash": "11076527127870690555" } }, "parameters": { @@ -900,8 +900,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "11912420961517980450" + "version": "0.17.1.54307", + "templateHash": "2612000844202476215" } }, "parameters": { @@ -1024,8 +1024,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "14234446244824387754" + "version": "0.17.1.54307", + "templateHash": "17856846537035111526" } }, "parameters": { @@ -1196,8 +1196,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1260,8 +1260,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1324,8 +1324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1388,8 +1388,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1452,8 +1452,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1516,8 +1516,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1580,8 +1580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1647,8 +1647,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { @@ -1714,8 +1714,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.16.2.56959", - "templateHash": "5435237477105157257" + "version": "0.17.1.54307", + "templateHash": "2525068256711044961" } }, "parameters": { From 101f6e68a1d9b29aeb82e5582c160588cb4006c0 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 15:52:08 +0800 Subject: [PATCH 035/112] refine readme --- Environments/OpenAISearch/README.md | 2 +- Environments/OpenAISummarization/README.md | 4 +- Environments/Spring/README.md | 75 ---------------------- 3 files changed, 3 insertions(+), 78 deletions(-) delete mode 100644 Environments/Spring/README.md diff --git a/Environments/OpenAISearch/README.md b/Environments/OpenAISearch/README.md index cef82527..c00d0bd2 100644 --- a/Environments/OpenAISearch/README.md +++ b/Environments/OpenAISearch/README.md @@ -3,4 +3,4 @@ This template helps the developer quickly set up the infrastructure about OpenAI ![Architecture](assets/OpenAISearchArchitecture.png) ->NOTE: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find "Object ID". \ No newline at end of file +>NOTE: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and find "Object ID". \ No newline at end of file diff --git a/Environments/OpenAISummarization/README.md b/Environments/OpenAISummarization/README.md index af53f4d7..05989303 100644 --- a/Environments/OpenAISummarization/README.md +++ b/Environments/OpenAISummarization/README.md @@ -3,6 +3,6 @@ This repository contains a Python Notebook that shows you how easy it is to depl ![Architecture](assets/SummarizationArchitecture.png) ->NOTE1: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and will find "Object ID". +>NOTE1: "principal id" is the id of the user or app to assign application roles. If you do not know your user id, you can go to "Azure Active Directory" -> "Users" to search your user and find "Object ID". > ->NOTE2: Model "text-davinci-001", "text-search-curie-doc-001" and "text-search-curie-query-001" in this demo used are only located in South Central US and West Europe. Pleasel provision in the two regions or you can modify the models accordingly. +>NOTE2: Model "text-davinci-001", "text-search-curie-doc-001" and "text-search-curie-query-001" in this demo used are only located in South Central US and West Europe. Please provision in the two regions or you can modify the models accordingly. diff --git a/Environments/Spring/README.md b/Environments/Spring/README.md deleted file mode 100644 index 861e65cc..00000000 --- a/Environments/Spring/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Spring Petclinic -This template helps the developer quickly set up the infrastructure about Spring Petclinic for Java. - -This repo is just infrastructure part. If you want to deploy application and data, please refer to [repo](https://github.com/luxu-ms/spring-petclinic-java-mysql.git) - -## Prerequisites -* Java 17 or later -* Maven -* [VSCode](https://code.visualstudio.com/) and its extension "Azure Tools" -* Azure Deployment Environment has provisioned the environment - -## QuickStart -1, Clone this [repo](https://github.com/luxu-ms/spring-petclinic-java-mysql.git) -2, Use VS Code to open the cloned folder -3, Right click the blank space in the VS Code Explorer and select "Deploy to Web App..." -4, Select subscription and App Service provisioned - -## How to verify -Go to the App service overview in Azure Portal, click the Default domain, there will be petclinic page. - -## Run application locally - -> NOTE: Azure Database for MySQL flexible servers don't allow connections from local machines by default for security. -> You must add current IP address of your local machine to the firewall rules in [Azure Portal](https://ms.portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.DBforMySQL%2Fservers) before running the application locally. - -![add firewall rule to allow local connections](readme.assests/add_mysql_firewall_rule.png) - -### VSCode -You can just launch the predefined `Debug PetClinic` configuration to run the application locally if you are using VSCode. - -![run application locally in VSCode](readme.assests/run_locally_vscode.png) - -### IntelliJ IDEA -You need to pass the environment variables below to the application first. Values of these -environment variables are all available in the `.azure/${Environment-Name}/.env` file if `azd provision` or `azd up ...` completes successfully. - -![setup environment variables in IntelliJ IDEA](readme.assests/run_locally_intellij.png) - -```properties -# activate `azure` and `mysql` spring profiles -SPRING_PROFILES_ACTIVE=azure,mysql -# Azure Application Insights connection string, for monitoring and logging -APPLICATIONINSIGHTS_CONNECTION_STRING=... -# Azure Key Vault endpoint, where the MySQL user password (${MYSQL_PASS}) is stored -AZURE_KEY_VAULT_ENDPOINT=... -# Azure Database for MySQL server jdbc url -MYSQL_URL=... -# Azure Database for MySQL server user name -MYSQL_USER=... -``` - -## Security - -### Roles - -This template creates a [managed identity](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) -for your app inside your Azure Active Directory tenant, and it is used to authenticate your app with Azure and other services -that support Azure AD authentication like Key Vault via access policies. You will see principalId referenced in the infrastructure -as code files, that refers to the id of the currently logged in Azure CLI user, which will be granted access policies and permissions -to run the application locally. To view your managed identity in the Azure Portal, follow these -[steps](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-view-managed-identity-service-principal-portal). - -### Key Vault - -This template uses [Azure Key Vault](https://docs.microsoft.com/azure/key-vault/general/overview) to securely store user password -for the provisioned Azure Database for MySQL flexible server. Key Vault is a cloud service for securely storing and accessing secrets -(API keys, passwords, certificates, cryptographic keys) and makes it simple to give other Azure services access to them. As you -continue developing your solution, you may add as many secrets to your Key Vault as you require. - - -## Credits - -This Spring microservices sample is forked from -[Azure-Samples/spring-petclinic-java-mysql](https://github.com/Azure-Samples/spring-petclinic-java-mysql). - From 83bada5bcb884b9cfd6310f55375e882f1a118c6 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 17:03:45 +0800 Subject: [PATCH 036/112] use resource group id token in OpenAISearch --- Environments/OpenAISearch/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep index ab44b177..9d79a7fa 100644 --- a/Environments/OpenAISearch/main.bicep +++ b/Environments/OpenAISearch/main.bicep @@ -38,7 +38,7 @@ param chatGptModelName string = 'gpt-35-turbo' param principalId string var abbrs = loadJsonContent('abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var resourceToken = toLower(uniqueString(resourceGroup().id, location)) var tags = { 'azd-env-name': environmentName } // Create an App Service Plan to group applications under the same payment plan and SKU From 1f6c87272023e5c14f159b299467c13f6602eb8a Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 12 May 2023 09:04:51 +0000 Subject: [PATCH 037/112] Rebuild ARM templates --- Environments/OpenAISearch/azuredeploy.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index e101c8d8..f1fc7242 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.17.1.54307", - "templateHash": "8522222895600300721" + "templateHash": "2428452766374925564" } }, "parameters": { @@ -245,7 +245,7 @@ "webStaticSites": "stapp-" }, "abbrs": "[variables('$fxv#0')]", - "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", "tags": { "azd-env-name": "[parameters('environmentName')]" } From f35f26d5564b1243fc1cb931a0c6544e2c847d9d Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 17:49:11 +0800 Subject: [PATCH 038/112] set proper location due to region limit --- Environments/OpenAISummarization/manifest.yaml | 7 +++++++ Environments/StaticWeb/manifest.yaml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/Environments/OpenAISummarization/manifest.yaml b/Environments/OpenAISummarization/manifest.yaml index 2d66772e..d648fbbe 100644 --- a/Environments/OpenAISummarization/manifest.yaml +++ b/Environments/OpenAISummarization/manifest.yaml @@ -13,6 +13,13 @@ parameters: required: false default: "test" +- id: "location" + name: "location" + description: "location or region" + type: "string" + required: false + default: "westeurope" + - id: "principalId" name: "principalId" description: "Id of the user or app to assign application roles" diff --git a/Environments/StaticWeb/manifest.yaml b/Environments/StaticWeb/manifest.yaml index 9a16b842..66d9ab1e 100644 --- a/Environments/StaticWeb/manifest.yaml +++ b/Environments/StaticWeb/manifest.yaml @@ -13,6 +13,13 @@ parameters: required: false default: "test" +- id: "location" + name: "location" + description: "location or region" + type: "string" + required: false + default: "eastus2" + - id: "apiServiceName" name: "apiServiceName" description: "Name of API service" From 306bff638717993655f26573ca1aa28f199866c5 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 18:04:28 +0800 Subject: [PATCH 039/112] add readme for static web --- Environments/StaticWeb/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Environments/StaticWeb/README.md diff --git a/Environments/StaticWeb/README.md b/Environments/StaticWeb/README.md new file mode 100644 index 00000000..1e605d68 --- /dev/null +++ b/Environments/StaticWeb/README.md @@ -0,0 +1,4 @@ +# Static Web +This template is to provision for React Web App + Functions with Node.js API and MongoDB on Azure + +>Note: Static Web available regions are West US2 (westus2), Central US (centralus), East US2 (eastus2), West Europe (westeurope) and East Asia (eastasia). \ No newline at end of file From 9da9be64bdec05bd60af23390efc3f6a5172ce61 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 12 May 2023 18:55:30 +0800 Subject: [PATCH 040/112] remove too many parameters --- Environments/StaticWeb/manifest.yaml | 72 +--------------------------- 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/Environments/StaticWeb/manifest.yaml b/Environments/StaticWeb/manifest.yaml index 66d9ab1e..330bf8ca 100644 --- a/Environments/StaticWeb/manifest.yaml +++ b/Environments/StaticWeb/manifest.yaml @@ -18,74 +18,4 @@ parameters: description: "location or region" type: "string" required: false - default: "eastus2" - -- id: "apiServiceName" - name: "apiServiceName" - description: "Name of API service" - type: "string" - required: false - default: "" - -- id: "applicationInsightsName" - name: "applicationInsightsName" - description: "Name of Application Insights" - type: "string" - required: false - default: "" - -- id: "appServicePlanName" - name: "appServicePlanName" - description: "Name of App service plan" - type: "string" - required: false - default: "" - -- id: "cosmosAccountName" - name: "cosmosAccountName" - description: "Name of CosmosDB account" - type: "string" - required: false - default: "" - -- id: "cosmosDatabaseName" - name: "cosmosDatabaseName" - description: "Name of CosmosDB database" - type: "string" - required: false - default: "" - -- id: "keyVaultName" - name: "keyVaultName" - description: "Name of Azure Keyvault" - type: "string" - required: false - default: "" - -- id: "logAnalyticsName" - name: "logAnalyticsName" - description: "Name of Azure Log Analytics" - type: "string" - required: false - default: "" - -- id: "storageAccountName" - name: "storageAccountName" - description: "Name of Azure Storage Account" - type: "string" - required: false - default: "" - -- id: "webServiceName" - name: "webServiceName" - description: "Name of App service" - type: "string" - required: false - default: "" - -- id: "apimServiceName" - name: "apimServiceName" - description: "Name of API Management" - type: "string" - required: false - default: "" \ No newline at end of file + default: "eastus2" \ No newline at end of file From 3f23184c4c3143761f6995b03fc2ed3f89a4da13 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 15 May 2023 10:04:42 +0800 Subject: [PATCH 041/112] optimize spring with asa --- Environments/Spring/abbreviations.json | 135 - Environments/Spring/app/app.bicep | 47 - Environments/Spring/azuredeploy.json | 2747 ++--------------- .../Spring/core/database/mysql/mysql-db.bicep | 50 - .../core/database/mysql/mysql-server.bicep | 121 - .../Spring/core/host/appservice.bicep | 97 - .../Spring/core/host/appserviceplan.bicep | 20 - .../Spring/core/host/springapps.bicep | 92 + .../applicationinsights-dashboard.bicep | 1235 -------- .../core/monitor/applicationinsights.bicep | 30 - .../Spring/core/monitor/loganalytics.bicep | 21 - .../Spring/core/monitor/monitoring.bicep | 31 - Environments/Spring/core/network/vnet.bicep | 72 + Environments/Spring/main.bicep | 150 +- Environments/Spring/manifest.yaml | 62 +- 15 files changed, 516 insertions(+), 4394 deletions(-) delete mode 100644 Environments/Spring/abbreviations.json delete mode 100644 Environments/Spring/app/app.bicep delete mode 100644 Environments/Spring/core/database/mysql/mysql-db.bicep delete mode 100644 Environments/Spring/core/database/mysql/mysql-server.bicep delete mode 100644 Environments/Spring/core/host/appservice.bicep delete mode 100644 Environments/Spring/core/host/appserviceplan.bicep create mode 100644 Environments/Spring/core/host/springapps.bicep delete mode 100644 Environments/Spring/core/monitor/applicationinsights-dashboard.bicep delete mode 100644 Environments/Spring/core/monitor/applicationinsights.bicep delete mode 100644 Environments/Spring/core/monitor/loganalytics.bicep delete mode 100644 Environments/Spring/core/monitor/monitoring.bicep create mode 100644 Environments/Spring/core/network/vnet.bicep diff --git a/Environments/Spring/abbreviations.json b/Environments/Spring/abbreviations.json deleted file mode 100644 index a4fc9dfe..00000000 --- a/Environments/Spring/abbreviations.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "analysisServicesServers": "as", - "apiManagementService": "apim-", - "appConfigurationConfigurationStores": "appcs-", - "appManagedEnvironments": "cae-", - "appContainerApps": "ca-", - "authorizationPolicyDefinitions": "policy-", - "automationAutomationAccounts": "aa-", - "blueprintBlueprints": "bp-", - "blueprintBlueprintsArtifacts": "bpa-", - "cacheRedis": "redis-", - "cdnProfiles": "cdnp-", - "cdnProfilesEndpoints": "cdne-", - "cognitiveServicesAccounts": "cog-", - "cognitiveServicesFormRecognizer": "cog-fr-", - "cognitiveServicesTextAnalytics": "cog-ta-", - "computeAvailabilitySets": "avail-", - "computeCloudServices": "cld-", - "computeDiskEncryptionSets": "des", - "computeDisks": "disk", - "computeDisksOs": "osdisk", - "computeGalleries": "gal", - "computeSnapshots": "snap-", - "computeVirtualMachines": "vm", - "computeVirtualMachineScaleSets": "vmss-", - "containerInstanceContainerGroups": "ci", - "containerRegistryRegistries": "cr", - "containerServiceManagedClusters": "aks-", - "databricksWorkspaces": "dbw-", - "dataFactoryFactories": "adf-", - "dataLakeAnalyticsAccounts": "dla", - "dataLakeStoreAccounts": "dls", - "dataMigrationServices": "dms-", - "dBforMySQLServers": "mysql-", - "dBforPostgreSQLServers": "psql-", - "devicesIotHubs": "iot-", - "devicesProvisioningServices": "provs-", - "devicesProvisioningServicesCertificates": "pcert-", - "documentDBDatabaseAccounts": "cosmos-", - "eventGridDomains": "evgd-", - "eventGridDomainsTopics": "evgt-", - "eventGridEventSubscriptions": "evgs-", - "eventHubNamespaces": "evhns-", - "eventHubNamespacesEventHubs": "evh-", - "hdInsightClustersHadoop": "hadoop-", - "hdInsightClustersHbase": "hbase-", - "hdInsightClustersKafka": "kafka-", - "hdInsightClustersMl": "mls-", - "hdInsightClustersSpark": "spark-", - "hdInsightClustersStorm": "storm-", - "hybridComputeMachines": "arcs-", - "insightsActionGroups": "ag-", - "insightsComponents": "appi-", - "keyVaultVaults": "kv-", - "kubernetesConnectedClusters": "arck", - "kustoClusters": "dec", - "kustoClustersDatabases": "dedb", - "logicIntegrationAccounts": "ia-", - "logicWorkflows": "logic-", - "machineLearningServicesWorkspaces": "mlw-", - "managedIdentityUserAssignedIdentities": "id-", - "managementManagementGroups": "mg-", - "migrateAssessmentProjects": "migr-", - "networkApplicationGateways": "agw-", - "networkApplicationSecurityGroups": "asg-", - "networkAzureFirewalls": "afw-", - "networkBastionHosts": "bas-", - "networkConnections": "con-", - "networkDnsZones": "dnsz-", - "networkExpressRouteCircuits": "erc-", - "networkFirewallPolicies": "afwp-", - "networkFirewallPoliciesWebApplication": "waf", - "networkFirewallPoliciesRuleGroups": "wafrg", - "networkFrontDoors": "fd-", - "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", - "networkLoadBalancersExternal": "lbe-", - "networkLoadBalancersInternal": "lbi-", - "networkLoadBalancersInboundNatRules": "rule-", - "networkLocalNetworkGateways": "lgw-", - "networkNatGateways": "ng-", - "networkNetworkInterfaces": "nic-", - "networkNetworkSecurityGroups": "nsg-", - "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", - "networkNetworkWatchers": "nw-", - "networkPrivateDnsZones": "pdnsz-", - "networkPrivateLinkServices": "pl-", - "networkPublicIPAddresses": "pip-", - "networkPublicIPPrefixes": "ippre-", - "networkRouteFilters": "rf-", - "networkRouteTables": "rt-", - "networkRouteTablesRoutes": "udr-", - "networkTrafficManagerProfiles": "traf-", - "networkVirtualNetworkGateways": "vgw-", - "networkVirtualNetworks": "vnet-", - "networkVirtualNetworksSubnets": "snet-", - "networkVirtualNetworksVirtualNetworkPeerings": "peer-", - "networkVirtualWans": "vwan-", - "networkVpnGateways": "vpng-", - "networkVpnGatewaysVpnConnections": "vcn-", - "networkVpnGatewaysVpnSites": "vst-", - "notificationHubsNamespaces": "ntfns-", - "notificationHubsNamespacesNotificationHubs": "ntf-", - "operationalInsightsWorkspaces": "log-", - "portalDashboards": "dash-", - "powerBIDedicatedCapacities": "pbi-", - "purviewAccounts": "pview-", - "recoveryServicesVaults": "rsv-", - "resourcesResourceGroups": "rg-", - "searchSearchServices": "srch-", - "serviceBusNamespaces": "sb-", - "serviceBusNamespacesQueues": "sbq-", - "serviceBusNamespacesTopics": "sbt-", - "serviceEndPointPolicies": "se-", - "serviceFabricClusters": "sf-", - "signalRServiceSignalR": "sigr", - "sqlManagedInstances": "sqlmi-", - "sqlServers": "sql-", - "sqlServersDataWarehouse": "sqldw-", - "sqlServersDatabases": "sqldb-", - "sqlServersDatabasesStretch": "sqlstrdb-", - "storageStorageAccounts": "st", - "storageStorageAccountsVm": "stvm", - "storSimpleManagers": "ssimp", - "streamAnalyticsCluster": "asa-", - "synapseWorkspaces": "syn", - "synapseWorkspacesAnalyticsWorkspaces": "synw", - "synapseWorkspacesSqlPoolsDedicated": "syndp", - "synapseWorkspacesSqlPoolsSpark": "synsp", - "timeSeriesInsightsEnvironments": "tsi-", - "webServerFarms": "plan-", - "webSitesAppService": "app-", - "webSitesAppServiceEnvironment": "ase-", - "webSitesFunctions": "func-", - "webStaticSites": "stapp-" -} \ No newline at end of file diff --git a/Environments/Spring/app/app.bicep b/Environments/Spring/app/app.bicep deleted file mode 100644 index 1ae114e4..00000000 --- a/Environments/Spring/app/app.bicep +++ /dev/null @@ -1,47 +0,0 @@ -param name string -param location string = resourceGroup().location -param tags object = {} - -param allowedOrigins array = [] -param appCommandLine string = '' -param applicationInsightsName string = '' -param appServicePlanId string -param appSettings object = {} -param keyVaultName string -param serviceName string = 'petclinic' - -@description('JVM runtime options. Use this instead of defining JAVA_OPTS manually on appSettings.') -param javaRuntimeOptions array = [] - -// applicationinsights-runtime-attach (and other plugins) that uses runtime attach -// require allowAttachSelf to be enabled on App Service. Otherwise, plugins will fail to attach -// on App Service. -var defaultJavaRuntimeOptions = [ '-Djdk.attach.allowAttachSelf=true' ] - -module app '../core/host/appservice.bicep' = { - name: '${name}-app-module' - params: { - name: name - location: location - tags: union(tags, { 'azd-service-name': serviceName }) - allowedOrigins: allowedOrigins - appCommandLine: appCommandLine - applicationInsightsName: applicationInsightsName - appServicePlanId: appServicePlanId - appSettings: union(appSettings, { - JAVA_OPTS: join( - concat( - javaRuntimeOptions, - defaultJavaRuntimeOptions), - ' ') - }) - keyVaultName: keyVaultName - runtimeName: 'java' - runtimeVersion: '17-java17' - scmDoBuildDuringDeployment: true - } -} - -output APP_IDENTITY_PRINCIPAL_ID string = app.outputs.identityPrincipalId -output APP_NAME string = app.outputs.name -output APP_URI string = app.outputs.uri diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index ad5ac488..46297205 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.17.1.54307", - "templateHash": "7122962929441782527" + "templateHash": "7083236466102790225" } }, "parameters": { @@ -13,230 +13,128 @@ "type": "string", "defaultValue": "test", "metadata": { - "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + "description": "Name which is used to generate a short unique hash for each resource" }, "maxLength": 64, "minLength": 1 }, - "location": { + "springCloudInstanceName": { "type": "string", - "defaultValue": "[resourceGroup().location]", + "defaultValue": "", "metadata": { - "description": "Primary location for all resources" - }, - "minLength": 1 + "description": "The instance name of the Azure Spring Cloud resource" + } }, - "appName": { + "appInsightsName": { "type": "string", - "defaultValue": "" + "defaultValue": "", + "metadata": { + "description": "The name of the Application Insights instance for Azure Spring Cloud" + } }, - "applicationInsightsDashboardName": { + "logAnalyticsWorkspaceName": { "type": "string", "defaultValue": "" }, - "applicationInsightsName": { + "springCloudAppSubnetID": { "type": "string", - "defaultValue": "" + "defaultValue": "", + "metadata": { + "description": "The resourceID of the Azure Spring Cloud App Subnet" + } }, - "appServicePlanName": { + "springCloudRuntimeSubnetID": { "type": "string", - "defaultValue": "" + "defaultValue": "", + "metadata": { + "description": "The resourceID of the Azure Spring Cloud Runtime Subnet" + } }, - "mySqlServerName": { + "springCloudServiceCidrs": { "type": "string", - "defaultValue": "" + "defaultValue": "10.20.0.0/16,10.21.0.0/16,10.22.0.1/16" }, - "mySqlServerAdminName": { + "vnetName": { "type": "string", - "defaultValue": "petclinic" + "defaultValue": "", + "metadata": { + "description": "The name of the Virtual Network" + } }, - "mySqlServerAdminPassword": { - "type": "securestring" + "ascAppSubnetName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "the app subnet name of the Azure Spring Cloud" + } }, - "mySqlDatabaseName": { + "ascRuntimeSubnetName": { "type": "string", - "defaultValue": "petclinic" + "defaultValue": "", + "metadata": { + "description": "the runtime subnet name of the Azure Spring Cloud" + } }, - "keyVaultName": { + "vnetAddressPrefixes": { "type": "string", - "defaultValue": "" + "defaultValue": "10.4.0.0/16", + "metadata": { + "description": "The address prefixes of the vnet" + } }, - "logAnalyticsName": { + "ascAppSubnetAddressPrefixes": { "type": "string", - "defaultValue": "" + "defaultValue": "10.4.0.0/24", + "metadata": { + "description": "The Azure Spring Cloud App subnet address prefixes in the vnet" + } }, - "principalId": { + "ascRuntimeSubnetAddressPrefixes": { "type": "string", - "defaultValue": "", + "defaultValue": "10.4.1.0/24", "metadata": { - "description": "Id of the user or app to assign application roles" + "description": "The Azure Spring Cloud Runtime subnet address prefixes in the vnet" } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" } }, "variables": { - "$fxv#0": { - "analysisServicesServers": "as", - "apiManagementService": "apim-", - "appConfigurationConfigurationStores": "appcs-", - "appManagedEnvironments": "cae-", - "appContainerApps": "ca-", - "authorizationPolicyDefinitions": "policy-", - "automationAutomationAccounts": "aa-", - "blueprintBlueprints": "bp-", - "blueprintBlueprintsArtifacts": "bpa-", - "cacheRedis": "redis-", - "cdnProfiles": "cdnp-", - "cdnProfilesEndpoints": "cdne-", - "cognitiveServicesAccounts": "cog-", - "cognitiveServicesFormRecognizer": "cog-fr-", - "cognitiveServicesTextAnalytics": "cog-ta-", - "computeAvailabilitySets": "avail-", - "computeCloudServices": "cld-", - "computeDiskEncryptionSets": "des", - "computeDisks": "disk", - "computeDisksOs": "osdisk", - "computeGalleries": "gal", - "computeSnapshots": "snap-", - "computeVirtualMachines": "vm", - "computeVirtualMachineScaleSets": "vmss-", - "containerInstanceContainerGroups": "ci", - "containerRegistryRegistries": "cr", - "containerServiceManagedClusters": "aks-", - "databricksWorkspaces": "dbw-", - "dataFactoryFactories": "adf-", - "dataLakeAnalyticsAccounts": "dla", - "dataLakeStoreAccounts": "dls", - "dataMigrationServices": "dms-", - "dBforMySQLServers": "mysql-", - "dBforPostgreSQLServers": "psql-", - "devicesIotHubs": "iot-", - "devicesProvisioningServices": "provs-", - "devicesProvisioningServicesCertificates": "pcert-", - "documentDBDatabaseAccounts": "cosmos-", - "eventGridDomains": "evgd-", - "eventGridDomainsTopics": "evgt-", - "eventGridEventSubscriptions": "evgs-", - "eventHubNamespaces": "evhns-", - "eventHubNamespacesEventHubs": "evh-", - "hdInsightClustersHadoop": "hadoop-", - "hdInsightClustersHbase": "hbase-", - "hdInsightClustersKafka": "kafka-", - "hdInsightClustersMl": "mls-", - "hdInsightClustersSpark": "spark-", - "hdInsightClustersStorm": "storm-", - "hybridComputeMachines": "arcs-", - "insightsActionGroups": "ag-", - "insightsComponents": "appi-", - "keyVaultVaults": "kv-", - "kubernetesConnectedClusters": "arck", - "kustoClusters": "dec", - "kustoClustersDatabases": "dedb", - "logicIntegrationAccounts": "ia-", - "logicWorkflows": "logic-", - "machineLearningServicesWorkspaces": "mlw-", - "managedIdentityUserAssignedIdentities": "id-", - "managementManagementGroups": "mg-", - "migrateAssessmentProjects": "migr-", - "networkApplicationGateways": "agw-", - "networkApplicationSecurityGroups": "asg-", - "networkAzureFirewalls": "afw-", - "networkBastionHosts": "bas-", - "networkConnections": "con-", - "networkDnsZones": "dnsz-", - "networkExpressRouteCircuits": "erc-", - "networkFirewallPolicies": "afwp-", - "networkFirewallPoliciesWebApplication": "waf", - "networkFirewallPoliciesRuleGroups": "wafrg", - "networkFrontDoors": "fd-", - "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", - "networkLoadBalancersExternal": "lbe-", - "networkLoadBalancersInternal": "lbi-", - "networkLoadBalancersInboundNatRules": "rule-", - "networkLocalNetworkGateways": "lgw-", - "networkNatGateways": "ng-", - "networkNetworkInterfaces": "nic-", - "networkNetworkSecurityGroups": "nsg-", - "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", - "networkNetworkWatchers": "nw-", - "networkPrivateDnsZones": "pdnsz-", - "networkPrivateLinkServices": "pl-", - "networkPublicIPAddresses": "pip-", - "networkPublicIPPrefixes": "ippre-", - "networkRouteFilters": "rf-", - "networkRouteTables": "rt-", - "networkRouteTablesRoutes": "udr-", - "networkTrafficManagerProfiles": "traf-", - "networkVirtualNetworkGateways": "vgw-", - "networkVirtualNetworks": "vnet-", - "networkVirtualNetworksSubnets": "snet-", - "networkVirtualNetworksVirtualNetworkPeerings": "peer-", - "networkVirtualWans": "vwan-", - "networkVpnGateways": "vpng-", - "networkVpnGatewaysVpnConnections": "vcn-", - "networkVpnGatewaysVpnSites": "vst-", - "notificationHubsNamespaces": "ntfns-", - "notificationHubsNamespacesNotificationHubs": "ntf-", - "operationalInsightsWorkspaces": "log-", - "portalDashboards": "dash-", - "powerBIDedicatedCapacities": "pbi-", - "purviewAccounts": "pview-", - "recoveryServicesVaults": "rsv-", - "resourcesResourceGroups": "rg-", - "searchSearchServices": "srch-", - "serviceBusNamespaces": "sb-", - "serviceBusNamespacesQueues": "sbq-", - "serviceBusNamespacesTopics": "sbt-", - "serviceEndPointPolicies": "se-", - "serviceFabricClusters": "sf-", - "signalRServiceSignalR": "sigr", - "sqlManagedInstances": "sqlmi-", - "sqlServers": "sql-", - "sqlServersDataWarehouse": "sqldw-", - "sqlServersDatabases": "sqldb-", - "sqlServersDatabasesStretch": "sqlstrdb-", - "storageStorageAccounts": "st", - "storageStorageAccountsVm": "stvm", - "storSimpleManagers": "ssimp", - "streamAnalyticsCluster": "asa-", - "synapseWorkspaces": "syn", - "synapseWorkspacesAnalyticsWorkspaces": "synw", - "synapseWorkspacesSqlPoolsDedicated": "syndp", - "synapseWorkspacesSqlPoolsSpark": "synsp", - "timeSeriesInsightsEnvironments": "tsi-", - "webServerFarms": "plan-", - "webSitesAppService": "app-", - "webSitesAppServiceEnvironment": "ase-", - "webSitesFunctions": "func-", - "webStaticSites": "stapp-" - }, - "abbrs": "[variables('$fxv#0')]", "resourceToken": "[toLower(uniqueString(resourceGroup().id, parameters('location')))]", "tags": { - "azd-env-name": "[parameters('environmentName')]" + "env-name": "[parameters('environmentName')]" } }, "resources": [ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "appserviceplan", + "name": "vnet", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": "[if(not(empty(parameters('appServicePlanName'))), createObject('value', parameters('appServicePlanName')), createObject('value', format('{0}{1}', variables('abbrs').webServerFarms, variables('resourceToken'))))]", + "vnetName": "[if(not(empty(parameters('vnetName'))), createObject('value', parameters('vnetName')), createObject('value', format('vnet-{0}', variables('resourceToken'))))]", "location": { "value": "[parameters('location')]" }, + "ascAppSubnetName": "[if(not(empty(parameters('ascAppSubnetName'))), createObject('value', parameters('ascAppSubnetName')), createObject('value', format('app-sub-{0}', variables('resourceToken'))))]", + "ascRuntimeSubnetName": "[if(not(empty(parameters('ascRuntimeSubnetName'))), createObject('value', parameters('ascRuntimeSubnetName')), createObject('value', format('runtime-sub-{0}', variables('resourceToken'))))]", + "vnetAddressPrefixes": { + "value": "[parameters('vnetAddressPrefixes')]" + }, + "ascAppSubnetAddressPrefixes": { + "value": "[parameters('ascAppSubnetAddressPrefixes')]" + }, + "ascRuntimeSubnetAddressPrefixes": { + "value": "[parameters('ascRuntimeSubnetAddressPrefixes')]" + }, "tags": { "value": "[variables('tags')]" - }, - "sku": { - "value": { - "name": "B1" - } } }, "template": { @@ -246,1551 +144,110 @@ "_generator": { "name": "bicep", "version": "0.17.1.54307", - "templateHash": "9579788953021113825" + "templateHash": "6689023659879453668" } }, "parameters": { - "name": { - "type": "string" - }, - "location": { + "vnetName": { "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} + "metadata": { + "description": "The name of the Virtual Network" + } }, - "kind": { + "ascAppSubnetName": { "type": "string", - "defaultValue": "" - }, - "reserved": { - "type": "bool", - "defaultValue": true + "metadata": { + "description": "the app subnet name of the Azure Spring Cloud" + } }, - "sku": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Web/serverfarms", - "apiVersion": "2022-03-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", - "kind": "[parameters('kind')]", - "properties": { - "reserved": "[parameters('reserved')]" + "ascRuntimeSubnetName": { + "type": "string", + "metadata": { + "description": "the runtime subnet name of the Azure Spring Cloud" } - } - ], - "outputs": { - "id": { + }, + "vnetAddressPrefixes": { "type": "string", - "value": "[resourceId('Microsoft.Web/serverfarms', parameters('name'))]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "monitoring", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", - "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", - "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14795379806004450628" - } - }, - "parameters": { - "logAnalyticsName": { - "type": "string" + "metadata": { + "description": "The address prefixes of the vnet" + } }, - "applicationInsightsName": { - "type": "string" + "ascAppSubnetAddressPrefixes": { + "type": "string", + "metadata": { + "description": "The Azure Spring Cloud App subnet address prefixes in the vnet" + } }, - "applicationInsightsDashboardName": { - "type": "string" + "ascRuntimeSubnetAddressPrefixes": { + "type": "string", + "metadata": { + "description": "The Azure Spring Cloud Runtime subnet address prefixes in the vnet" + } }, "location": { "type": "string", - "defaultValue": "[resourceGroup().location]" + "metadata": { + "description": "The location of the resource" + } }, "tags": { "type": "object", - "defaultValue": {} + "metadata": { + "description": "The tags that will be associated to the Resources" + } } }, "resources": [ { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "loganalytics", + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2019-11-01", + "name": "[parameters('vnetName')]", + "location": "[parameters('location')]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "addressSpace": { + "addressPrefixes": [ + "[parameters('vnetAddressPrefixes')]" + ] }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('logAnalyticsName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10301535207560739586" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} + "subnets": [ + { + "name": "[parameters('ascAppSubnetName')]", + "properties": { + "addressPrefix": "[parameters('ascAppSubnetAddressPrefixes')]" } }, - "resources": [ - { - "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-12-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "retentionInDays": 30, - "features": { - "searchVersion": 1 - }, - "sku": { - "name": "PerGB2018" - } - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" + { + "name": "[parameters('ascRuntimeSubnetName')]", + "properties": { + "addressPrefix": "[parameters('ascRuntimeSubnetAddressPrefixes')]" } } - } - } + ] + }, + "tags": "[parameters('tags')]" }, { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "applicationinsights", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-10-01-preview", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('vnetName'))]", + "name": "[guid(resourceGroup().id)]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('applicationInsightsName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "dashboardName": { - "value": "[parameters('applicationInsightsDashboardName')]" - }, - "logAnalyticsWorkspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11997294327656983299" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "dashboardName": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "logAnalyticsWorkspaceId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Insights/components", - "apiVersion": "2020-02-02", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "kind": "web", - "properties": { - "Application_Type": "web", - "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "application-insights-dashboard", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('dashboardName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "applicationInsightsName": { - "value": "[parameters('name')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "13636254352620323826" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "applicationInsightsName": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - } - }, - "resources": [ - { - "type": "Microsoft.Portal/dashboards", - "apiVersion": "2020-09-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "lenses": [ - { - "order": 0, - "parts": [ - { - "position": { - "x": 0, - "y": 0, - "colSpan": 2, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "id", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", - "asset": { - "idInputName": "id", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "overview" - } - }, - { - "position": { - "x": 2, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "ProactiveDetection" - } - }, - { - "position": { - "x": 3, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - } - ], - "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 4, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-04T01:20:33.345Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 5, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-08T18:47:35.237Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "ConfigurationId", - "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 0, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Usage", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 3, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-04T01:22:35.782Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - } - ], - "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 4, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Reliability", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 7, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "DataModel", - "value": { - "version": "1.0.0", - "timeContext": { - "durationMs": 86400000, - "createdTime": "2018-05-04T23:42:40.072Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - "isOptional": true - }, - { - "name": "ConfigurationId", - "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", - "isOptional": true - } - ], - "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", - "isAdapter": true, - "asset": { - "idInputName": "ResourceId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "failures" - } - }, - { - "position": { - "x": 8, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Responsiveness\r\n", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 11, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "DataModel", - "value": { - "version": "1.0.0", - "timeContext": { - "durationMs": 86400000, - "createdTime": "2018-05-04T23:43:37.804Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - "isOptional": true - }, - { - "name": "ConfigurationId", - "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", - "isOptional": true - } - ], - "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", - "isAdapter": true, - "asset": { - "idInputName": "ResourceId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "performance" - } - }, - { - "position": { - "x": 12, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Browser", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 15, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "MetricsExplorerJsonDefinitionId", - "value": "BrowserPerformanceTimelineMetrics" - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "createdTime": "2018-05-08T12:16:27.534Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "CurrentFilter", - "value": { - "eventTypes": [ - 4, - 1, - 3, - 5, - 2, - 6, - 13 - ], - "typeFacets": {}, - "isPermissive": false - } - }, - { - "name": "id", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "browser" - } - }, - { - "position": { - "x": 0, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "sessions/count", - "aggregationType": 5, - "namespace": "microsoft.insights/components/kusto", - "metricVisualization": { - "displayName": "Sessions", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "users/count", - "aggregationType": 5, - "namespace": "microsoft.insights/components/kusto", - "metricVisualization": { - "displayName": "Users", - "color": "#7E58FF" - } - } - ], - "title": "Unique sessions and users", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "segmentationUsers" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "requests/failed", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Failed requests", - "color": "#EC008C" - } - } - ], - "title": "Failed requests", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "failures" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "requests/duration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Server response time", - "color": "#00BCF2" - } - } - ], - "title": "Server response time", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "performance" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 12, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/networkDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Page load network connect time", - "color": "#7E58FF" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/processingDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Client processing time", - "color": "#44F1C8" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/sendDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Send request time", - "color": "#EB9371" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/receiveDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Receiving response time", - "color": "#0672F1" - } - } - ], - "title": "Average page load time breakdown", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 0, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "availabilityResults/availabilityPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Availability", - "color": "#47BDF5" - } - } - ], - "title": "Average availability", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "availability" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "exceptions/server", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Server exceptions", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "dependencies/failed", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Dependency failures", - "color": "#7E58FF" - } - } - ], - "title": "Server exceptions and Dependency failures", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processorCpuPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Processor time", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processCpuPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Process CPU", - "color": "#7E58FF" - } - } - ], - "title": "Average processor and process CPU utilization", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 12, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "exceptions/browser", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Browser exceptions", - "color": "#47BDF5" - } - } - ], - "title": "Browser exceptions", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 0, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "availabilityResults/count", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Availability test results count", - "color": "#47BDF5" - } - } - ], - "title": "Availability test results count", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processIOBytesPerSecond", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Process IO rate", - "color": "#47BDF5" - } - } - ], - "title": "Average process I/O rate", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/memoryAvailableBytes", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Available memory", - "color": "#47BDF5" - } - } - ], - "title": "Average available memory", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - } - ] - } - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/components', parameters('name'))]" - ] - } - ], - "outputs": { - "connectionString": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" - }, - "instrumentationKey": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } + "principalId": "d2531223-68f9-459e-b225-5592f90d145e", + "roleDefinitionId": "[format('{0}/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635', subscription().id)]" }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" ] } ], "outputs": { - "applicationInsightsConnectionString": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" - }, - "applicationInsightsInstrumentationKey": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" - }, - "applicationInsightsName": { + "ascAppSubnetId": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('ascAppSubnetName'))]" }, - "logAnalyticsWorkspaceId": { + "ascRuntimeSubetId": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" - }, - "logAnalyticsWorkspaceName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('ascRuntimeSubnetName'))]" } } } @@ -1799,22 +256,26 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "keyvault", + "name": "springcloud", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "springCloudInstanceName": "[if(not(empty(parameters('springCloudInstanceName'))), createObject('value', parameters('springCloudInstanceName')), createObject('value', format('asa-{0}', variables('resourceToken'))))]", "location": { "value": "[parameters('location')]" }, + "appInsightsName": "[if(not(empty(parameters('appInsightsName'))), createObject('value', parameters('appInsightsName')), createObject('value', format('appi-{0}', variables('resourceToken'))))]", + "logAnalyticsWorkspaceName": "[if(not(empty(parameters('logAnalyticsWorkspaceName'))), createObject('value', parameters('logAnalyticsWorkspaceName')), createObject('value', format('log-{0}', variables('resourceToken'))))]", + "springCloudAppSubnetID": "[if(not(empty(parameters('springCloudAppSubnetID'))), createObject('value', parameters('springCloudAppSubnetID')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'vnet'), '2022-09-01').outputs.ascAppSubnetId.value))]", + "springCloudRuntimeSubnetID": "[if(not(empty(parameters('springCloudRuntimeSubnetID'))), createObject('value', parameters('springCloudRuntimeSubnetID')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'vnet'), '2022-09-01').outputs.ascRuntimeSubetId.value))]", + "springCloudServiceCidrs": { + "value": "[parameters('springCloudServiceCidrs')]" + }, "tags": { "value": "[variables('tags')]" - }, - "principalId": { - "value": "[parameters('principalId')]" } }, "template": { @@ -1824,872 +285,152 @@ "_generator": { "name": "bicep", "version": "0.17.1.54307", - "templateHash": "15428045515587502988" + "templateHash": "5594778554212141711" } }, "parameters": { - "name": { - "type": "string" - }, - "location": { + "springCloudInstanceName": { "type": "string", - "defaultValue": "[resourceGroup().location]" + "metadata": { + "description": "The instance name of the Azure Spring Cloud resource" + } }, - "tags": { - "type": "object", - "defaultValue": {} + "logAnalyticsWorkspaceName": { + "type": "string" }, - "principalId": { + "appInsightsName": { "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + "metadata": { + "description": "The name of the Application Insights instance for Azure Spring Cloud" } - } - ], - "outputs": { - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" }, - "name": { + "springCloudAppSubnetID": { "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "mysql-db", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "serverName": "[if(not(empty(parameters('mySqlServerName'))), createObject('value', parameters('mySqlServerName')), createObject('value', format('{0}{1}', variables('abbrs').dBforMySQLServers, variables('resourceToken'))))]", - "serverAdminName": { - "value": "[parameters('mySqlServerAdminName')]" - }, - "serverAdminPassword": { - "value": "[parameters('mySqlServerAdminPassword')]" - }, - "databaseName": "[if(not(empty(parameters('mySqlDatabaseName'))), createObject('value', parameters('mySqlDatabaseName')), createObject('value', 'petclinic'))]", - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "15254487284233766693" - } - }, - "parameters": { - "serverName": { - "type": "string" + "defaultValue": "10.4.0.0/24", + "metadata": { + "description": "The resourceID of the Azure Spring Cloud App Subnet" + } }, - "databaseName": { - "type": "string" + "springCloudRuntimeSubnetID": { + "type": "string", + "defaultValue": "10.4.1.0/24", + "metadata": { + "description": "The resourceID of the Azure Spring Cloud Runtime Subnet" + } }, - "location": { + "springCloudServiceCidrs": { "type": "string", - "defaultValue": "[resourceGroup().location]" + "defaultValue": "10.0.0.0/16,10.2.0.0/16,10.3.0.1/16", + "metadata": { + "description": "Comma-separated list of IP address ranges in CIDR format. The IP ranges are reserved to host underlying Azure Spring Cloud infrastructure, which should be 3 at least /16 unused IP ranges, must not overlap with any Subnet IP ranges" + } }, "tags": { "type": "object", - "defaultValue": {} - }, - "keyVaultName": { - "type": "string" - }, - "serverAdminName": { - "type": "string", - "minLength": 1, + "defaultValue": {}, "metadata": { - "description": "Database administrator login name" + "description": "The tags that will be associated to the Resources" } }, - "serverAdminPasswordKey": { + "location": { "type": "string", - "defaultValue": "MYSQL-PASS" - }, - "serverAdminPassword": { - "type": "securestring", - "minLength": 8, - "metadata": { - "description": "Database administrator password" - } + "defaultValue": "[resourceGroup().location]" } }, "resources": [ { - "type": "Microsoft.DBforMySQL/flexibleServers/databases", - "apiVersion": "2021-05-01", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('databaseName'))]", + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2020-03-01-preview", + "name": "[parameters('logAnalyticsWorkspaceName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + }, + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02-preview", + "name": "[parameters('appInsightsName')]", + "location": "[parameters('location')]", + "kind": "web", + "tags": "[parameters('tags')]", "properties": { - "charset": "utf8", - "collation": "utf8_general_ci" + "Application_Type": "web", + "Flow_Type": "Bluefield", + "Request_Source": "rest", + "WorkspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'mysql-server')]" + "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" ] }, { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "mysql-server", + "type": "Microsoft.AppPlatform/Spring", + "apiVersion": "2022-03-01-preview", + "name": "[parameters('springCloudInstanceName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "S0", + "tier": "Standard" + }, "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('serverName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "adminName": { - "value": "[parameters('serverAdminName')]" - }, - "adminPassword": { - "value": "[parameters('serverAdminPassword')]" - }, - "adminPasswordKey": { - "value": "[parameters('serverAdminPasswordKey')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2841349531746939468" - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Server Name for Azure database for MySQL" - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Location for all resources." - } - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "keyVaultName": { - "type": "string" - }, - "adminName": { - "type": "string", - "defaultValue": "mySqlAdmin", - "minLength": 1, - "metadata": { - "description": "Database administrator login name" - } - }, - "adminPasswordKey": { - "type": "string", - "defaultValue": "MYSQL-PASS" - }, - "adminPassword": { - "type": "securestring", - "minLength": 8, - "metadata": { - "description": "Database administrator password" - } - }, - "skuName": { - "type": "string", - "defaultValue": "Standard_B1s", - "metadata": { - "description": "Azure database for MySQL sku name " - } - }, - "autoGrow": { - "type": "string", - "defaultValue": "Enabled", - "metadata": { - "description": "Enable Storage Auto Grow or not" - }, - "allowedValues": [ - "Enabled", - "Disabled" - ] - }, - "storageSizeGB": { - "type": "int", - "defaultValue": 20, - "metadata": { - "description": "Azure database for MySQL storage Size " - } - }, - "storageIops": { - "type": "int", - "defaultValue": 360, - "metadata": { - "description": "Azure database for MySQL storage Iops" - } - }, - "skuTier": { - "type": "string", - "defaultValue": "Burstable", - "allowedValues": [ - "GeneralPurpose", - "MemoryOptimized", - "Burstable" - ], - "metadata": { - "description": "Azure database for MySQL pricing tier" - } - }, - "version": { - "type": "string", - "defaultValue": "8.0.21", - "allowedValues": [ - "5.7", - "8.0.21" - ], - "metadata": { - "description": "MySQL version" - } - }, - "backupRetentionDays": { - "type": "int", - "defaultValue": 7, - "metadata": { - "description": "MySQL Server backup retention days" - } - }, - "geoRedundantBackup": { - "type": "string", - "defaultValue": "Disabled", - "metadata": { - "description": "Geo-Redundant Backup setting" - } - }, - "highAvailabilityMode": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Disabled", - "ZoneRedundant", - "SameZone" - ] - } - }, - "resources": [ - { - "type": "Microsoft.DBforMySQL/flexibleServers", - "apiVersion": "2021-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('skuName')]", - "tier": "[parameters('skuTier')]" - }, - "properties": { - "administratorLogin": "[parameters('adminName')]", - "administratorLoginPassword": "[parameters('adminPassword')]", - "storage": { - "autoGrow": "[parameters('autoGrow')]", - "iops": "[parameters('storageIops')]", - "storageSizeGB": "[parameters('storageSizeGB')]" - }, - "createMode": "Default", - "version": "[parameters('version')]", - "backup": { - "backupRetentionDays": "[parameters('backupRetentionDays')]", - "geoRedundantBackup": "[parameters('geoRedundantBackup')]" - }, - "highAvailability": { - "mode": "[parameters('highAvailabilityMode')]" - } - } - }, - { - "type": "Microsoft.DBforMySQL/flexibleServers/firewallRules", - "apiVersion": "2021-05-01", - "name": "[format('{0}/{1}', parameters('name'), 'AllowAzureIPs')]", - "properties": { - "startIpAddress": "0.0.0.0", - "endIpAddress": "0.0.0.0" - }, - "dependsOn": [ - "[resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name'))]" - ] - }, - { - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('adminPasswordKey'))]", - "properties": { - "value": "[parameters('adminPassword')]" - } - } - ], - "outputs": { - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "adminName": { - "type": "string", - "value": "[parameters('adminName')]" - }, - "adminPasswordKey": { - "type": "string", - "value": "[parameters('adminPasswordKey')]" - }, - "fullyQualifiedDomainName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name')), '2021-05-01').fullyQualifiedDomainName]" - }, - "endpoint": { - "type": "string", - "value": "[format('jdbc:mysql://{0}:3306/?useSSL=true&requireSSL=false', reference(resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name')), '2021-05-01').fullyQualifiedDomainName)]" - } - } + "networkProfile": { + "serviceCidr": "[parameters('springCloudServiceCidrs')]", + "serviceRuntimeSubnetId": "[parameters('springCloudRuntimeSubnetID')]", + "appSubnetId": "[parameters('springCloudAppSubnetID')]" } } - } - ], - "outputs": { - "serverAdminPasswordKey": { - "type": "string", - "value": "[parameters('serverAdminPasswordKey')]" - }, - "databaseName": { - "type": "string", - "value": "[parameters('databaseName')]" - }, - "endpoint": { - "type": "string", - "value": "[format('jdbc:mysql://{0}:3306/{1}?useSSL=true&requireSSL=false', reference(resourceId('Microsoft.Resources/deployments', 'mysql-server'), '2022-09-01').outputs.fullyQualifiedDomainName.value, parameters('databaseName'))]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "app", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('appName'))), createObject('value', parameters('appName')), createObject('value', format('{0}petclinic-{1}', variables('abbrs').webSitesAppService, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" - }, - "appServicePlanId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'appserviceplan'), '2022-09-01').outputs.id.value]" - }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, - "appSettings": { - "value": { - "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]", - "AZURE_KEY_VAULT_ENDPOINT": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]", - "SPRING_PROFILES_ACTIVE": "azure,mysql", - "MYSQL_URL": "[reference(resourceId('Microsoft.Resources/deployments', 'mysql-db'), '2022-09-01').outputs.endpoint.value]", - "MYSQL_USER": "[parameters('mySqlServerAdminName')]" - } - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "16501122015582133594" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "allowedOrigins": { - "type": "array", - "defaultValue": [] - }, - "appCommandLine": { - "type": "string", - "defaultValue": "" - }, - "applicationInsightsName": { - "type": "string", - "defaultValue": "" - }, - "appServicePlanId": { - "type": "string" }, - "appSettings": { - "type": "object", - "defaultValue": {} - }, - "keyVaultName": { - "type": "string" - }, - "serviceName": { - "type": "string", - "defaultValue": "petclinic" - }, - "javaRuntimeOptions": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "JVM runtime options. Use this instead of defining JAVA_OPTS manually on appSettings." - } - } - }, - "variables": { - "defaultJavaRuntimeOptions": [ - "-Djdk.attach.allowAttachSelf=true" - ] - }, - "resources": [ { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-app-module', parameters('name'))]", + "type": "Microsoft.AppPlatform/Spring/monitoringSettings", + "apiVersion": "2020-07-01", + "name": "[format('{0}/default', parameters('springCloudInstanceName'))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" - }, - "allowedOrigins": { - "value": "[parameters('allowedOrigins')]" - }, - "appCommandLine": { - "value": "[parameters('appCommandLine')]" - }, - "applicationInsightsName": { - "value": "[parameters('applicationInsightsName')]" - }, - "appServicePlanId": { - "value": "[parameters('appServicePlanId')]" - }, - "appSettings": { - "value": "[union(parameters('appSettings'), createObject('JAVA_OPTS', join(concat(parameters('javaRuntimeOptions'), variables('defaultJavaRuntimeOptions')), ' ')))]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "runtimeName": { - "value": "java" - }, - "runtimeVersion": { - "value": "17-java17" - }, - "scmDoBuildDuringDeployment": { - "value": true - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14158023622954101240" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "applicationInsightsName": { - "type": "string", - "defaultValue": "" - }, - "appServicePlanId": { - "type": "string" - }, - "keyVaultName": { - "type": "string", - "defaultValue": "" - }, - "managedIdentity": { - "type": "bool", - "defaultValue": "[not(empty(parameters('keyVaultName')))]" - }, - "runtimeName": { - "type": "string", - "allowedValues": [ - "dotnet", - "dotnetcore", - "dotnet-isolated", - "node", - "python", - "java", - "powershell", - "custom" - ] - }, - "runtimeNameAndVersion": { - "type": "string", - "defaultValue": "[format('{0}|{1}', parameters('runtimeName'), parameters('runtimeVersion'))]" - }, - "runtimeVersion": { - "type": "string" - }, - "kind": { - "type": "string", - "defaultValue": "app,linux" - }, - "allowedOrigins": { - "type": "array", - "defaultValue": [] - }, - "alwaysOn": { - "type": "bool", - "defaultValue": true - }, - "appCommandLine": { - "type": "string", - "defaultValue": "" - }, - "appSettings": { - "type": "object", - "defaultValue": {} - }, - "clientAffinityEnabled": { - "type": "bool", - "defaultValue": false - }, - "enableOryxBuild": { - "type": "bool", - "defaultValue": "[contains(parameters('kind'), 'linux')]" - }, - "functionAppScaleLimit": { - "type": "int", - "defaultValue": -1 - }, - "linuxFxVersion": { - "type": "string", - "defaultValue": "[parameters('runtimeNameAndVersion')]" - }, - "minimumElasticInstanceCount": { - "type": "int", - "defaultValue": -1 - }, - "numberOfWorkers": { - "type": "int", - "defaultValue": -1 - }, - "scmDoBuildDuringDeployment": { - "type": "bool", - "defaultValue": false - }, - "use32BitWorkerProcess": { - "type": "bool", - "defaultValue": false - } - }, - "resources": [ - { - "type": "Microsoft.Web/sites/config", - "apiVersion": "2022-03-01", - "name": "[format('{0}/{1}', parameters('name'), 'appsettings')]", - "properties": "[union(parameters('appSettings'), createObject('SCM_DO_BUILD_DURING_DEPLOYMENT', string(parameters('scmDoBuildDuringDeployment')), 'ENABLE_ORYX_BUILD', string(parameters('enableOryxBuild'))), if(not(empty(parameters('applicationInsightsName'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString), createObject()), if(not(empty(parameters('keyVaultName'))), createObject('AZURE_KEY_VAULT_ENDPOINT', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri), createObject()))]", - "dependsOn": [ - "[resourceId('Microsoft.Web/sites', parameters('name'))]" - ] - }, - { - "type": "Microsoft.Web/sites/config", - "apiVersion": "2022-03-01", - "name": "[format('{0}/{1}', parameters('name'), 'logs')]", - "properties": { - "applicationLogs": { - "fileSystem": { - "level": "Verbose" - } - }, - "detailedErrorMessages": { - "enabled": true - }, - "failedRequestsTracing": { - "enabled": true - }, - "httpLogs": { - "fileSystem": { - "enabled": true, - "retentionInDays": 1, - "retentionInMb": 35 - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Web/sites', parameters('name'))]", - "[resourceId('Microsoft.Web/sites/config', parameters('name'), 'appsettings')]" - ] - }, - { - "type": "Microsoft.Web/sites", - "apiVersion": "2022-03-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "kind": "[parameters('kind')]", - "properties": { - "serverFarmId": "[parameters('appServicePlanId')]", - "siteConfig": { - "linuxFxVersion": "[parameters('linuxFxVersion')]", - "alwaysOn": "[parameters('alwaysOn')]", - "ftpsState": "FtpsOnly", - "appCommandLine": "[parameters('appCommandLine')]", - "numberOfWorkers": "[if(not(equals(parameters('numberOfWorkers'), -1)), parameters('numberOfWorkers'), null())]", - "minimumElasticInstanceCount": "[if(not(equals(parameters('minimumElasticInstanceCount'), -1)), parameters('minimumElasticInstanceCount'), null())]", - "use32BitWorkerProcess": "[parameters('use32BitWorkerProcess')]", - "functionAppScaleLimit": "[if(not(equals(parameters('functionAppScaleLimit'), -1)), parameters('functionAppScaleLimit'), null())]", - "cors": { - "allowedOrigins": "[union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins'))]" - } - }, - "clientAffinityEnabled": "[parameters('clientAffinityEnabled')]", - "httpsOnly": true - }, - "identity": { - "type": "[if(parameters('managedIdentity'), 'SystemAssigned', 'None')]" - } - } - ], - "outputs": { - "identityPrincipalId": { - "type": "string", - "value": "[if(parameters('managedIdentity'), reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01', 'full').identity.principalId, '')]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "uri": { - "type": "string", - "value": "[format('https://{0}', reference(resourceId('Microsoft.Web/sites', parameters('name')), '2022-03-01').defaultHostName)]" - } - } - } - } - } - ], - "outputs": { - "APP_IDENTITY_PRINCIPAL_ID": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-app-module', parameters('name'))), '2022-09-01').outputs.identityPrincipalId.value]" - }, - "APP_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-app-module', parameters('name'))), '2022-09-01').outputs.name.value]" - }, - "APP_URI": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-app-module', parameters('name'))), '2022-09-01').outputs.uri.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'appserviceplan')]", - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]", - "[resourceId('Microsoft.Resources/deployments', 'mysql-db')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "app-keyvault-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, - "principalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app'), '2022-09-01').outputs.APP_IDENTITY_PRINCIPAL_ID.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17512594306716167359" - } - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "add" - }, - "keyVaultName": { - "type": "string", - "defaultValue": "" - }, - "permissions": { - "type": "object", - "defaultValue": { - "secrets": [ - "get", - "list" - ] - } + "traceEnabled": true, + "appInsightsInstrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('appInsightsName')), '2020-02-02-preview').InstrumentationKey]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('appInsightsName'))]", + "[resourceId('Microsoft.AppPlatform/Spring', parameters('springCloudInstanceName'))]" + ] }, - "principalId": { - "type": "string" - } - }, - "resources": [ { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2017-05-01-preview", + "scope": "[format('Microsoft.AppPlatform/Spring/{0}', parameters('springCloudInstanceName'))]", + "name": "monitoring", "properties": { - "accessPolicies": [ + "workspaceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]", + "logs": [ { - "objectId": "[parameters('principalId')]", - "tenantId": "[subscription().tenantId]", - "permissions": "[parameters('permissions')]" + "category": "ApplicationConsole", + "enabled": true, + "retentionPolicy": { + "days": 30, + "enabled": false + } } ] - } + }, + "dependsOn": [ + "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]", + "[resourceId('Microsoft.AppPlatform/Spring', parameters('springCloudInstanceName'))]" + ] } ] } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'app')]", - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + "[resourceId('Microsoft.Resources/deployments', 'vnet')]" ] } - ], - "outputs": { - "MYSQL_URL": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'mysql-db'), '2022-09-01').outputs.endpoint.value]" - }, - "MYSQL_USER": { - "type": "string", - "value": "[parameters('mySqlServerAdminName')]" - }, - "APPLICATIONINSIGHTS_CONNECTION_STRING": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" - }, - "AZURE_KEY_VAULT_ENDPOINT": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" - }, - "AZURE_KEY_VAULT_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, - "AZURE_LOCATION": { - "type": "string", - "value": "[parameters('location')]" - }, - "AZURE_TENANT_ID": { - "type": "string", - "value": "[tenant().tenantId]" - }, - "SPRING_PROFILES_ACTIVE": { - "type": "string", - "value": "azure,mysql" - } - } + ] } \ No newline at end of file diff --git a/Environments/Spring/core/database/mysql/mysql-db.bicep b/Environments/Spring/core/database/mysql/mysql-db.bicep deleted file mode 100644 index a95cd8ad..00000000 --- a/Environments/Spring/core/database/mysql/mysql-db.bicep +++ /dev/null @@ -1,50 +0,0 @@ -param serverName string -param databaseName string -param location string = resourceGroup().location -param tags object = {} - -param keyVaultName string -@description('Database administrator login name') -@minLength(1) -param serverAdminName string - -// this is not the password, but the key used to load password from Key Vault -#disable-next-line secure-secrets-in-params -param serverAdminPasswordKey string = 'MYSQL-PASS' - -@description('Database administrator password') -@minLength(8) -@secure() -param serverAdminPassword string - -// The database server -module server 'mysql-server.bicep' = { - name: 'mysql-server' - params: { - name: serverName - location: location - tags: tags - adminName: serverAdminName - adminPassword: serverAdminPassword - adminPasswordKey: serverAdminPasswordKey - keyVaultName: keyVaultName - } -} - -resource database 'Microsoft.DBforMySQL/flexibleServers/databases@2021-05-01' = { - name: '${serverName}/${databaseName}' - properties: { - charset: 'utf8' - collation: 'utf8_general_ci' - } - - dependsOn: [ - server - ] -} - -// this is not the password, but the key used to load password from Key Vault -#disable-next-line outputs-should-not-contain-secrets -output serverAdminPasswordKey string = serverAdminPasswordKey -output databaseName string = databaseName -output endpoint string = 'jdbc:mysql://${server.outputs.fullyQualifiedDomainName}:3306/${databaseName}?useSSL=true&requireSSL=false' diff --git a/Environments/Spring/core/database/mysql/mysql-server.bicep b/Environments/Spring/core/database/mysql/mysql-server.bicep deleted file mode 100644 index 5cf26493..00000000 --- a/Environments/Spring/core/database/mysql/mysql-server.bicep +++ /dev/null @@ -1,121 +0,0 @@ -@description('Server Name for Azure database for MySQL') -param name string -@description('Location for all resources.') -param location string = resourceGroup().location -param tags object = {} - -param keyVaultName string - -@description('Database administrator login name') -@minLength(1) -param adminName string = 'mySqlAdmin' - -// this is not the password, but the key used to load password from Key Vault -#disable-next-line secure-secrets-in-params -param adminPasswordKey string = 'MYSQL-PASS' - -@description('Database administrator password') -@minLength(8) -@secure() -param adminPassword string - -@description('Azure database for MySQL sku name ') -param skuName string = 'Standard_B1s' - -@allowed([ - 'Enabled' - 'Disabled' -]) -@description('Enable Storage Auto Grow or not') -param autoGrow string = 'Enabled' - -@description('Azure database for MySQL storage Size ') -param storageSizeGB int = 20 - -@description('Azure database for MySQL storage Iops') -param storageIops int = 360 - -@description('Azure database for MySQL pricing tier') -@allowed([ - 'GeneralPurpose' - 'MemoryOptimized' - 'Burstable' -]) -param skuTier string = 'Burstable' - -@description('MySQL version') -@allowed([ - '5.7' - '8.0.21' -]) -param version string = '8.0.21' - -@description('MySQL Server backup retention days') -param backupRetentionDays int = 7 - -@description('Geo-Redundant Backup setting') -param geoRedundantBackup string = 'Disabled' - -@allowed([ - 'Disabled' - 'ZoneRedundant' - 'SameZone' -]) -param highAvailabilityMode string = 'Disabled' - -resource server 'Microsoft.DBforMySQL/flexibleServers@2021-05-01' = { - name: name - location: location - tags: tags - sku: { - name: skuName - tier: skuTier - } - properties: { - administratorLogin: adminName - administratorLoginPassword: adminPassword - storage: { - autoGrow: autoGrow - iops: storageIops - storageSizeGB: storageSizeGB - } - createMode: 'Default' - version: version - backup: { - backupRetentionDays: backupRetentionDays - geoRedundantBackup: geoRedundantBackup - } - highAvailability: { - mode: highAvailabilityMode - } - } -} - -resource firewallRuleAllowAllAzureIps 'Microsoft.DBforMySQL/flexibleServers/firewallRules@2021-05-01' = { - parent: server - name: 'AllowAzureIPs' - properties: { - startIpAddress: '0.0.0.0' - endIpAddress: '0.0.0.0' - } -} - -resource mySqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: adminPasswordKey - properties: { - value: adminPassword - } -} - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - -output name string = server.name -output adminName string = adminName -// this is not the password, but the key used to load password from Key Vault -#disable-next-line outputs-should-not-contain-secrets -output adminPasswordKey string = adminPasswordKey -output fullyQualifiedDomainName string = server.properties.fullyQualifiedDomainName -output endpoint string = 'jdbc:mysql://${server.properties.fullyQualifiedDomainName}:3306/?useSSL=true&requireSSL=false' diff --git a/Environments/Spring/core/host/appservice.bicep b/Environments/Spring/core/host/appservice.bicep deleted file mode 100644 index 62e34a65..00000000 --- a/Environments/Spring/core/host/appservice.bicep +++ /dev/null @@ -1,97 +0,0 @@ -param name string -param location string = resourceGroup().location -param tags object = {} - -// Reference Properties -param applicationInsightsName string = '' -param appServicePlanId string -param keyVaultName string = '' -param managedIdentity bool = !empty(keyVaultName) - -// Runtime Properties -@allowed([ - 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' -]) -param runtimeName string -param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' -param runtimeVersion string - -// Microsoft.Web/sites Properties -param kind string = 'app,linux' - -// Microsoft.Web/sites/config -param allowedOrigins array = [] -param alwaysOn bool = true -param appCommandLine string = '' -param appSettings object = {} -param clientAffinityEnabled bool = false -param enableOryxBuild bool = contains(kind, 'linux') -param functionAppScaleLimit int = -1 -param linuxFxVersion string = runtimeNameAndVersion -param minimumElasticInstanceCount int = -1 -param numberOfWorkers int = -1 -param scmDoBuildDuringDeployment bool = false -param use32BitWorkerProcess bool = false - -resource appService 'Microsoft.Web/sites@2022-03-01' = { - name: name - location: location - tags: tags - kind: kind - properties: { - serverFarmId: appServicePlanId - siteConfig: { - linuxFxVersion: linuxFxVersion - alwaysOn: alwaysOn - ftpsState: 'FtpsOnly' - appCommandLine: appCommandLine - numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null - minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null - use32BitWorkerProcess: use32BitWorkerProcess - functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null - cors: { - allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) - } - } - clientAffinityEnabled: clientAffinityEnabled - httpsOnly: true - } - - identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } - - resource configAppSettings 'config' = { - name: 'appsettings' - properties: union(appSettings, - { - SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) - ENABLE_ORYX_BUILD: string(enableOryxBuild) - }, - !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, - !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) - } - - resource configLogs 'config' = { - name: 'logs' - properties: { - applicationLogs: { fileSystem: { level: 'Verbose' } } - detailedErrorMessages: { enabled: true } - failedRequestsTracing: { enabled: true } - httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } - } - dependsOn: [ - configAppSettings - ] - } -} - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { - name: keyVaultName -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { - name: applicationInsightsName -} - -output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' -output name string = appService.name -output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Spring/core/host/appserviceplan.bicep b/Environments/Spring/core/host/appserviceplan.bicep deleted file mode 100644 index 69c35d78..00000000 --- a/Environments/Spring/core/host/appserviceplan.bicep +++ /dev/null @@ -1,20 +0,0 @@ -param name string -param location string = resourceGroup().location -param tags object = {} - -param kind string = '' -param reserved bool = true -param sku object - -resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { - name: name - location: location - tags: tags - sku: sku - kind: kind - properties: { - reserved: reserved - } -} - -output id string = appServicePlan.id diff --git a/Environments/Spring/core/host/springapps.bicep b/Environments/Spring/core/host/springapps.bicep new file mode 100644 index 00000000..94cf3f19 --- /dev/null +++ b/Environments/Spring/core/host/springapps.bicep @@ -0,0 +1,92 @@ +@description('The instance name of the Azure Spring Cloud resource') +param springCloudInstanceName string + +param logAnalyticsWorkspaceName string + +@description('The name of the Application Insights instance for Azure Spring Cloud') +param appInsightsName string + +@description('The resourceID of the Azure Spring Cloud App Subnet') +param springCloudAppSubnetID string = '10.4.0.0/24' + +@description('The resourceID of the Azure Spring Cloud Runtime Subnet') +param springCloudRuntimeSubnetID string = '10.4.1.0/24' + +@description('Comma-separated list of IP address ranges in CIDR format. The IP ranges are reserved to host underlying Azure Spring Cloud infrastructure, which should be 3 at least /16 unused IP ranges, must not overlap with any Subnet IP ranges') +param springCloudServiceCidrs string = '10.0.0.0/16,10.2.0.0/16,10.3.0.1/16' + +@description('The tags that will be associated to the Resources') +param tags object = {} + +param location string = resourceGroup().location + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-03-01-preview' = { + name: logAnalyticsWorkspaceName + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +resource appInsights 'Microsoft.Insights/components@2020-02-02-preview' = { + name: appInsightsName + location: location + kind: 'web' + tags: tags + properties: { + Application_Type: 'web' + Flow_Type: 'Bluefield' + Request_Source: 'rest' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + +resource springCloudInstance 'Microsoft.AppPlatform/Spring@2022-03-01-preview' = { + name: springCloudInstanceName + location: location + tags: tags + sku: { + name: 'S0' + tier: 'Standard' + } + properties: { + networkProfile: { + serviceCidr: springCloudServiceCidrs + serviceRuntimeSubnetId: springCloudRuntimeSubnetID + appSubnetId: springCloudAppSubnetID + } + } +} + +resource springCloudMonitoringSettings 'Microsoft.AppPlatform/Spring/monitoringSettings@2020-07-01' = { + name: '${springCloudInstance.name}/default' // The only supported value is 'default' + properties: { + traceEnabled: true + appInsightsInstrumentationKey: appInsights.properties.InstrumentationKey + } +} + +resource springCloudDiagnostics 'microsoft.insights/diagnosticSettings@2017-05-01-preview' = { + name: 'monitoring' + scope: springCloudInstance + properties: { + workspaceId: logAnalyticsWorkspace.id + logs: [ + { + category: 'ApplicationConsole' + enabled: true + retentionPolicy: { + days: 30 + enabled: false + } + } + ] + } +} diff --git a/Environments/Spring/core/monitor/applicationinsights-dashboard.bicep b/Environments/Spring/core/monitor/applicationinsights-dashboard.bicep deleted file mode 100644 index b7af2c1a..00000000 --- a/Environments/Spring/core/monitor/applicationinsights-dashboard.bicep +++ /dev/null @@ -1,1235 +0,0 @@ -param name string -param applicationInsightsName string -param location string = resourceGroup().location -param tags object = {} - -// 2020-09-01-preview because that is the latest valid version -resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { - name: name - location: location - tags: tags - properties: { - lenses: [ - { - order: 0 - parts: [ - { - position: { - x: 0 - y: 0 - colSpan: 2 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'id' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' - asset: { - idInputName: 'id' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'overview' - } - } - { - position: { - x: 2 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'ProactiveDetection' - } - } - { - position: { - x: 3 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:20:33.345Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 5 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-08T18:47:35.237Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'ConfigurationId' - value: '78ce933e-e864-4b05-a27b-71fd55a6afad' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 0 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Usage' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 3 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:22:35.782Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Reliability' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 7 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:42:40.072Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'failures' - } - } - { - position: { - x: 8 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Responsiveness\r\n' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 11 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:43:37.804Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'performance' - } - } - { - position: { - x: 12 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Browser' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 15 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'MetricsExplorerJsonDefinitionId' - value: 'BrowserPerformanceTimelineMetrics' - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - createdTime: '2018-05-08T12:16:27.534Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'CurrentFilter' - value: { - eventTypes: [ - 4 - 1 - 3 - 5 - 2 - 6 - 13 - ] - typeFacets: {} - isPermissive: false - } - } - { - name: 'id' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'browser' - } - } - { - position: { - x: 0 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'sessions/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Sessions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'users/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Users' - color: '#7E58FF' - } - } - ] - title: 'Unique sessions and users' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'segmentationUsers' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Failed requests' - color: '#EC008C' - } - } - ] - title: 'Failed requests' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'failures' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/duration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server response time' - color: '#00BCF2' - } - } - ] - title: 'Server response time' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'performance' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/networkDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Page load network connect time' - color: '#7E58FF' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/processingDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Client processing time' - color: '#44F1C8' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/sendDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Send request time' - color: '#EB9371' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/receiveDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Receiving response time' - color: '#0672F1' - } - } - ] - title: 'Average page load time breakdown' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/availabilityPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability' - color: '#47BDF5' - } - } - ] - title: 'Average availability' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'availability' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/server' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server exceptions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'dependencies/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Dependency failures' - color: '#7E58FF' - } - } - ] - title: 'Server exceptions and Dependency failures' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processorCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Processor time' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process CPU' - color: '#7E58FF' - } - } - ] - title: 'Average processor and process CPU utilization' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/browser' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Browser exceptions' - color: '#47BDF5' - } - } - ] - title: 'Browser exceptions' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/count' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability test results count' - color: '#47BDF5' - } - } - ] - title: 'Availability test results count' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processIOBytesPerSecond' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process IO rate' - color: '#47BDF5' - } - } - ] - title: 'Average process I/O rate' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/memoryAvailableBytes' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Available memory' - color: '#47BDF5' - } - } - ] - title: 'Average available memory' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - ] - } - ] - } -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { - name: applicationInsightsName -} diff --git a/Environments/Spring/core/monitor/applicationinsights.bicep b/Environments/Spring/core/monitor/applicationinsights.bicep deleted file mode 100644 index f76b292b..00000000 --- a/Environments/Spring/core/monitor/applicationinsights.bicep +++ /dev/null @@ -1,30 +0,0 @@ -param name string -param dashboardName string -param location string = resourceGroup().location -param tags object = {} - -param logAnalyticsWorkspaceId string - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { - name: name - location: location - tags: tags - kind: 'web' - properties: { - Application_Type: 'web' - WorkspaceResourceId: logAnalyticsWorkspaceId - } -} - -module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { - name: 'application-insights-dashboard' - params: { - name: dashboardName - location: location - applicationInsightsName: applicationInsights.name - } -} - -output connectionString string = applicationInsights.properties.ConnectionString -output instrumentationKey string = applicationInsights.properties.InstrumentationKey -output name string = applicationInsights.name diff --git a/Environments/Spring/core/monitor/loganalytics.bicep b/Environments/Spring/core/monitor/loganalytics.bicep deleted file mode 100644 index 770544cc..00000000 --- a/Environments/Spring/core/monitor/loganalytics.bicep +++ /dev/null @@ -1,21 +0,0 @@ -param name string -param location string = resourceGroup().location -param tags object = {} - -resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { - name: name - location: location - tags: tags - properties: any({ - retentionInDays: 30 - features: { - searchVersion: 1 - } - sku: { - name: 'PerGB2018' - } - }) -} - -output id string = logAnalytics.id -output name string = logAnalytics.name diff --git a/Environments/Spring/core/monitor/monitoring.bicep b/Environments/Spring/core/monitor/monitoring.bicep deleted file mode 100644 index 96ba11e5..00000000 --- a/Environments/Spring/core/monitor/monitoring.bicep +++ /dev/null @@ -1,31 +0,0 @@ -param logAnalyticsName string -param applicationInsightsName string -param applicationInsightsDashboardName string -param location string = resourceGroup().location -param tags object = {} - -module logAnalytics 'loganalytics.bicep' = { - name: 'loganalytics' - params: { - name: logAnalyticsName - location: location - tags: tags - } -} - -module applicationInsights 'applicationinsights.bicep' = { - name: 'applicationinsights' - params: { - name: applicationInsightsName - location: location - tags: tags - dashboardName: applicationInsightsDashboardName - logAnalyticsWorkspaceId: logAnalytics.outputs.id - } -} - -output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString -output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey -output applicationInsightsName string = applicationInsights.outputs.name -output logAnalyticsWorkspaceId string = logAnalytics.outputs.id -output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Spring/core/network/vnet.bicep b/Environments/Spring/core/network/vnet.bicep new file mode 100644 index 00000000..f237d066 --- /dev/null +++ b/Environments/Spring/core/network/vnet.bicep @@ -0,0 +1,72 @@ +@description('The name of the Virtual Network') +param vnetName string + +@description('the app subnet name of the Azure Spring Cloud') +param ascAppSubnetName string + +@description('the runtime subnet name of the Azure Spring Cloud') +param ascRuntimeSubnetName string + +@description('The address prefixes of the vnet') +param vnetAddressPrefixes string + +@description('The Azure Spring Cloud App subnet address prefixes in the vnet') +param ascAppSubnetAddressPrefixes string + +@description('The Azure Spring Cloud Runtime subnet address prefixes in the vnet') +param ascRuntimeSubnetAddressPrefixes string + +@description('The location of the resource') +param location string + +@description('The tags that will be associated to the Resources') +param tags object + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { + name: vnetName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + vnetAddressPrefixes + ] + } + subnets: [ + { + name: ascAppSubnetName + properties: { + addressPrefix: ascAppSubnetAddressPrefixes + } + } + { + name: ascRuntimeSubnetName + properties: { + addressPrefix: ascRuntimeSubnetAddressPrefixes + } + } + ] + } + + resource ascAppSubnet 'subnets' existing = { + name: ascAppSubnetName + } + + resource ascRuntimeSubnet 'subnets' existing = { + name: ascRuntimeSubnetName + } + + tags: tags +} + +//Grant the access for the vnet to Azure Spring Cloud +resource ascRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = { + name: guid(resourceGroup().id) + scope: virtualNetwork + properties: { + principalId: 'd2531223-68f9-459e-b225-5592f90d145e' + roleDefinitionId: '${subscription().id}/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' //Grant Owner permission + } +} + +output ascAppSubnetId string = virtualNetwork::ascAppSubnet.id +output ascRuntimeSubetId string = virtualNetwork::ascRuntimeSubnet.id diff --git a/Environments/Spring/main.bicep b/Environments/Spring/main.bicep index 445798b6..66e17608 100644 --- a/Environments/Spring/main.bicep +++ b/Environments/Spring/main.bicep @@ -1,118 +1,72 @@ @minLength(1) @maxLength(64) -@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +@description('Name which is used to generate a short unique hash for each resource') param environmentName string = 'test' -@minLength(1) -@description('Primary location for all resources') -param location string = resourceGroup().location +@description('The instance name of the Azure Spring Cloud resource') +param springCloudInstanceName string = '' + +@description('The name of the Application Insights instance for Azure Spring Cloud') +param appInsightsName string = '' + +param logAnalyticsWorkspaceName string = '' + +@description('The resourceID of the Azure Spring Cloud App Subnet') +param springCloudAppSubnetID string = '' + +@description('The resourceID of the Azure Spring Cloud Runtime Subnet') +param springCloudRuntimeSubnetID string = '' + +param springCloudServiceCidrs string = '10.20.0.0/16,10.21.0.0/16,10.22.0.1/16' -param appName string = '' -param applicationInsightsDashboardName string = '' -param applicationInsightsName string = '' -param appServicePlanName string = '' -param mySqlServerName string = '' -param mySqlServerAdminName string = 'petclinic' -@secure() -param mySqlServerAdminPassword string -param mySqlDatabaseName string = 'petclinic' -param keyVaultName string = '' -param logAnalyticsName string = '' - -@description('Id of the user or app to assign application roles') -param principalId string = '' - -var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(resourceGroup().id, location)) -var tags = { 'azd-env-name': environmentName } -// Create an App Service Plan to group applications under the same payment plan and SKU -module appServicePlan './core/host/appserviceplan.bicep' = { - name: 'appserviceplan' - params: { - name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' - location: location - tags: tags - sku: { - name: 'B1' - } - } -} +@description('The name of the Virtual Network') +param vnetName string = '' -// Monitor application with Azure Monitor -module monitoring './core/monitor/monitoring.bicep' = { - name: 'monitoring' - params: { - location: location - tags: tags - logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' - applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' - applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' - } -} +@description('the app subnet name of the Azure Spring Cloud') +param ascAppSubnetName string = '' -// Store secrets in a keyvault -module keyVault './core/security/keyvault.bicep' = { - name: 'keyvault' - params: { - name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' - location: location - tags: tags - principalId: principalId - } -} +@description('the runtime subnet name of the Azure Spring Cloud') +param ascRuntimeSubnetName string = '' -// The application database -module mySql './core/database/mysql/mysql-db.bicep' = { - name: 'mysql-db' - params: { +@description('The address prefixes of the vnet') +param vnetAddressPrefixes string = '10.4.0.0/16' + +@description('The Azure Spring Cloud App subnet address prefixes in the vnet') +param ascAppSubnetAddressPrefixes string = '10.4.0.0/24' + +@description('The Azure Spring Cloud Runtime subnet address prefixes in the vnet') +param ascRuntimeSubnetAddressPrefixes string = '10.4.1.0/24' + +param location string = resourceGroup().location + +var tags = { 'env-name': environmentName } + +module vnet 'core/network/vnet.bicep' = { + name: 'vnet' + params:{ + vnetName: !empty(vnetName) ? vnetName : 'vnet-${resourceToken}' location: location + ascAppSubnetName: !empty(ascAppSubnetName) ? ascAppSubnetName : 'app-sub-${resourceToken}' + ascRuntimeSubnetName: !empty(ascRuntimeSubnetName) ? ascRuntimeSubnetName : 'runtime-sub-${resourceToken}' + vnetAddressPrefixes: vnetAddressPrefixes + ascAppSubnetAddressPrefixes: ascAppSubnetAddressPrefixes + ascRuntimeSubnetAddressPrefixes: ascRuntimeSubnetAddressPrefixes tags: tags - serverName: !empty(mySqlServerName) ? mySqlServerName : '${abbrs.dBforMySQLServers}${resourceToken}' - serverAdminName: mySqlServerAdminName - serverAdminPassword: mySqlServerAdminPassword - databaseName: !empty(mySqlDatabaseName) ? mySqlDatabaseName : 'petclinic' - keyVaultName: keyVault.outputs.name } } -// The application backend -module app './app/app.bicep' = { - name: 'app' +module springcloud 'core/host/springapps.bicep' = { + name: 'springcloud' params: { - name: !empty(appName) ? appName : '${abbrs.webSitesAppService}petclinic-${resourceToken}' + springCloudInstanceName: !empty(springCloudInstanceName) ? springCloudInstanceName : 'asa-${resourceToken}' location: location + appInsightsName: !empty(appInsightsName) ? appInsightsName : 'appi-${resourceToken}' + logAnalyticsWorkspaceName: !empty(logAnalyticsWorkspaceName) ? logAnalyticsWorkspaceName : 'log-${resourceToken}' + springCloudAppSubnetID: !empty(springCloudAppSubnetID) ? springCloudAppSubnetID : vnet.outputs.ascAppSubnetId + springCloudRuntimeSubnetID: !empty(springCloudRuntimeSubnetID) ? springCloudRuntimeSubnetID : vnet.outputs.ascRuntimeSubetId + springCloudServiceCidrs: springCloudServiceCidrs tags: tags - applicationInsightsName: monitoring.outputs.applicationInsightsName - appServicePlanId: appServicePlan.outputs.id - keyVaultName: keyVault.outputs.name - appSettings: { - APPLICATIONINSIGHTS_CONNECTION_STRING: monitoring.outputs.applicationInsightsConnectionString - AZURE_KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint - SPRING_PROFILES_ACTIVE: 'azure,mysql' - MYSQL_URL: mySql.outputs.endpoint - MYSQL_USER: mySqlServerAdminName - } - } -} - -// Give the API access to KeyVault -module appKeyVaultAccess './core/security/keyvault-access.bicep' = { - name: 'app-keyvault-access' - params: { - keyVaultName: keyVault.outputs.name - principalId: app.outputs.APP_IDENTITY_PRINCIPAL_ID } } - -// Data outputs -output MYSQL_URL string = mySql.outputs.endpoint -output MYSQL_USER string = mySqlServerAdminName - -// App outputs -output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString -output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint -output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name -output AZURE_LOCATION string = location -output AZURE_TENANT_ID string = tenant().tenantId -output SPRING_PROFILES_ACTIVE string = 'azure,mysql' diff --git a/Environments/Spring/manifest.yaml b/Environments/Spring/manifest.yaml index 953ebbf8..4ed00f27 100644 --- a/Environments/Spring/manifest.yaml +++ b/Environments/Spring/manifest.yaml @@ -1,7 +1,7 @@ name: Spring version: 1.0.0 -summary: Spring Petclinic -description: Deploy Spring Petlinic Java app with MySQL +summary: Spring Apps +description: Deploy Spring Apps Java app runner: ARM templatePath: azuredeploy.json @@ -13,8 +13,58 @@ parameters: required: false default: "test" -- id: "mySqlServerAdminPassword" - name: "mySqlServerAdminPassword" - description: "MySQL Admin Password" +- id: "springCloudInstanceName" + name: "springCloudInstanceName" + description: "Name of Spring Apps instance" type: "string" - required: true \ No newline at end of file + required: false + default: "" + +- id: "vnetName" + name: "vnetName" + description: "Name of Spring Apps VNet" + type: "string" + required: false + default: "" + +- id: "vnetAddressPrefixes" + name: "vnetAddressPrefixes" + description: "VNet address Prefixes for Spring Apps" + type: "string" + required: true + default: "10.4.0.0/16" + +- id: "ascAppSubnetName" + name: "ascAppSubnetName" + description: "Name of Spring Apps application subnet" + type: "string" + required: false + default: "" + +- id: "ascAppSubnetAddressPrefixes" + name: "ascAppSubnetAddressPrefixes" + description: "Address prefixes of Spring Apps application subnet" + type: "string" + required: true + default: "10.4.0.0/24" + +- id: "ascRuntimeSubnetName" + name: "ascRuntimeSubnetName" + description: "Name of Spring Apps runtime subnet" + type: "string" + required: false + default: "" + +- id: "ascRuntimeSubnetAddressPrefixes" + name: "ascRuntimeSubnetAddressPrefixes" + description: "Address prefixes of Spring Apps runtime subnet" + type: "string" + required: true + default: "10.4.1.0/24" + +- id: "springCloudServiceCidrs" + name: "springCloudServiceCidrs" + description: "CIDR of Spring Apps" + type: "string" + required: true + default: "10.20.0.0/16,10.21.0.0/16,10.22.0.1/16" \ No newline at end of file From 3177d0f4c2272af30d9a2452d7e7c1796b782bab Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 15 May 2023 11:06:32 +0800 Subject: [PATCH 042/112] make parameter optional --- Environments/Spring/manifest.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Environments/Spring/manifest.yaml b/Environments/Spring/manifest.yaml index 4ed00f27..31835cc3 100644 --- a/Environments/Spring/manifest.yaml +++ b/Environments/Spring/manifest.yaml @@ -31,7 +31,7 @@ parameters: name: "vnetAddressPrefixes" description: "VNet address Prefixes for Spring Apps" type: "string" - required: true + required: false default: "10.4.0.0/16" - id: "ascAppSubnetName" @@ -45,7 +45,7 @@ parameters: name: "ascAppSubnetAddressPrefixes" description: "Address prefixes of Spring Apps application subnet" type: "string" - required: true + required: false default: "10.4.0.0/24" - id: "ascRuntimeSubnetName" @@ -59,12 +59,12 @@ parameters: name: "ascRuntimeSubnetAddressPrefixes" description: "Address prefixes of Spring Apps runtime subnet" type: "string" - required: true + required: false default: "10.4.1.0/24" - id: "springCloudServiceCidrs" name: "springCloudServiceCidrs" description: "CIDR of Spring Apps" type: "string" - required: true + required: false default: "10.20.0.0/16,10.21.0.0/16,10.22.0.1/16" \ No newline at end of file From 9f3b1d73a467b266df0414f5e26796051ceb6c21 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 15 May 2023 13:43:25 +0800 Subject: [PATCH 043/112] add catalog items --- Environments/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Environments/README.md b/Environments/README.md index 2df66dcc..97be7a74 100644 --- a/Environments/README.md +++ b/Environments/README.md @@ -7,7 +7,14 @@ The sample Catalog consists of a few catalog items (ARM Template + associated ma - [Function App](FunctionApp): Deploys an Azure Function App, Storage Account, and Application Insights - [Sandbox](Sandbox): Deploys an empty "sandbox" environment - [Web App](WebApp): Deploys an Azure Web App without a data store -- [OpenAI Search](OpenAISearch): Deploys Azure Web App, OpenAI service and Cogonitive search +- [OpenAI Search](OpenAISearch): Deploys Azure Web App, OpenAI service and Cogonitive search +- [OpenAI Summarization](OpenAISummarization): Deploys Azure OpenAI along with Azure Cognitive Search, Azure Storage +- [AKS](AKS): Deploys Azure Kubernetes Service with MongoDB +- [Container App](ContainerApp): Deploys Container Apps with Azure Key Vault and Container Registry +- [Spring Apps](Spring): Deploys Spring Apps +- [APIM](APIM): Deploys API Management +- [AppVNet](AppVNet): Deploys App Service in Private Network +- [Static Web](StaticWeb): Deploys Static Web with Function App and MongoDB ## ARM and Bicep From 2d314274b6d6ad3503416eb1a7ae7aecf4104f49 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 19 May 2023 13:30:41 +0800 Subject: [PATCH 044/112] Give better name for parameters and examples --- Environments/AKS/manifest.yaml | 2 +- Environments/APIM/manifest.yaml | 6 +++--- Environments/AppVNet/manifest.yaml | 6 +++--- Environments/ContainerApp/manifest.yaml | 4 ++-- Environments/OpenAISearch/manifest.yaml | 12 ++++++------ Environments/OpenAISummarization/manifest.yaml | 12 ++++++------ Environments/Spring/manifest.yaml | 18 +++++++++--------- Environments/StaticWeb/manifest.yaml | 4 ++-- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Environments/AKS/manifest.yaml b/Environments/AKS/manifest.yaml index 2b2bc6f3..e5bd6f6d 100644 --- a/Environments/AKS/manifest.yaml +++ b/Environments/AKS/manifest.yaml @@ -7,7 +7,7 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false diff --git a/Environments/APIM/manifest.yaml b/Environments/APIM/manifest.yaml index 9e4ae541..31707131 100644 --- a/Environments/APIM/manifest.yaml +++ b/Environments/APIM/manifest.yaml @@ -7,20 +7,20 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false default: "test" - id: "publisherEmail" - name: "publisherEmail" + name: "Publisher Email" description: "Eamil of the publisher" type: "string" required: true - id: "publisherName" - name: "publisherName" + name: "Publisher Name" description: "Name of the publisher" type: "string" required: true \ No newline at end of file diff --git a/Environments/AppVNet/manifest.yaml b/Environments/AppVNet/manifest.yaml index 46809bed..0800799d 100644 --- a/Environments/AppVNet/manifest.yaml +++ b/Environments/AppVNet/manifest.yaml @@ -7,20 +7,20 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false default: "test" - id: "databasePassword" - name: "databasePassword" + name: "Database Password" description: "Password of PostgreSQL" type: "string" required: true - id: "secretKey" - name: "secretKey" + name: "Secret Key" description: "Django SECRET_KEY for securing signed data" type: "string" required: true \ No newline at end of file diff --git a/Environments/ContainerApp/manifest.yaml b/Environments/ContainerApp/manifest.yaml index c07d3524..fc6d9165 100644 --- a/Environments/ContainerApp/manifest.yaml +++ b/Environments/ContainerApp/manifest.yaml @@ -7,14 +7,14 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false default: "test" - id: "principalId" - name: "principalId" + name: "Principal Id (e.g. )" description: "Id of the user or app to assign application roles" type: "string" required: false diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index 6322e2cb..6e7ca783 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -7,41 +7,41 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false default: "test" - id: "principalId" - name: "principalId" + name: "Principal Id (e.g. )" description: "Id of the user or app to assign application roles" type: "string" required: true - id: "backendServiceName" - name: "backendServiceName" + name: "Backend Service Name (e.g. app-aisearch)" description: "Name of app service" type: "string" required: false default: "" - id: "storageAccountName" - name: "storageAccountName" + name: "Storage Account Name (e.g. staisearch)" description: "Name of storage account" type: "string" required: false default: "" - id: "searchServiceName" - name: "searchServiceName" + name: "Search Service Name (e.g. gptkb-aisearch)" description: "Name of search service" type: "string" required: false default: "" - id: "formRecognizerServiceName" - name: "formRecognizerServiceName" + name: "Form Recognizer Service Name (e.g. cog-fr-aisearch)" description: "Name of form recognizer service" type: "string" required: false diff --git a/Environments/OpenAISummarization/manifest.yaml b/Environments/OpenAISummarization/manifest.yaml index d648fbbe..394d18ff 100644 --- a/Environments/OpenAISummarization/manifest.yaml +++ b/Environments/OpenAISummarization/manifest.yaml @@ -7,41 +7,41 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false default: "test" - id: "location" - name: "location" + name: "Location (e.g. southcentralus or westeurope)" description: "location or region" type: "string" required: false default: "westeurope" - id: "principalId" - name: "principalId" + name: "Principal Id (e.g. )" description: "Id of the user or app to assign application roles" type: "string" required: true - id: "openAiAccountName" - name: "openAiAccountName" + name: "OpenAI Account Name (e.g. cog-summarization)" description: "Name of Open AI account" type: "string" required: false default: "" - id: "storageAccountName" - name: "storageAccountName" + name: "Storage Account Name (e.g. stsummarization)" description: "Name of storage account" type: "string" required: false default: "" - id: "searchServicesName" - name: "searchServicesName" + name: "Search Services Name (e.g. srch-summarization)" description: "Name of search service" type: "string" required: false diff --git a/Environments/Spring/manifest.yaml b/Environments/Spring/manifest.yaml index 31835cc3..539947f0 100644 --- a/Environments/Spring/manifest.yaml +++ b/Environments/Spring/manifest.yaml @@ -7,63 +7,63 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false default: "test" - id: "springCloudInstanceName" - name: "springCloudInstanceName" + name: "Spring Apps Instance Name (e.g. asa-petlicnic)" description: "Name of Spring Apps instance" type: "string" required: false default: "" - id: "vnetName" - name: "vnetName" + name: "Visual Network Name (e.g. vnet-petlicnic)" description: "Name of Spring Apps VNet" type: "string" required: false default: "" - id: "vnetAddressPrefixes" - name: "vnetAddressPrefixes" + name: "Virtual Network Address Prefixes" description: "VNet address Prefixes for Spring Apps" type: "string" required: false default: "10.4.0.0/16" - id: "ascAppSubnetName" - name: "ascAppSubnetName" + name: "Spring Apps Application Subnet Name (e.g. app-subnet)" description: "Name of Spring Apps application subnet" type: "string" required: false default: "" - id: "ascAppSubnetAddressPrefixes" - name: "ascAppSubnetAddressPrefixes" + name: "Spring Apps Application Subnet Address Prefixes" description: "Address prefixes of Spring Apps application subnet" type: "string" required: false default: "10.4.0.0/24" - id: "ascRuntimeSubnetName" - name: "ascRuntimeSubnetName" + name: "Spring Apps Runtime Subnet Name (e.g. runtime-subnet)" description: "Name of Spring Apps runtime subnet" type: "string" required: false default: "" - id: "ascRuntimeSubnetAddressPrefixes" - name: "ascRuntimeSubnetAddressPrefixes" + name: "Spring Apps Runtime Subnet Address Prefixes" description: "Address prefixes of Spring Apps runtime subnet" type: "string" required: false default: "10.4.1.0/24" - id: "springCloudServiceCidrs" - name: "springCloudServiceCidrs" + name: "Spring Apps Service CIDRs" description: "CIDR of Spring Apps" type: "string" required: false diff --git a/Environments/StaticWeb/manifest.yaml b/Environments/StaticWeb/manifest.yaml index 330bf8ca..4e2c7971 100644 --- a/Environments/StaticWeb/manifest.yaml +++ b/Environments/StaticWeb/manifest.yaml @@ -7,14 +7,14 @@ templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "environmentName" + name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" required: false default: "test" - id: "location" - name: "location" + name: "Location (e.g. westus2, centralus, eastus2, westeurope, eastasia or eastasiastage)" description: "location or region" type: "string" required: false From 10baa73c97fe3f53aacf7173cfab7219c653109a Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 19 May 2023 13:51:11 +0800 Subject: [PATCH 045/112] fix typo --- Environments/Spring/manifest.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Environments/Spring/manifest.yaml b/Environments/Spring/manifest.yaml index 539947f0..24e29194 100644 --- a/Environments/Spring/manifest.yaml +++ b/Environments/Spring/manifest.yaml @@ -14,14 +14,14 @@ parameters: default: "test" - id: "springCloudInstanceName" - name: "Spring Apps Instance Name (e.g. asa-petlicnic)" + name: "Spring Apps Instance Name (e.g. asa-petclinic001)" description: "Name of Spring Apps instance" type: "string" required: false default: "" - id: "vnetName" - name: "Visual Network Name (e.g. vnet-petlicnic)" + name: "Visual Network Name (e.g. vnet-petclinic001)" description: "Name of Spring Apps VNet" type: "string" required: false From 59bb7acfb3129a319c8c8b231ad2df7e92debb5d Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Tue, 13 Jun 2023 22:55:53 +0800 Subject: [PATCH 046/112] add parameter in metadata --- Environments/OpenAISearch/manifest.yaml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index 6e7ca783..fa4efdbb 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -19,6 +19,13 @@ parameters: type: "string" required: true +- id: "appServicePlanName" + name: "App service plan Name (e.g. plan-aisearch)" + description: "Name of App service plan" + type: "string" + required: false + default: "" + - id: "backendServiceName" name: "Backend Service Name (e.g. app-aisearch)" description: "Name of app service" @@ -45,4 +52,11 @@ parameters: description: "Name of form recognizer service" type: "string" required: false - default: "" \ No newline at end of file + default: "" + +- id: "openAiServiceName" + name: "OpenAI Service Name (e.g. cog-aisearch)" + description: "Name of OpenAI service" + type: "string" + required: false + default: "" From 0afafbbc02213c8a3cb4f98be4e7b3d536927d76 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Tue, 20 Jun 2023 14:19:32 +0800 Subject: [PATCH 047/112] add location --- Environments/OpenAISearch/manifest.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index fa4efdbb..c14e0a5d 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -60,3 +60,10 @@ parameters: type: "string" required: false default: "" + +- id: "region" + name: "Region (e.g. eastus)" + description: "Region" + type: "string" + required: false + default: "eastus" From 2c6b457aa1a42b8888054617627d32ec1e98473f Mon Sep 17 00:00:00 2001 From: luxu-ms <92287281+luxu-ms@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:58:07 +0800 Subject: [PATCH 048/112] Update manifest.yaml --- Environments/OpenAISearch/manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index c14e0a5d..2a13d11b 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -61,7 +61,7 @@ parameters: required: false default: "" -- id: "region" +- id: "location" name: "Region (e.g. eastus)" description: "Region" type: "string" From 877f806467f91f57bff495d9a9267e765379f768 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 23 Feb 2024 10:36:55 +0800 Subject: [PATCH 049/112] add todo nodejs mongo aca --- .../Todo-Nodejs-Mongo-ACA/abbreviations.json | 136 + .../Todo-Nodejs-Mongo-ACA/app/api.bicep | 75 + .../app/apim-api-policy.xml | 92 + .../Todo-Nodejs-Mongo-ACA/app/apim-api.bicep | 120 + .../Todo-Nodejs-Mongo-ACA/app/db.bicep | 40 + .../Todo-Nodejs-Mongo-ACA/app/openapi.yaml | 312 ++ .../Todo-Nodejs-Mongo-ACA/app/web.bicep | 54 + .../Todo-Nodejs-Mongo-ACA/azuredeploy.json | 4679 +++++++++++++++++ .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 + .../core/gateway/apim.bicep | 79 + .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 + .../Todo-Nodejs-Mongo-ACA/core/host/aks.bicep | 280 + .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 + .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 + .../core/host/container-app.bicep | 162 + .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 + .../core/host/functions.bicep | 86 + .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + Environments/Todo-Nodejs-Mongo-ACA/main.bicep | 178 + .../main.parameters.json | 27 + .../Todo-Nodejs-Mongo-ACA/manifest.yaml | 54 + 51 files changed, 9230 insertions(+) create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/abbreviations.json create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/app/api.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/app/apim-api-policy.xml create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/app/apim-api.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/app/db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/app/openapi.yaml create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/app/web.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/gateway/apim.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-agent-pool.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-managed-cluster.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/aks.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice-appsettings.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/appserviceplan.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app-upsert.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps-environment.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/container-registry.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/functions.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/host/staticwebapp.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/monitor/loganalytics.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/monitor/monitoring.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-endpoint.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-profile.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/search/search-services.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-access.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-secret.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/security/registry-access.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/security/role.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/core/testing/loadtesting.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/main.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/main.parameters.json create mode 100644 Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml diff --git a/Environments/Todo-Nodejs-Mongo-ACA/abbreviations.json b/Environments/Todo-Nodejs-Mongo-ACA/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Todo-Nodejs-Mongo-ACA/app/api.bicep b/Environments/Todo-Nodejs-Mongo-ACA/app/api.bicep new file mode 100644 index 00000000..1df68145 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/app/api.bicep @@ -0,0 +1,75 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param keyVaultName string +param serviceName string = 'api' +param corsAcaUrl string +param exists bool + +resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +// Give the API access to KeyVault +module apiKeyVaultAccess '../core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: apiIdentity.properties.principalId + } +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + dependsOn: [ apiKeyVaultAccess ] + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: apiIdentity.name + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: '1.0' + containerMemory: '2.0Gi' + env: [ + { + name: 'AZURE_CLIENT_ID' + value: apiIdentity.properties.clientId + } + { + name: 'AZURE_KEY_VAULT_ENDPOINT' + value: keyVault.properties.vaultUri + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'API_ALLOW_ORIGINS' + value: corsAcaUrl + } + ] + targetPort: 3100 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Todo-Nodejs-Mongo-ACA/app/apim-api-policy.xml b/Environments/Todo-Nodejs-Mongo-ACA/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +

    *
    + + +
    *
    +
    + + + + + + + + Call to the @(context.Api.Name) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + + diff --git a/Environments/Todo-Nodejs-Mongo-ACA/app/apim-api.bicep b/Environments/Todo-Nodejs-Mongo-ACA/app/apim-api.bicep new file mode 100644 index 00000000..b6008404 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('./openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/Todo-Nodejs-Mongo-ACA/app/db.bicep b/Environments/Todo-Nodejs-Mongo-ACA/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Nodejs-Mongo-ACA/app/openapi.yaml b/Environments/Todo-Nodejs-Mongo-ACA/app/openapi.yaml new file mode 100644 index 00000000..c42e99ca --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/app/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid diff --git a/Environments/Todo-Nodejs-Mongo-ACA/app/web.bicep b/Environments/Todo-Nodejs-Mongo-ACA/app/web.bicep new file mode 100644 index 00000000..2c692278 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/app/web.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param apiBaseUrl string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param serviceName string = 'web' +param exists bool + +resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: identityName + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + env: [ + { + name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'REACT_APP_API_BASE_URL' + value: apiBaseUrl + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + ] + targetPort: 80 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId +output SERVICE_WEB_NAME string = app.outputs.name +output SERVICE_WEB_URI string = app.outputs.uri +output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json new file mode 100644 index 00000000..65c1d9f7 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json @@ -0,0 +1,4679 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5915759848307494038" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "apiContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "webContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "apimServiceName": { + "type": "string", + "defaultValue": "" + }, + "apiAppExists": { + "type": "bool", + "defaultValue": false + }, + "webAppExists": { + "type": "bool", + "defaultValue": false + }, + "useAPIM": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "webApiBaseUrl": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The base URL used by the web service for sending API requests" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "containerRegistryAdminUserEnabled": { + "value": true + }, + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14116108111976192358" + }, + "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "containerRegistryResourceGroupName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryAdminUserEnabled": { + "type": "bool", + "defaultValue": false + }, + "logAnalyticsWorkspaceName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4766903245227392386" + }, + "description": "Creates an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Application Insights resource" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-registry', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "adminUserEnabled": { + "value": "[parameters('containerRegistryAdminUserEnabled')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" + }, + "registryLoginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.loginServer.value]" + }, + "registryName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "web", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('webContainerAppName'))), createObject('value', parameters('webContainerAppName')), createObject('value', format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}web-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "apiBaseUrl": "[if(not(empty(parameters('webApiBaseUrl'))), createObject('value', parameters('webApiBaseUrl')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value))]", + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "exists": { + "value": "[parameters('webAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5244656399300381833" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "apiBaseUrl": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "web" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "env": { + "value": [ + { + "name": "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "REACT_APP_API_BASE_URL", + "value": "[parameters('apiBaseUrl')]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + } + ] + }, + "targetPort": { + "value": 80 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + } + } + ], + "outputs": { + "SERVICE_WEB_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_WEB_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_WEB_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apiContainerAppName'))), createObject('value', parameters('apiContainerAppName')), createObject('value', format('{0}api-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}api-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "corsAcaUrl": { + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "exists": { + "value": "[parameters('apiAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11092891629527222377" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "keyVaultName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "api" + }, + "corsAcaUrl": { + "type": "string" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "1.0" + }, + "containerMemory": { + "value": "2.0Gi" + }, + "env": { + "value": [ + { + "name": "AZURE_CLIENT_ID", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').clientId]" + }, + { + "name": "AZURE_KEY_VAULT_ENDPOINT", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "API_ALLOW_ORIGINS", + "value": "[parameters('corsAcaUrl')]" + } + ] + }, + "targetPort": { + "value": 3100 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]", + "[resourceId('Microsoft.Resources/deployments', 'api-keyvault-access')]" + ] + } + ], + "outputs": { + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_API_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_API_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5730728686647632614" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14549161001187918251" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8317058180807592714" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apimServiceName'))), createObject('value', parameters('apimServiceName')), createObject('value', format('{0}{1}', variables('abbrs').apiManagementService, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "3036576769636454145" + }, + "description": "Creates an Azure API Management instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "publisherEmail": { + "type": "string", + "defaultValue": "noreply@microsoft.com", + "minLength": 1, + "metadata": { + "description": "The email address of the owner of the service" + } + }, + "publisherName": { + "type": "string", + "defaultValue": "n/a", + "minLength": 1, + "metadata": { + "description": "The name of the owner of the service" + } + }, + "sku": { + "type": "string", + "defaultValue": "Consumption", + "allowedValues": [ + "Consumption", + "Developer", + "Standard", + "Premium" + ], + "metadata": { + "description": "The pricing tier of this API Management service" + } + }, + "skuCount": { + "type": "int", + "defaultValue": 0, + "allowedValues": [ + 0, + 1, + 2 + ], + "metadata": { + "description": "The instance size of this API Management service." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "Azure Application Insights Name" + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2021-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('azd-service-name', parameters('name')))]", + "sku": { + "name": "[parameters('sku')]", + "capacity": "[if(equals(parameters('sku'), 'Consumption'), 0, if(equals(parameters('sku'), 'Developer'), 1, parameters('skuCount')))]" + }, + "properties": { + "publisherEmail": "[parameters('publisherEmail')]", + "publisherName": "[parameters('publisherName')]", + "customProperties": "[if(equals(parameters('sku'), 'Consumption'), createObject(), createObject('Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30', 'false'))]" + } + }, + { + "condition": "[not(empty(parameters('applicationInsightsName')))]", + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'app-insights-logger')]", + "properties": { + "credentials": { + "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" + }, + "description": "Logger to Azure Application Insights", + "isBuffered": false, + "loggerType": "applicationInsights", + "resourceId": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" + ] + } + ], + "outputs": { + "apimServiceName": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-api-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(parameters('useAPIM'), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'apim-deployment'), '2022-09-01').outputs.apimServiceName.value), createObject('value', ''))]", + "apiName": { + "value": "todo-api" + }, + "apiDisplayName": { + "value": "Simple Todo API" + }, + "apiDescription": { + "value": "This is a simple Todo API" + }, + "apiPath": { + "value": "todo" + }, + "webFrontendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "apiBackendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6615097664318461925" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "apiName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Resource name to uniquely identify this API within the API Management service instance" + } + }, + "apiDisplayName": { + "type": "string", + "minLength": 1, + "maxLength": 300, + "metadata": { + "description": "The Display Name of the API" + } + }, + "apiDescription": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Description of the API. May include HTML formatting tags." + } + }, + "apiPath": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + } + }, + "webFrontendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the web frontend" + } + }, + "apiBackendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the backend service implementing this API." + } + }, + "apiAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Resource name for backend Web App or Function App" + } + } + }, + "variables": { + "$fxv#0": "\n\n \n \n \n \n \n {origin}\n \n \n PUT\n GET\n POST\n DELETE\n PATCH\n \n \n
    *
    \n
    \n \n
    *
    \n
    \n
    \n \n \n \n \n \n \n Call to the @(context.Api.Name)\n \n \n \n \n \n
    \n \n \n \n \n \n \n \n \n \n \n \n = 200 && context.Response.StatusCode < 300)\">\n \n \n \n \n \n \n \n = 400 && context.Response.StatusCode < 600)\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Failed to process the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n An unexpected error has occurred.\n \n \n
    \n", + "$fxv#1": "openapi: 3.0.0\ninfo:\n description: Simple Todo API\n version: 3.0.0\n title: Simple Todo API\n contact:\n email: azdevteam@microsoft.com\n\ncomponents:\n schemas:\n TodoItem:\n type: object\n required:\n - listId\n - name\n - description\n description: A task that needs to be completed\n properties:\n id:\n type: string\n listId:\n type: string\n name:\n type: string\n description:\n type: string\n state:\n $ref: \"#/components/schemas/TodoState\"\n dueDate:\n type: string\n format: date-time\n completedDate:\n type: string\n format: date-time\n TodoList:\n type: object\n required:\n - name\n properties:\n id:\n type: string\n name:\n type: string\n description:\n type: string\n description: \" A list of related Todo items\"\n TodoState:\n type: string\n enum:\n - todo\n - inprogress\n - done\n parameters:\n listId:\n in: path\n required: true\n name: listId\n description: The Todo list unique identifier\n schema:\n type: string\n itemId:\n in: path\n required: true\n name: itemId\n description: The Todo item unique identifier\n schema:\n type: string\n state:\n in: path\n required: true\n name: state\n description: The Todo item state\n schema:\n $ref: \"#/components/schemas/TodoState\"\n top:\n in: query\n required: false\n name: top\n description: The max number of items to returns in a result\n schema:\n type: number\n default: 20\n skip:\n in: query\n required: false\n name: skip\n description: The number of items to skip within the results\n schema:\n type: number\n default: 0\n\n requestBodies:\n TodoList:\n description: The Todo List\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: The Todo Item\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n\n responses:\n TodoList:\n description: A Todo list result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoListArray:\n description: An array of Todo lists\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: A Todo item result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n TodoItemArray:\n description: An array of Todo items\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoItem\"\n\npaths:\n /lists:\n get:\n operationId: GetLists\n summary: Gets an array of Todo lists\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoListArray\"\n post:\n operationId: CreateList\n summary: Creates a new Todo list\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoList\"\n 400:\n description: Invalid request schema\n /lists/{listId}:\n get:\n operationId: GetListById\n summary: Gets a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n put:\n operationId: UpdateListById\n summary: Updates a Todo list by unique identifier\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n 400:\n description: Todo list is invalid\n delete:\n operationId: DeleteListById\n summary: Deletes a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 204:\n description: Todo list deleted successfully\n 404:\n description: Todo list not found\n /lists/{listId}/items:\n post:\n operationId: CreateItem\n summary: Creates a new Todo item within a list\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list not found\n get:\n operationId: GetItemsByListId\n summary: Gets Todo items within the specified list\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list not found\n /lists/{listId}/items/{itemId}:\n get:\n operationId: GetItemById\n summary: Gets a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemById\n summary: Updates a Todo item by unique identifier\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 400:\n description: Todo item is invalid\n 404:\n description: Todo list or item not found\n delete:\n operationId: DeleteItemById\n summary: Deletes a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 204:\n description: Todo item deleted successfully\n 404:\n description: Todo list or item not found\n /lists/{listId}/items/state/{state}:\n get:\n operationId: GetItemsByListIdAndState\n summary: Gets a list of Todo items of a specific state\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemsStateByListId\n summary: Changes the state of the specified list items\n tags:\n - Items\n requestBody:\n description: unique identifiers of the Todo items to update\n content:\n application/json:\n schema:\n type: array\n items:\n description: The Todo item unique identifier\n type: string\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n responses:\n 204:\n description: Todo items updated\n 400:\n description: Update request is invalid\n", + "apiPolicyContent": "[replace(variables('$fxv#0'), '{origin}', parameters('webFrontendUrl'))]", + "appNameForBicep": "[if(not(empty(parameters('apiAppName'))), parameters('apiAppName'), 'placeholderName')]" + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), parameters('apiName'))]", + "properties": { + "description": "[parameters('apiDescription')]", + "displayName": "[parameters('apiDisplayName')]", + "path": "[parameters('apiPath')]", + "protocols": [ + "https" + ], + "subscriptionRequired": false, + "type": "http", + "format": "openapi", + "serviceUrl": "[parameters('apiBackendUrl')]", + "value": "[variables('$fxv#1')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'policy')]", + "properties": { + "format": "rawxml", + "value": "[variables('apiPolicyContent')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'applicationinsights')]", + "properties": { + "alwaysLog": "allErrors", + "backend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "frontend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "httpCorrelationProtocol": "W3C", + "logClientIp": true, + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('name'), 'app-insights-logger')]", + "metrics": true, + "sampling": { + "percentage": 100, + "samplingType": "fixed" + }, + "verbosity": "verbose" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "condition": "[not(empty(parameters('apiAppName')))]", + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/web', variables('appNameForBicep'))]", + "kind": "string", + "properties": { + "apiManagementConfig": { + "id": "[format('{0}/apis/{1}', resourceId('Microsoft.ApiManagement/service', parameters('name')), parameters('apiName'))]" + } + } + } + ], + "outputs": { + "SERVICE_API_URI": { + "type": "string", + "value": "[format('{0}/{1}', reference(resourceId('Microsoft.ApiManagement/service', parameters('name')), '2021-08-01').gatewayUrl, parameters('apiPath'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'apim-deployment')]", + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "API_CORS_ACA_URL": { + "type": "string", + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "APPLICATIONINSIGHTS_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_API_BASE_URL": { + "type": "string", + "value": "[if(parameters('useAPIM'), reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "REACT_APP_WEB_BASE_URL": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_NAME.value]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_NAME.value]" + }, + "USE_APIM": { + "type": "bool", + "value": "[parameters('useAPIM')]" + }, + "SERVICE_API_ENDPOINTS": { + "type": "array", + "value": "[if(parameters('useAPIM'), createArray(reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value), createArray())]" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/postgresql/flexibleserver.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/sqlserver/sqlserver.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/gateway/apim.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-agent-pool.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-managed-cluster.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks.bicep new file mode 100644 index 00000000..536a534b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/aks.bicep @@ -0,0 +1,280 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice-appsettings.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/appserviceplan.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app-upsert.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps-environment.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-registry.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/functions.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/host/staticwebapp.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/loganalytics.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/monitoring.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-endpoint.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-profile.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/search/search-services.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/security/aks-managed-cluster-access.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-access.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-secret.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/security/registry-access.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/security/role.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/testing/loadtesting.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/Todo-Nodejs-Mongo-ACA/main.bicep b/Environments/Todo-Nodejs-Mongo-ACA/main.bicep new file mode 100644 index 00000000..25088009 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/main.bicep @@ -0,0 +1,178 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param apiContainerAppName string = '' +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param containerAppsEnvironmentName string = '' +param containerRegistryName string = '' +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param logAnalyticsName string = '' +param webContainerAppName string = '' +param apimServiceName string = '' +param apiAppExists bool = false +param webAppExists bool = false + +@description('Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API') +param useAPIM bool = false + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('The base URL used by the web service for sending API requests') +param webApiBaseUrl string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } +var apiContainerAppNameOrDefault = '${abbrs.appContainerApps}web-${resourceToken}' +var corsAcaUrl = 'https://${apiContainerAppNameOrDefault}.${containerApps.outputs.defaultDomain}' + +// Container apps host (including container registry) +module containerApps './core/host/container-apps.bicep' = { + name: 'container-apps' + params: { + name: 'app' + location: location + tags: tags + containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + // Work around Azure/azure-dev#3157 (the root cause of which is Azure/acr#723) by explicitly enabling the admin user to allow users which + // don't have the `Owner` role granted (and instead are classic administrators) to access the registry to push even if AAD authentication fails. + // + // This addresses the following error during deploy: + // + // failed getting ACR token: POST https://.azurecr.io/oauth2/exchange 401 Unauthorized + containerRegistryAdminUserEnabled: true + logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Web frontend +module web './app/web.bicep' = { + name: 'web' + params: { + name: !empty(webContainerAppName) ? webContainerAppName : '${abbrs.appContainerApps}web-${resourceToken}' + location: location + tags: tags + identityName: '${abbrs.managedIdentityUserAssignedIdentities}web-${resourceToken}' + apiBaseUrl: !empty(webApiBaseUrl) ? webApiBaseUrl : api.outputs.SERVICE_API_URI + applicationInsightsName: monitoring.outputs.applicationInsightsName + containerAppsEnvironmentName: containerApps.outputs.environmentName + containerRegistryName: containerApps.outputs.registryName + exists: webAppExists + } +} + +// Api backend +module api './app/api.bicep' = { + name: 'api' + params: { + name: !empty(apiContainerAppName) ? apiContainerAppName : '${abbrs.appContainerApps}api-${resourceToken}' + location: location + tags: tags + identityName: '${abbrs.managedIdentityUserAssignedIdentities}api-${resourceToken}' + applicationInsightsName: monitoring.outputs.applicationInsightsName + containerAppsEnvironmentName: containerApps.outputs.environmentName + containerRegistryName: containerApps.outputs.registryName + keyVaultName: keyVault.outputs.name + corsAcaUrl: corsAcaUrl + exists: apiAppExists + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Creates Azure API Management (APIM) service to mediate the requests between the frontend and the backend API +module apim './core/gateway/apim.bicep' = if (useAPIM) { + name: 'apim-deployment' + params: { + name: !empty(apimServiceName) ? apimServiceName : '${abbrs.apiManagementService}${resourceToken}' + location: location + tags: tags + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Configures the API in the Azure API Management (APIM) service +module apimApi './app/apim-api.bicep' = if (useAPIM) { + name: 'apim-api-deployment' + params: { + name: useAPIM ? apim.outputs.apimServiceName : '' + apiName: 'todo-api' + apiDisplayName: 'Simple Todo API' + apiDescription: 'This is a simple Todo API' + apiPath: 'todo' + webFrontendUrl: web.outputs.SERVICE_WEB_URI + apiBackendUrl: api.outputs.SERVICE_API_URI + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output API_CORS_ACA_URL string = corsAcaUrl +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName +output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerApps.outputs.registryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = containerApps.outputs.registryName +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output REACT_APP_API_BASE_URL string = useAPIM ? apimApi.outputs.SERVICE_API_URI : api.outputs.SERVICE_API_URI +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output REACT_APP_WEB_BASE_URL string = web.outputs.SERVICE_WEB_URI +output SERVICE_API_NAME string = api.outputs.SERVICE_API_NAME +output SERVICE_WEB_NAME string = web.outputs.SERVICE_WEB_NAME +output USE_APIM bool = useAPIM +output SERVICE_API_ENDPOINTS array = useAPIM ? [ apimApi.outputs.SERVICE_API_URI, api.outputs.SERVICE_API_URI ]: [] diff --git a/Environments/Todo-Nodejs-Mongo-ACA/main.parameters.json b/Environments/Todo-Nodejs-Mongo-ACA/main.parameters.json new file mode 100644 index 00000000..8ddb71e6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/main.parameters.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + }, + "apiAppExists": { + "value": "${SERVICE_API_RESOURCE_EXISTS=false}" + }, + "webAppExists": { + "value": "${SERVICE_WEB_RESOURCE_EXISTS=false}" + }, + "webApiBaseUrl": { + "value": "${REACT_APP_API_BASE_URL}" + }, + "useAPIM": { + "value": "${USE_APIM=false}" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml new file mode 100644 index 00000000..ced41b78 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml @@ -0,0 +1,54 @@ +name: Todo-Nodejs-Mongo-ACA +version: 1.0.0 +summary: Todo Nodejs Mongo ACA +description: Deploys a todo app with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: "string" + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: "string" + required: true + +- id: "principalId" + name: "Principal Id (e.g. )" + description: "Id of the user or app to assign application roles" + type: "string" + required: false + default: "${AZURE_PRINCIPAL_ID}" + +- id: "apiAppExists" + name: "Api App Exists" + description: "Api App Exists or not" + type: "string" + required: false + default: "${SERVICE_API_RESOURCE_EXISTS=false}" + +- id: "webAppExists" + name: "Web App Exists" + description: "Web App Exists or not" + type: "string" + required: false + default: "${SERVICE_WEB_RESOURCE_EXISTS=false}" + +- id: "webApiBaseUrl" + name: "Web Api Base Url" + description: "Base Url of Web Api" + type: "string" + required: false + default: "${REACT_APP_API_BASE_URL}" + +- id: "useAPIM" + name: "Use APIM" + description: "Use APIM or not" + type: "string" + required: false + default: "${USE_APIM=false}" From 8258b4597624d209fa80c6309351749899c9b931 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 23 Feb 2024 02:40:34 +0000 Subject: [PATCH 050/112] Rebuild ARM templates --- Environments/AKS/azuredeploy.json | 90 +++++++++---------- Environments/APIM/azuredeploy.json | 54 +++++------ Environments/AppVNet/azuredeploy.json | 22 ++--- Environments/ContainerApp/azuredeploy.json | 42 ++++----- Environments/FunctionApp/azuredeploy.json | 4 +- Environments/OpenAISearch/azuredeploy.json | 70 +++++++-------- .../OpenAISummarization/azuredeploy.json | 46 +++++----- Environments/Sandbox/azuredeploy.json | 4 +- Environments/Spring/azuredeploy.json | 18 ++-- Environments/WebApp/azuredeploy.json | 4 +- 10 files changed, 177 insertions(+), 177 deletions(-) diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index d718baeb..3f544eba 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,27 +4,27 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "4959868895440606389" + "version": "0.25.53.49325", + "templateHash": "10018920614151267096" } }, "parameters": { "environmentName": { "type": "string", "defaultValue": "test", + "minLength": 1, + "maxLength": 64, "metadata": { "description": "Name of the the environment which is used to generate a short unique hash used in all resources." - }, - "maxLength": 64, - "minLength": 1 + } }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", + "minLength": 1, "metadata": { "description": "Primary location for all resources" - }, - "minLength": 1 + } }, "clusterName": { "type": "string", @@ -243,8 +243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "5858231189521761249" + "version": "0.25.53.49325", + "templateHash": "16060944584185565434" } }, "parameters": { @@ -323,29 +323,29 @@ "systemPoolType": { "type": "string", "defaultValue": "CostOptimised", - "metadata": { - "description": "The System Pool Preset sizing" - }, "allowedValues": [ "CostOptimised", "Standard", "HighSpec", "Custom" - ] + ], + "metadata": { + "description": "The System Pool Preset sizing" + } }, "agentPoolType": { "type": "string", "defaultValue": "", - "metadata": { - "description": "The User Pool Preset sizing" - }, "allowedValues": [ "", "CostOptimised", "Standard", "HighSpec", "Custom" - ] + ], + "metadata": { + "description": "The User Pool Preset sizing" + } }, "systemPoolConfig": { "type": "object", @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "12367137306066387435" + "version": "0.25.53.49325", + "templateHash": "10614293808878569427" } }, "parameters": { @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "1069227136590891661" + "version": "0.25.53.49325", + "templateHash": "12339762446846171191" } }, "parameters": { @@ -787,8 +787,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14794375381139164570" + "version": "0.25.53.49325", + "templateHash": "203201205747518522" } }, "parameters": { @@ -932,8 +932,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "5378345674008955659" + "version": "0.25.53.49325", + "templateHash": "7101107860953907100" } }, "parameters": { @@ -990,8 +990,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "18301876310392488097" + "version": "0.25.53.49325", + "templateHash": "10350383361040663332" } }, "parameters": { @@ -1105,8 +1105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10658647303664219792" + "version": "0.25.53.49325", + "templateHash": "10326446962893102982" } }, "parameters": { @@ -1186,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "6962395895547155086" + "version": "0.25.53.49325", + "templateHash": "12895087016061856845" } }, "parameters": { @@ -1293,8 +1293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "6947634037528798693" + "version": "0.25.53.49325", + "templateHash": "74163935805296023" } }, "parameters": { @@ -1353,8 +1353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "13620265882151180505" + "version": "0.25.53.49325", + "templateHash": "10930228915954428963" } }, "parameters": { @@ -1532,8 +1532,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "15428045515587502988" + "version": "0.25.53.49325", + "templateHash": "5173484044918305307" } }, "parameters": { @@ -1609,8 +1609,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14795379806004450628" + "version": "0.25.53.49325", + "templateHash": "5828371091540255080" } }, "parameters": { @@ -1659,8 +1659,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10301535207560739586" + "version": "0.25.53.49325", + "templateHash": "16989282822094195299" } }, "parameters": { @@ -1739,8 +1739,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11997294327656983299" + "version": "0.25.53.49325", + "templateHash": "12454312259379619990" } }, "parameters": { @@ -1801,8 +1801,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "13636254352620323826" + "version": "0.25.53.49325", + "templateHash": "9513575600833160668" } }, "parameters": { diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json index 6bd28608..95c5c42e 100644 --- a/Environments/APIM/azuredeploy.json +++ b/Environments/APIM/azuredeploy.json @@ -4,27 +4,27 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "6138348945656610072" + "version": "0.25.53.49325", + "templateHash": "11168061523890493302" } }, "parameters": { "environmentName": { "type": "string", "defaultValue": "test", + "minLength": 1, + "maxLength": 64, "metadata": { "description": "Name which is used to generate a short unique hash for each resource" - }, - "maxLength": 64, - "minLength": 1 + } }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", + "minLength": 1, "metadata": { "description": "Primary location for all resources" - }, - "minLength": 1 + } }, "publisherEmail": { "type": "string", @@ -82,8 +82,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14795379806004450628" + "version": "0.25.53.49325", + "templateHash": "5828371091540255080" } }, "parameters": { @@ -132,8 +132,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10301535207560739586" + "version": "0.25.53.49325", + "templateHash": "16989282822094195299" } }, "parameters": { @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11997294327656983299" + "version": "0.25.53.49325", + "templateHash": "12454312259379619990" } }, "parameters": { @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "13636254352620323826" + "version": "0.25.53.49325", + "templateHash": "9513575600833160668" } }, "parameters": { @@ -1582,8 +1582,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "13809511953096171798" + "version": "0.25.53.49325", + "templateHash": "3566538323657511454" } }, "parameters": { @@ -1710,8 +1710,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "9579788953021113825" + "version": "0.25.53.49325", + "templateHash": "5745686453184402361" } }, "parameters": { @@ -1810,8 +1810,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "4377134390306475369" + "version": "0.25.53.49325", + "templateHash": "1361641401814185517" } }, "parameters": { @@ -2014,8 +2014,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "14158023622954101240" + "version": "0.25.53.49325", + "templateHash": "1045760257801402220" } }, "parameters": { @@ -2262,8 +2262,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "1442451087124190423" + "version": "0.25.53.49325", + "templateHash": "1795228908000942816" } }, "parameters": { @@ -2396,8 +2396,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "16472563199209300832" + "version": "0.25.53.49325", + "templateHash": "8794806107235716835" } }, "parameters": { diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json index 650c72af..98c6d48b 100644 --- a/Environments/AppVNet/azuredeploy.json +++ b/Environments/AppVNet/azuredeploy.json @@ -4,27 +4,27 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17914070543937939267" + "version": "0.25.53.49325", + "templateHash": "1536878747358182609" } }, "parameters": { "environmentName": { "type": "string", "defaultValue": "test", + "minLength": 1, + "maxLength": 64, "metadata": { "description": "Name which is used to generate a short unique hash for each resource" - }, - "maxLength": 64, - "minLength": 1 + } }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", + "minLength": 1, "metadata": { "description": "Primary location for all resources" - }, - "minLength": 1 + } }, "databasePassword": { "type": "securestring", @@ -81,8 +81,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17810419246017990523" + "version": "0.25.53.49325", + "templateHash": "847261945363262167" } }, "parameters": { @@ -369,8 +369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17765872346499755481" + "version": "0.25.53.49325", + "templateHash": "8155104801331874358" } }, "parameters": { diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json index b78bd934..6363eb80 100644 --- a/Environments/ContainerApp/azuredeploy.json +++ b/Environments/ContainerApp/azuredeploy.json @@ -4,27 +4,27 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "4272651749285762279" + "version": "0.25.53.49325", + "templateHash": "14152034777764657013" } }, "parameters": { "environmentName": { "type": "string", "defaultValue": "test", + "minLength": 1, + "maxLength": 64, "metadata": { "description": "Name which is used to generate a short unique hash for each resource" - }, - "maxLength": 64, - "minLength": 1 + } }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", + "minLength": 1, "metadata": { "description": "Primary location for all resources" - }, - "minLength": 1 + } }, "apiImageName": { "type": "string", @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "15428045515587502988" + "version": "0.25.53.49325", + "templateHash": "5173484044918305307" } }, "parameters": { @@ -161,8 +161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "7437297476919271337" + "version": "0.25.53.49325", + "templateHash": "2232453168194463623" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "4782162412977538651" + "version": "0.25.53.49325", + "templateHash": "1835581463787778494" } }, "parameters": { @@ -293,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10225668587554043828" + "version": "0.25.53.49325", + "templateHash": "5616502198215845633" } }, "parameters": { @@ -434,8 +434,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17254398984980713782" + "version": "0.25.53.49325", + "templateHash": "10619128240462649164" } }, "parameters": { @@ -508,8 +508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17927222532671817166" + "version": "0.25.53.49325", + "templateHash": "17857818200713698273" } }, "parameters": { @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "10301535207560739586" + "version": "0.25.53.49325", + "templateHash": "16989282822094195299" } }, "parameters": { diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index e09d3c9e..21dc8212 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "12123717580285782629" + "version": "0.25.53.49325", + "templateHash": "16024393491195140895" } }, "parameters": { diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index f1fc7242..a0da7559 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,19 +4,19 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2428452766374925564" + "version": "0.25.53.49325", + "templateHash": "3847265105826312822" } }, "parameters": { "environmentName": { "type": "string", "defaultValue": "test", + "minLength": 1, + "maxLength": 64, "metadata": { "description": "Name of the the environment which is used to generate a short unique hash used in all resources." - }, - "maxLength": 64, - "minLength": 1 + } }, "location": { "type": "string", @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "8694592921587637446" + "version": "0.25.53.49325", + "templateHash": "5484207409505496262" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3317056714714509123" + "version": "0.25.53.49325", + "templateHash": "13872150863523986003" } }, "parameters": { @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11076527127870690555" + "version": "0.25.53.49325", + "templateHash": "9784023372961823844" } }, "parameters": { @@ -771,8 +771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11076527127870690555" + "version": "0.25.53.49325", + "templateHash": "9784023372961823844" } }, "parameters": { @@ -900,8 +900,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2612000844202476215" + "version": "0.25.53.49325", + "templateHash": "4262027139711048800" } }, "parameters": { @@ -1024,8 +1024,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17856846537035111526" + "version": "0.25.53.49325", + "templateHash": "546351219266755734" } }, "parameters": { @@ -1196,8 +1196,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1260,8 +1260,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1324,8 +1324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1388,8 +1388,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1452,8 +1452,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1516,8 +1516,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1580,8 +1580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1647,8 +1647,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1714,8 +1714,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "2525068256711044961" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index 72e22a3e..42ab9fa9 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -4,27 +4,27 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "17999043670447524747" + "version": "0.25.53.49325", + "templateHash": "4415727950855254716" } }, "parameters": { "environmentName": { "type": "string", "defaultValue": "test", + "minLength": 1, + "maxLength": 64, "metadata": { "description": "Name of the the environment which is used to generate a short unique hash used in all resources." - }, - "maxLength": 64, - "minLength": 1 + } }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", + "minLength": 1, "metadata": { "description": "Primary location for all resources" - }, - "minLength": 1 + } }, "openAiAccountName": { "type": "string", @@ -249,8 +249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "6037704335748105467" + "version": "0.25.53.49325", + "templateHash": "7233160254411631600" } }, "parameters": { @@ -342,8 +342,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "1483229855564020533" + "version": "0.25.53.49325", + "templateHash": "7073695551235566262" } }, "parameters": { @@ -437,8 +437,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "4037976535302248709" + "version": "0.25.53.49325", + "templateHash": "11821310300065265050" } }, "parameters": { @@ -602,8 +602,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3526872952926862635" + "version": "0.25.53.49325", + "templateHash": "10674317264156662708" } }, "parameters": { @@ -659,8 +659,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3526872952926862635" + "version": "0.25.53.49325", + "templateHash": "10674317264156662708" } }, "parameters": { @@ -716,8 +716,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3526872952926862635" + "version": "0.25.53.49325", + "templateHash": "10674317264156662708" } }, "parameters": { @@ -773,8 +773,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3526872952926862635" + "version": "0.25.53.49325", + "templateHash": "10674317264156662708" } }, "parameters": { @@ -830,8 +830,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "3526872952926862635" + "version": "0.25.53.49325", + "templateHash": "10674317264156662708" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index 4add3976..657dc9c5 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11429704302890700233" + "version": "0.25.53.49325", + "templateHash": "11408007220382628824" } }, "resources": [] diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index 46297205..37e08254 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -4,19 +4,19 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "7083236466102790225" + "version": "0.25.53.49325", + "templateHash": "16870146603885581990" } }, "parameters": { "environmentName": { "type": "string", "defaultValue": "test", + "minLength": 1, + "maxLength": 64, "metadata": { "description": "Name which is used to generate a short unique hash for each resource" - }, - "maxLength": 64, - "minLength": 1 + } }, "springCloudInstanceName": { "type": "string", @@ -143,8 +143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "6689023659879453668" + "version": "0.25.53.49325", + "templateHash": "12452574487687496324" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "5594778554212141711" + "version": "0.25.53.49325", + "templateHash": "1175982527140700654" } }, "parameters": { diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 932a2654..23e50e38 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.17.1.54307", - "templateHash": "11616939137952571479" + "version": "0.25.53.49325", + "templateHash": "13878161974033062355" } }, "parameters": { From 14c68909cd07e7e26e326a8069d88f45e67b704a Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 23 Feb 2024 10:48:42 +0800 Subject: [PATCH 051/112] update default value --- Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml index ced41b78..ca8ae446 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml +++ b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml @@ -23,32 +23,32 @@ parameters: description: "Id of the user or app to assign application roles" type: "string" required: false - default: "${AZURE_PRINCIPAL_ID}" + default: "" - id: "apiAppExists" name: "Api App Exists" description: "Api App Exists or not" type: "string" required: false - default: "${SERVICE_API_RESOURCE_EXISTS=false}" + default: false - id: "webAppExists" name: "Web App Exists" description: "Web App Exists or not" type: "string" required: false - default: "${SERVICE_WEB_RESOURCE_EXISTS=false}" + default: false - id: "webApiBaseUrl" name: "Web Api Base Url" description: "Base Url of Web Api" type: "string" required: false - default: "${REACT_APP_API_BASE_URL}" + default: "" - id: "useAPIM" name: "Use APIM" description: "Use APIM or not" type: "string" required: false - default: "${USE_APIM=false}" + default: false From 7ffd88a6c26c8badd9f9d45118f14ce3207178ad Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 23 Feb 2024 16:05:27 +0800 Subject: [PATCH 052/112] update some parameter to boolean type --- Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml index ca8ae446..f699816a 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml +++ b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml @@ -9,46 +9,46 @@ parameters: - id: "environmentName" name: "Environment Name (e.g. testenv)" description: "Name of the Environment" - type: "string" + type: string required: true - id: "location" name: "Region (e.g. eastus)" description: "Location of the resources" - type: "string" + type: string required: true - id: "principalId" name: "Principal Id (e.g. )" description: "Id of the user or app to assign application roles" - type: "string" + type: string required: false default: "" - id: "apiAppExists" name: "Api App Exists" description: "Api App Exists or not" - type: "string" + type: boolean required: false default: false - id: "webAppExists" name: "Web App Exists" description: "Web App Exists or not" - type: "string" + type: boolean required: false default: false - id: "webApiBaseUrl" name: "Web Api Base Url" description: "Base Url of Web Api" - type: "string" + type: string required: false default: "" - id: "useAPIM" name: "Use APIM" description: "Use APIM or not" - type: "string" + type: boolean required: false default: false From cb13587087f6b62fe4922156f69774c4ca996886 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 26 Feb 2024 10:58:14 +0800 Subject: [PATCH 053/112] just leave env name and location --- .../Todo-Nodejs-Mongo-ACA/manifest.yaml | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml index f699816a..6e818b0d 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml +++ b/Environments/Todo-Nodejs-Mongo-ACA/manifest.yaml @@ -17,38 +17,3 @@ parameters: description: "Location of the resources" type: string required: true - -- id: "principalId" - name: "Principal Id (e.g. )" - description: "Id of the user or app to assign application roles" - type: string - required: false - default: "" - -- id: "apiAppExists" - name: "Api App Exists" - description: "Api App Exists or not" - type: boolean - required: false - default: false - -- id: "webAppExists" - name: "Web App Exists" - description: "Web App Exists or not" - type: boolean - required: false - default: false - -- id: "webApiBaseUrl" - name: "Web Api Base Url" - description: "Base Url of Web Api" - type: string - required: false - default: "" - -- id: "useAPIM" - name: "Use APIM" - description: "Use APIM or not" - type: boolean - required: false - default: false From 930991d8f4fb19e3d07ad8b9a05c36b641f6186e Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 29 Feb 2024 11:24:07 +0800 Subject: [PATCH 054/112] add todo mongo aks --- .../Todo-Nodejs-Mongo-AKS/abbreviations.json | 136 ++ .../Todo-Nodejs-Mongo-AKS/app/db.bicep | 40 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + Environments/Todo-Nodejs-Mongo-AKS/main.bicep | 36 + .../main.parameters.json | 15 + .../Todo-Nodejs-Mongo-AKS/manifest.yaml | 25 + .../Todo-Shared-AKS/abbreviations.json | 136 ++ Environments/Todo-Shared-AKS/app/db.bicep | 40 + .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 ++ .../Todo-Shared-AKS/core/gateway/apim.bicep | 79 ++ .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 ++ .../Todo-Shared-AKS/core/host/aks.bicep | 280 ++++ .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 ++ .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 ++ .../core/host/container-app.bicep | 162 +++ .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 ++ .../Todo-Shared-AKS/core/host/functions.bicep | 86 ++ .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++++++++++++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../Todo-Shared-AKS/core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../Todo-Shared-AKS/core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + Environments/Todo-Shared-AKS/main.bicep | 73 + .../Todo-Shared-AKS/main.parameters.json | 15 + Environments/Todo-Shared-AKS/manifest.yaml | 28 + 57 files changed, 4271 insertions(+) create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/main.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml create mode 100644 Environments/Todo-Shared-AKS/abbreviations.json create mode 100644 Environments/Todo-Shared-AKS/app/db.bicep create mode 100644 Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/Todo-Shared-AKS/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/Todo-Shared-AKS/core/gateway/apim.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/aks-agent-pool.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/aks-managed-cluster.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/aks.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/appservice-appsettings.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/appservice.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/appserviceplan.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/container-app-upsert.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/container-app.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/container-apps-environment.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/container-apps.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/container-registry.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/functions.bicep create mode 100644 Environments/Todo-Shared-AKS/core/host/staticwebapp.bicep create mode 100644 Environments/Todo-Shared-AKS/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Todo-Shared-AKS/core/monitor/applicationinsights.bicep create mode 100644 Environments/Todo-Shared-AKS/core/monitor/loganalytics.bicep create mode 100644 Environments/Todo-Shared-AKS/core/monitor/monitoring.bicep create mode 100644 Environments/Todo-Shared-AKS/core/networking/cdn-endpoint.bicep create mode 100644 Environments/Todo-Shared-AKS/core/networking/cdn-profile.bicep create mode 100644 Environments/Todo-Shared-AKS/core/networking/cdn.bicep create mode 100644 Environments/Todo-Shared-AKS/core/search/search-services.bicep create mode 100644 Environments/Todo-Shared-AKS/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Todo-Shared-AKS/core/security/keyvault-access.bicep create mode 100644 Environments/Todo-Shared-AKS/core/security/keyvault-secret.bicep create mode 100644 Environments/Todo-Shared-AKS/core/security/keyvault.bicep create mode 100644 Environments/Todo-Shared-AKS/core/security/registry-access.bicep create mode 100644 Environments/Todo-Shared-AKS/core/security/role.bicep create mode 100644 Environments/Todo-Shared-AKS/core/storage/storage-account.bicep create mode 100644 Environments/Todo-Shared-AKS/core/testing/loadtesting.bicep create mode 100644 Environments/Todo-Shared-AKS/main.bicep create mode 100644 Environments/Todo-Shared-AKS/main.parameters.json create mode 100644 Environments/Todo-Shared-AKS/manifest.yaml diff --git a/Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json b/Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Todo-Nodejs-Mongo-AKS/main.bicep b/Environments/Todo-Nodejs-Mongo-AKS/main.bicep new file mode 100644 index 00000000..0320a02a --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/main.bicep @@ -0,0 +1,36 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyvaultName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyvaultName + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId diff --git a/Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json b/Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml b/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml new file mode 100644 index 00000000..5a56bb76 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml @@ -0,0 +1,25 @@ +name: Todo-Nodejs-Mongo-AKS +version: 1.0.0 +summary: Todo Nodejs Mongo AKS +description: Deploys a todo app with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + +- id: "keyvaultName" + name: "keyvaultName (e.g. kv-abc123)" + description: "keyvault name that store the secret for mongo connection string" + type: string + required: true \ No newline at end of file diff --git a/Environments/Todo-Shared-AKS/abbreviations.json b/Environments/Todo-Shared-AKS/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Todo-Shared-AKS/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Todo-Shared-AKS/app/db.bicep b/Environments/Todo-Shared-AKS/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Todo-Shared-AKS/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep b/Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Todo-Shared-AKS/core/database/postgresql/flexibleserver.bicep b/Environments/Todo-Shared-AKS/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/Todo-Shared-AKS/core/database/sqlserver/sqlserver.bicep b/Environments/Todo-Shared-AKS/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/Todo-Shared-AKS/core/gateway/apim.bicep b/Environments/Todo-Shared-AKS/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/Todo-Shared-AKS/core/host/aks-agent-pool.bicep b/Environments/Todo-Shared-AKS/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/Todo-Shared-AKS/core/host/aks-managed-cluster.bicep b/Environments/Todo-Shared-AKS/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/Todo-Shared-AKS/core/host/aks.bicep b/Environments/Todo-Shared-AKS/core/host/aks.bicep new file mode 100644 index 00000000..536a534b --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/aks.bicep @@ -0,0 +1,280 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/Todo-Shared-AKS/core/host/appservice-appsettings.bicep b/Environments/Todo-Shared-AKS/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/Todo-Shared-AKS/core/host/appservice.bicep b/Environments/Todo-Shared-AKS/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Todo-Shared-AKS/core/host/appserviceplan.bicep b/Environments/Todo-Shared-AKS/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/Todo-Shared-AKS/core/host/container-app-upsert.bicep b/Environments/Todo-Shared-AKS/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/Todo-Shared-AKS/core/host/container-app.bicep b/Environments/Todo-Shared-AKS/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/Todo-Shared-AKS/core/host/container-apps-environment.bicep b/Environments/Todo-Shared-AKS/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Todo-Shared-AKS/core/host/container-apps.bicep b/Environments/Todo-Shared-AKS/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Todo-Shared-AKS/core/host/container-registry.bicep b/Environments/Todo-Shared-AKS/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/Todo-Shared-AKS/core/host/functions.bicep b/Environments/Todo-Shared-AKS/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/Todo-Shared-AKS/core/host/staticwebapp.bicep b/Environments/Todo-Shared-AKS/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/Todo-Shared-AKS/core/monitor/applicationinsights-dashboard.bicep b/Environments/Todo-Shared-AKS/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Todo-Shared-AKS/core/monitor/applicationinsights.bicep b/Environments/Todo-Shared-AKS/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Todo-Shared-AKS/core/monitor/loganalytics.bicep b/Environments/Todo-Shared-AKS/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Todo-Shared-AKS/core/monitor/monitoring.bicep b/Environments/Todo-Shared-AKS/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Todo-Shared-AKS/core/networking/cdn-endpoint.bicep b/Environments/Todo-Shared-AKS/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/Todo-Shared-AKS/core/networking/cdn-profile.bicep b/Environments/Todo-Shared-AKS/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/Todo-Shared-AKS/core/networking/cdn.bicep b/Environments/Todo-Shared-AKS/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/Todo-Shared-AKS/core/search/search-services.bicep b/Environments/Todo-Shared-AKS/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/Todo-Shared-AKS/core/security/aks-managed-cluster-access.bicep b/Environments/Todo-Shared-AKS/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Todo-Shared-AKS/core/security/keyvault-access.bicep b/Environments/Todo-Shared-AKS/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Shared-AKS/core/security/keyvault-secret.bicep b/Environments/Todo-Shared-AKS/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Shared-AKS/core/security/keyvault.bicep b/Environments/Todo-Shared-AKS/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Todo-Shared-AKS/core/security/registry-access.bicep b/Environments/Todo-Shared-AKS/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Todo-Shared-AKS/core/security/role.bicep b/Environments/Todo-Shared-AKS/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Todo-Shared-AKS/core/storage/storage-account.bicep b/Environments/Todo-Shared-AKS/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/Todo-Shared-AKS/core/testing/loadtesting.bicep b/Environments/Todo-Shared-AKS/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/Todo-Shared-AKS/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/Todo-Shared-AKS/main.bicep b/Environments/Todo-Shared-AKS/main.bicep new file mode 100644 index 00000000..bb7d4593 --- /dev/null +++ b/Environments/Todo-Shared-AKS/main.bicep @@ -0,0 +1,73 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +@description('The resource name of the AKS cluster') +param clusterName string = '' + +@description('The resource name of the Container Registry (ACR)') +param containerRegistryName string = '' + +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param keyVaultName string = '' +param logAnalyticsName string = '' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// The AKS cluster to host applications +module aks './core/host/aks.bicep' = { + name: 'aks' + params: { + location: location + name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName +output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Todo-Shared-AKS/main.parameters.json b/Environments/Todo-Shared-AKS/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/Todo-Shared-AKS/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Shared-AKS/manifest.yaml b/Environments/Todo-Shared-AKS/manifest.yaml new file mode 100644 index 00000000..5a5c1e75 --- /dev/null +++ b/Environments/Todo-Shared-AKS/manifest.yaml @@ -0,0 +1,28 @@ +name: Todo-Nodejs-Mongo-AKS +version: 1.0.0 +summary: Todo Nodejs Mongo AKS +description: Deploys a todo app with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + +- id: "principalId" + name: "principalId (e.g. )" + description: "principal id that have the permission (get list) to access key vault" + type: string + required: false + default: '' + + From 233312a9422bbb2bde7e1408e4d0cafdc2d130c2 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Thu, 29 Feb 2024 03:25:43 +0000 Subject: [PATCH 055/112] Rebuild ARM templates --- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 634 ++++ Environments/Todo-Shared-AKS/azuredeploy.json | 2936 +++++++++++++++++ 2 files changed, 3570 insertions(+) create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json create mode 100644 Environments/Todo-Shared-AKS/azuredeploy.json diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json new file mode 100644 index 00000000..98e028d8 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -0,0 +1,634 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14091873261497882625" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyvaultName": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[parameters('keyvaultName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5730728686647632614" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14549161001187918251" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8317058180807592714" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Shared-AKS/azuredeploy.json b/Environments/Todo-Shared-AKS/azuredeploy.json new file mode 100644 index 00000000..8277c2be --- /dev/null +++ b/Environments/Todo-Shared-AKS/azuredeploy.json @@ -0,0 +1,2936 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9109697828467853480" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the AKS cluster" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the Container Registry (ACR)" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4109929557657560885" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name for the Azure container registry (ACR)" + } + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the connected log analytics workspace" + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the keyvault to grant access" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "addOns": { + "type": "object", + "defaultValue": { + "azurePolicy": { + "enabled": true, + "config": { + "version": "v2" + } + }, + "keyVault": { + "enabled": true, + "config": { + "enableSecretRotation": "true", + "rotationPollInterval": "2m" + } + }, + "openServiceMesh": { + "enabled": false, + "config": {} + }, + "omsAgent": { + "enabled": true, + "config": {} + }, + "applicationGateway": { + "enabled": false, + "config": {} + } + }, + "metadata": { + "description": "AKS add-ons configuration" + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "systemPoolType": { + "type": "string", + "defaultValue": "CostOptimised", + "allowedValues": [ + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The System Pool Preset sizing" + } + }, + "agentPoolType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The User Pool Preset sizing" + } + }, + "systemPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of system node pool" + } + }, + "agentPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of user node pool" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + } + }, + "variables": { + "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", + "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", + "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", + "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", + "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", + "nodePoolBase": { + "osType": "Linux", + "maxPods": 30, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "33%" + } + }, + "nodePoolPresets": { + "CostOptimised": { + "vmSize": "Standard_B4ms", + "count": 1, + "minCount": 1, + "maxCount": 3, + "enableAutoScaling": true, + "availabilityZones": [] + }, + "Standard": { + "vmSize": "Standard_DS2_v2", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + }, + "HighSpec": { + "vmSize": "Standard_D4s_v3", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "managed-cluster", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" + }, + "nodeResourceGroupName": { + "value": "[parameters('nodeResourceGroupName')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "dnsPrefix": { + "value": "[parameters('dnsPrefix')]" + }, + "kubernetesVersion": { + "value": "[parameters('kubernetesVersion')]" + }, + "addOns": { + "value": "[variables('addOnsConfig')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]", + "enableAad": { + "value": "[and(parameters('enableAzureRbac'), not(equals(parameters('aadTenantId'), '')))]" + }, + "disableLocalAccounts": { + "value": "[parameters('disableLocalAccounts')]" + }, + "aadTenantId": { + "value": "[parameters('aadTenantId')]" + }, + "enableRbac": { + "value": "[parameters('enableRbac')]" + }, + "enableAzureRbac": { + "value": "[parameters('enableAzureRbac')]" + }, + "webAppRoutingAddon": { + "value": "[parameters('webAppRoutingAddon')]" + }, + "loadBalancerSku": { + "value": "[parameters('loadBalancerSku')]" + }, + "networkPlugin": { + "value": "[parameters('networkPlugin')]" + }, + "networkPolicy": { + "value": "[parameters('networkPolicy')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8184151159222198677" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-10-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPolicy": "[parameters('networkPolicy')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webAppRoutingAddon')]" + } + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.resourceId]" + } + } + } + } + } + }, + { + "condition": "[variables('hasAgentPool')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks-node-pool", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "name": { + "value": "npuserpool" + }, + "config": { + "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6072270897511874144" + }, + "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "name": { + "type": "string", + "metadata": { + "description": "The agent pool name" + } + }, + "config": { + "type": "object", + "metadata": { + "description": "The agent pool configuration" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2023-10-02-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": "[parameters('config')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-container-registry-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "condition": "[or(parameters('enableAzureRbac'), parameters('disableLocalAccounts'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8205013527918052324" + }, + "description": "Assigns RBAC role to the specified AKS cluster and principal." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "aksClusterAdminRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('aksClusterAdminRole'))]", + "properties": { + "roleDefinitionId": "[variables('aksClusterAdminRole')]", + "principalType": "User", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The resource name of the ACR" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The login server for the container registry" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_AKS_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file From e945cd4e8e3fbf49ff1a73522461e77ebed1ebc1 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 29 Feb 2024 13:11:44 +0800 Subject: [PATCH 056/112] change shared aks manifest name --- Environments/Todo-Shared-AKS/manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/Todo-Shared-AKS/manifest.yaml b/Environments/Todo-Shared-AKS/manifest.yaml index 5a5c1e75..b7fa8d99 100644 --- a/Environments/Todo-Shared-AKS/manifest.yaml +++ b/Environments/Todo-Shared-AKS/manifest.yaml @@ -1,4 +1,4 @@ -name: Todo-Nodejs-Mongo-AKS +name: Todo-Shared-AKS version: 1.0.0 summary: Todo Nodejs Mongo AKS description: Deploys a todo app with Nodejs and Mongo From 35a91bd412e80748862e4a3f4f57736fa20473d5 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 29 Feb 2024 16:29:28 +0800 Subject: [PATCH 057/112] add keyvault resource group --- .../core/database/cosmos/cosmos-account.bicep | 17 ++++++++--------- .../cosmos/cosmos-connection-string.bicep | 15 +++++++++++++++ .../cosmos/mongo/cosmos-mongo-account.bicep | 2 ++ .../database/cosmos/mongo/cosmos-mongo-db.bicep | 2 ++ 4 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep index 6f8747f5..3832b3e4 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep @@ -5,6 +5,7 @@ param tags object = {} param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' param keyVaultName string +param keyVaultResourceGroupName string @allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) param kind string @@ -31,18 +32,16 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { } } -resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: connectionStringKey - properties: { - value: cosmos.listConnectionStrings().connectionStrings[0].connectionString +module cosmosConnectionStringModule './cosmos-connection-string.bicep' = { + name: 'cosmosConnectionStringModule' + scope: resourceGroup(keyVaultResourceGroupName) + params: { + keyVaultName: keyVaultName + connectionStringKey: connectionStringKey + connectionString: cosmos.listConnectionStrings().connectionStrings[0].connectionString } } -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - output connectionStringKey string = connectionStringKey output endpoint string = cosmos.properties.documentEndpoint output id string = cosmos.id diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep new file mode 100644 index 00000000..5a96f353 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep @@ -0,0 +1,15 @@ +param keyVaultName string +param connectionStringKey string +param connectionString string + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: connectionString + } +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep index 4aafbf38..665e6a5c 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -4,6 +4,7 @@ param location string = resourceGroup().location param tags object = {} param keyVaultName string +param keyVaultResourceGroupName string param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' module cosmos '../../cosmos/cosmos-account.bicep' = { @@ -13,6 +14,7 @@ module cosmos '../../cosmos/cosmos-account.bicep' = { location: location connectionStringKey: connectionStringKey keyVaultName: keyVaultName + keyVaultResourceGroupName: keyVaultResourceGroupName kind: 'MongoDB' tags: tags } diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep index 2a670578..dd7d0aa3 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -7,6 +7,7 @@ param tags object = {} param collections array = [] param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' param keyVaultName string +param keyVaultResourceGroupName string module cosmos 'cosmos-mongo-account.bicep' = { name: 'cosmos-mongo-account' @@ -14,6 +15,7 @@ module cosmos 'cosmos-mongo-account.bicep' = { name: accountName location: location keyVaultName: keyVaultName + keyVaultResourceGroupName: keyVaultResourceGroupName tags: tags connectionStringKey: connectionStringKey } From a0ff49312776802b3f913fc9e94edea526b1968a Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 29 Feb 2024 17:24:57 +0800 Subject: [PATCH 058/112] fix key vault resource gorup error --- Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep | 2 ++ Environments/Todo-Nodejs-Mongo-AKS/main.bicep | 2 ++ Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml | 8 +++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep index 938949c6..8d828333 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep @@ -18,6 +18,7 @@ param collections array = [ ] param databaseName string = '' param keyVaultName string +param keyVaultResourceGroupName string // Because databaseName is optional in main.bicep, we make sure the database name is set here. var defaultDatabaseName = 'Todo' @@ -31,6 +32,7 @@ module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { location: location collections: collections keyVaultName: keyVaultName + keyVaultResourceGroupName: keyVaultResourceGroupName tags: tags } } diff --git a/Environments/Todo-Nodejs-Mongo-AKS/main.bicep b/Environments/Todo-Nodejs-Mongo-AKS/main.bicep index 0320a02a..c70b89ac 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/main.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/main.bicep @@ -10,6 +10,7 @@ param location string = resourceGroup().location param cosmosAccountName string = '' param cosmosDatabaseName string = '' param keyvaultName string = '' +param keyVaultResourceGroupName string = resourceGroup().name var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) @@ -24,6 +25,7 @@ module cosmos './app/db.bicep' = { location: location tags: tags keyVaultName: keyvaultName + keyVaultResourceGroupName: keyVaultResourceGroupName } } diff --git a/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml b/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml index 5a56bb76..18c37573 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml +++ b/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml @@ -22,4 +22,10 @@ parameters: name: "keyvaultName (e.g. kv-abc123)" description: "keyvault name that store the secret for mongo connection string" type: string - required: true \ No newline at end of file + required: true + +- id: "keyvaultResourceGroupName" + name: "keyvaultResourceGroupName (e.g. rg-abc123)" + description: "keyvault group name contains the key vault above" + type: string + required: true \ No newline at end of file From 1a9f95e06e52b4b957e1483305c2af90aba53efa Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Thu, 29 Feb 2024 09:26:39 +0000 Subject: [PATCH 059/112] Rebuild ARM templates --- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 93 +++++++++++++++++-- 1 file changed, 84 insertions(+), 9 deletions(-) diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json index 98e028d8..dfe17bd2 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "14091873261497882625" + "templateHash": "11686091938214168014" } }, "parameters": { @@ -36,6 +36,10 @@ "keyvaultName": { "type": "string", "defaultValue": "" + }, + "keyVaultResourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]" } }, "variables": { @@ -204,6 +208,9 @@ }, "keyVaultName": { "value": "[parameters('keyvaultName')]" + }, + "keyVaultResourceGroupName": { + "value": "[parameters('keyVaultResourceGroupName')]" } }, "template": { @@ -213,7 +220,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "templateHash": "850519296196313996" } }, "parameters": { @@ -251,6 +258,9 @@ }, "keyVaultName": { "type": "string" + }, + "keyVaultResourceGroupName": { + "type": "string" } }, "variables": { @@ -283,6 +293,9 @@ "keyVaultName": { "value": "[parameters('keyVaultName')]" }, + "keyVaultResourceGroupName": { + "value": "[parameters('keyVaultResourceGroupName')]" + }, "tags": { "value": "[parameters('tags')]" } @@ -294,7 +307,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "templateHash": "13509848683723089892" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -323,6 +336,9 @@ }, "keyVaultName": { "type": "string" + }, + "keyVaultResourceGroupName": { + "type": "string" } }, "resources": [ @@ -388,6 +404,9 @@ "keyVaultName": { "value": "[parameters('keyVaultName')]" }, + "keyVaultResourceGroupName": { + "value": "[parameters('keyVaultResourceGroupName')]" + }, "tags": { "value": "[parameters('tags')]" }, @@ -402,7 +421,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "templateHash": "6834223066721695440" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -421,6 +440,9 @@ "keyVaultName": { "type": "string" }, + "keyVaultResourceGroupName": { + "type": "string" + }, "connectionStringKey": { "type": "string", "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" @@ -449,6 +471,9 @@ "keyVaultName": { "value": "[parameters('keyVaultName')]" }, + "keyVaultResourceGroupName": { + "value": "[parameters('keyVaultResourceGroupName')]" + }, "kind": { "value": "MongoDB" }, @@ -463,7 +488,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "templateHash": "263326484500044406" }, "description": "Creates an Azure Cosmos DB account." }, @@ -486,6 +511,9 @@ "keyVaultName": { "type": "string" }, + "keyVaultResourceGroupName": { + "type": "string" + }, "kind": { "type": "string", "allowedValues": [ @@ -526,11 +554,58 @@ } }, { - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmosConnectionStringModule", + "resourceGroup": "[parameters('keyVaultResourceGroupName')]", "properties": { - "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "connectionString": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "16039028337730776547" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string" + }, + "connectionString": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[parameters('connectionString')]" + } + } + ] + } }, "dependsOn": [ "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" From f4161eac971ba04e4e6cfbbdf19e04b4e2d873de Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 29 Feb 2024 19:31:31 +0800 Subject: [PATCH 060/112] keep mongo --- .../abbreviations.json | 0 .../{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/app/db.bicep | 0 .../{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/azuredeploy.json | 0 .../core/database/cosmos/cosmos-account.bicep | 0 .../core/database/cosmos/cosmos-connection-string.bicep | 0 .../core/database/cosmos/mongo/cosmos-mongo-account.bicep | 0 .../core/database/cosmos/mongo/cosmos-mongo-db.bicep | 0 .../core/database/cosmos/sql/cosmos-sql-account.bicep | 0 .../core/database/cosmos/sql/cosmos-sql-db.bicep | 0 .../core/database/cosmos/sql/cosmos-sql-role-assign.bicep | 0 .../core/database/cosmos/sql/cosmos-sql-role-def.bicep | 0 .../{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/main.bicep | 0 .../main.parameters.json | 0 .../{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/manifest.yaml | 2 +- 14 files changed, 1 insertion(+), 1 deletion(-) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/abbreviations.json (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/app/db.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/azuredeploy.json (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/cosmos-account.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/cosmos-connection-string.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/mongo/cosmos-mongo-account.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/mongo/cosmos-mongo-db.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/sql/cosmos-sql-account.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/sql/cosmos-sql-db.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/sql/cosmos-sql-role-assign.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/core/database/cosmos/sql/cosmos-sql-role-def.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/main.bicep (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/main.parameters.json (100%) rename Environments/{Todo-Nodejs-Mongo-AKS => Todo-Mongo-AKS}/manifest.yaml (96%) diff --git a/Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json b/Environments/Todo-Mongo-AKS/abbreviations.json similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json rename to Environments/Todo-Mongo-AKS/abbreviations.json diff --git a/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep b/Environments/Todo-Mongo-AKS/app/db.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep rename to Environments/Todo-Mongo-AKS/app/db.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json rename to Environments/Todo-Mongo-AKS/azuredeploy.json diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep rename to Environments/Todo-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/main.bicep rename to Environments/Todo-Mongo-AKS/main.bicep diff --git a/Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json b/Environments/Todo-Mongo-AKS/main.parameters.json similarity index 100% rename from Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json rename to Environments/Todo-Mongo-AKS/main.parameters.json diff --git a/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml b/Environments/Todo-Mongo-AKS/manifest.yaml similarity index 96% rename from Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml rename to Environments/Todo-Mongo-AKS/manifest.yaml index 18c37573..c78120e0 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml +++ b/Environments/Todo-Mongo-AKS/manifest.yaml @@ -1,4 +1,4 @@ -name: Todo-Nodejs-Mongo-AKS +name: Todo-Mongo-AKS version: 1.0.0 summary: Todo Nodejs Mongo AKS description: Deploys a todo app with Nodejs and Mongo From 22e2134e44ed02df938a0b6037a0e674bfd9cb67 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 29 Feb 2024 19:35:04 +0800 Subject: [PATCH 061/112] entire aks and mongo --- .../Todo-Nodejs-Mongo-AKS/abbreviations.json | 136 ++ .../Todo-Nodejs-Mongo-AKS/app/db.bicep | 40 + .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 ++ .../core/gateway/apim.bicep | 79 ++ .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 ++ .../Todo-Nodejs-Mongo-AKS/core/host/aks.bicep | 280 ++++ .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 ++ .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 ++ .../core/host/container-app.bicep | 162 +++ .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 ++ .../core/host/functions.bicep | 86 ++ .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++++++++++++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + Environments/Todo-Nodejs-Mongo-AKS/main.bicep | 91 ++ .../main.parameters.json | 15 + .../Todo-Nodejs-Mongo-AKS/manifest.yaml | 28 + 45 files changed, 3773 insertions(+) create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/gateway/apim.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-agent-pool.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-managed-cluster.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/aks.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice-appsettings.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/appserviceplan.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app-upsert.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps-environment.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/container-registry.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/functions.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/host/staticwebapp.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/monitor/loganalytics.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/monitor/monitoring.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-endpoint.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-profile.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/search/search-services.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-access.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-secret.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/security/registry-access.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/security/role.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/core/testing/loadtesting.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/main.bicep create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml diff --git a/Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json b/Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/postgresql/flexibleserver.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/sqlserver/sqlserver.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/gateway/apim.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-agent-pool.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-managed-cluster.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks.bicep new file mode 100644 index 00000000..536a534b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/aks.bicep @@ -0,0 +1,280 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice-appsettings.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/appserviceplan.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app-upsert.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps-environment.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-registry.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/functions.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/host/staticwebapp.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights-dashboard.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/loganalytics.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/monitoring.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-endpoint.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-profile.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/search/search-services.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/security/aks-managed-cluster-access.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-access.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-secret.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/security/registry-access.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/security/role.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/testing/loadtesting.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/Todo-Nodejs-Mongo-AKS/main.bicep b/Environments/Todo-Nodejs-Mongo-AKS/main.bicep new file mode 100644 index 00000000..a382fedb --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/main.bicep @@ -0,0 +1,91 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string + +@description('The resource name of the AKS cluster') +param clusterName string = '' + +@description('The resource name of the Container Registry (ACR)') +param containerRegistryName string = '' + +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param logAnalyticsName string = '' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// The AKS cluster to host applications +module aks './core/host/aks.bicep' = { + name: 'aks' + params: { + location: location + name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName + keyVaultName: keyVault.outputs.name + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName +output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json b/Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml b/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml new file mode 100644 index 00000000..5a5c1e75 --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/manifest.yaml @@ -0,0 +1,28 @@ +name: Todo-Nodejs-Mongo-AKS +version: 1.0.0 +summary: Todo Nodejs Mongo AKS +description: Deploys a todo app with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + +- id: "principalId" + name: "principalId (e.g. )" + description: "principal id that have the permission (get list) to access key vault" + type: string + required: false + default: '' + + From 49650023fa8b8faf8b2279b2f941ad35ea196b25 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Thu, 29 Feb 2024 11:37:05 +0000 Subject: [PATCH 062/112] Rebuild ARM templates --- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 3384 +++++++++++++++++ 1 file changed, 3384 insertions(+) create mode 100644 Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json new file mode 100644 index 00000000..9626594f --- /dev/null +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -0,0 +1,3384 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13440997233145271475" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the AKS cluster" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the Container Registry (ACR)" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4109929557657560885" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name for the Azure container registry (ACR)" + } + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the connected log analytics workspace" + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the keyvault to grant access" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "addOns": { + "type": "object", + "defaultValue": { + "azurePolicy": { + "enabled": true, + "config": { + "version": "v2" + } + }, + "keyVault": { + "enabled": true, + "config": { + "enableSecretRotation": "true", + "rotationPollInterval": "2m" + } + }, + "openServiceMesh": { + "enabled": false, + "config": {} + }, + "omsAgent": { + "enabled": true, + "config": {} + }, + "applicationGateway": { + "enabled": false, + "config": {} + } + }, + "metadata": { + "description": "AKS add-ons configuration" + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "systemPoolType": { + "type": "string", + "defaultValue": "CostOptimised", + "allowedValues": [ + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The System Pool Preset sizing" + } + }, + "agentPoolType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The User Pool Preset sizing" + } + }, + "systemPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of system node pool" + } + }, + "agentPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of user node pool" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + } + }, + "variables": { + "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", + "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", + "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", + "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", + "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", + "nodePoolBase": { + "osType": "Linux", + "maxPods": 30, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "33%" + } + }, + "nodePoolPresets": { + "CostOptimised": { + "vmSize": "Standard_B4ms", + "count": 1, + "minCount": 1, + "maxCount": 3, + "enableAutoScaling": true, + "availabilityZones": [] + }, + "Standard": { + "vmSize": "Standard_DS2_v2", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + }, + "HighSpec": { + "vmSize": "Standard_D4s_v3", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "managed-cluster", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" + }, + "nodeResourceGroupName": { + "value": "[parameters('nodeResourceGroupName')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "dnsPrefix": { + "value": "[parameters('dnsPrefix')]" + }, + "kubernetesVersion": { + "value": "[parameters('kubernetesVersion')]" + }, + "addOns": { + "value": "[variables('addOnsConfig')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]", + "enableAad": { + "value": "[and(parameters('enableAzureRbac'), not(equals(parameters('aadTenantId'), '')))]" + }, + "disableLocalAccounts": { + "value": "[parameters('disableLocalAccounts')]" + }, + "aadTenantId": { + "value": "[parameters('aadTenantId')]" + }, + "enableRbac": { + "value": "[parameters('enableRbac')]" + }, + "enableAzureRbac": { + "value": "[parameters('enableAzureRbac')]" + }, + "webAppRoutingAddon": { + "value": "[parameters('webAppRoutingAddon')]" + }, + "loadBalancerSku": { + "value": "[parameters('loadBalancerSku')]" + }, + "networkPlugin": { + "value": "[parameters('networkPlugin')]" + }, + "networkPolicy": { + "value": "[parameters('networkPolicy')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8184151159222198677" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-10-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPolicy": "[parameters('networkPolicy')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webAppRoutingAddon')]" + } + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.resourceId]" + } + } + } + } + } + }, + { + "condition": "[variables('hasAgentPool')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks-node-pool", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "name": { + "value": "npuserpool" + }, + "config": { + "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6072270897511874144" + }, + "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "name": { + "type": "string", + "metadata": { + "description": "The agent pool name" + } + }, + "config": { + "type": "object", + "metadata": { + "description": "The agent pool configuration" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2023-10-02-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": "[parameters('config')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-container-registry-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "condition": "[or(parameters('enableAzureRbac'), parameters('disableLocalAccounts'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8205013527918052324" + }, + "description": "Assigns RBAC role to the specified AKS cluster and principal." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "aksClusterAdminRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('aksClusterAdminRole'))]", + "properties": { + "roleDefinitionId": "[variables('aksClusterAdminRole')]", + "principalType": "User", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The resource name of the ACR" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The login server for the container registry" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5730728686647632614" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14549161001187918251" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8317058180807592714" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_AKS_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file From e20f270c82f77bfa8ac8b2d4e6545793ec5ea399 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 1 Mar 2024 10:43:40 +0800 Subject: [PATCH 063/112] split ACA to shared container environments and container apps --- .../Todo-Mongo-ACA/abbreviations.json | 136 + Environments/Todo-Mongo-ACA/app/api.bicep | 79 + .../Todo-Mongo-ACA/app/apim-api-policy.xml | 92 + .../Todo-Mongo-ACA/app/apim-api.bicep | 120 + Environments/Todo-Mongo-ACA/app/db.bicep | 40 + Environments/Todo-Mongo-ACA/app/openapi.yaml | 312 ++ Environments/Todo-Mongo-ACA/app/web.bicep | 58 + Environments/Todo-Mongo-ACA/azuredeploy.json | 4679 +++++++++++++++++ .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 + .../Todo-Mongo-ACA/core/gateway/apim.bicep | 79 + .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 + .../Todo-Mongo-ACA/core/host/aks.bicep | 280 + .../core/host/appservice-appsettings.bicep | 17 + .../Todo-Mongo-ACA/core/host/appservice.bicep | 123 + .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 109 + .../core/host/container-app.bicep | 168 + .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 + .../Todo-Mongo-ACA/core/host/functions.bicep | 86 + .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../Todo-Mongo-ACA/core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../Todo-Mongo-ACA/core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + Environments/Todo-Mongo-ACA/main.bicep | 158 + .../Todo-Mongo-ACA/main.parameters.json | 27 + Environments/Todo-Mongo-ACA/manifest.yaml | 43 + .../Todo-Shared-ACA/abbreviations.json | 136 + Environments/Todo-Shared-ACA/app/api.bicep | 75 + .../Todo-Shared-ACA/app/apim-api-policy.xml | 92 + .../Todo-Shared-ACA/app/apim-api.bicep | 120 + Environments/Todo-Shared-ACA/app/db.bicep | 40 + Environments/Todo-Shared-ACA/app/openapi.yaml | 312 ++ Environments/Todo-Shared-ACA/app/web.bicep | 54 + Environments/Todo-Shared-ACA/azuredeploy.json | 4679 +++++++++++++++++ .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 23 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + Environments/Todo-Shared-ACA/main.bicep | 59 + .../Todo-Shared-ACA/main.parameters.json | 12 + Environments/Todo-Shared-ACA/manifest.yaml | 19 + 68 files changed, 16199 insertions(+) create mode 100644 Environments/Todo-Mongo-ACA/abbreviations.json create mode 100644 Environments/Todo-Mongo-ACA/app/api.bicep create mode 100644 Environments/Todo-Mongo-ACA/app/apim-api-policy.xml create mode 100644 Environments/Todo-Mongo-ACA/app/apim-api.bicep create mode 100644 Environments/Todo-Mongo-ACA/app/db.bicep create mode 100644 Environments/Todo-Mongo-ACA/app/openapi.yaml create mode 100644 Environments/Todo-Mongo-ACA/app/web.bicep create mode 100644 Environments/Todo-Mongo-ACA/azuredeploy.json create mode 100644 Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/gateway/apim.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/aks-agent-pool.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/aks-managed-cluster.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/aks.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/appservice-appsettings.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/appservice.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/appserviceplan.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/container-app-upsert.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/container-app.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/container-apps-environment.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/container-apps.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/container-registry.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/functions.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/host/staticwebapp.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/monitor/applicationinsights.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/monitor/loganalytics.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/monitor/monitoring.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/networking/cdn-endpoint.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/networking/cdn-profile.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/networking/cdn.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/search/search-services.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/security/keyvault-access.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/security/keyvault-secret.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/security/keyvault.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/security/registry-access.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/security/role.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep create mode 100644 Environments/Todo-Mongo-ACA/core/testing/loadtesting.bicep create mode 100644 Environments/Todo-Mongo-ACA/main.bicep create mode 100644 Environments/Todo-Mongo-ACA/main.parameters.json create mode 100644 Environments/Todo-Mongo-ACA/manifest.yaml create mode 100644 Environments/Todo-Shared-ACA/abbreviations.json create mode 100644 Environments/Todo-Shared-ACA/app/api.bicep create mode 100644 Environments/Todo-Shared-ACA/app/apim-api-policy.xml create mode 100644 Environments/Todo-Shared-ACA/app/apim-api.bicep create mode 100644 Environments/Todo-Shared-ACA/app/db.bicep create mode 100644 Environments/Todo-Shared-ACA/app/openapi.yaml create mode 100644 Environments/Todo-Shared-ACA/app/web.bicep create mode 100644 Environments/Todo-Shared-ACA/azuredeploy.json create mode 100644 Environments/Todo-Shared-ACA/core/host/container-apps-environment.bicep create mode 100644 Environments/Todo-Shared-ACA/core/host/container-apps.bicep create mode 100644 Environments/Todo-Shared-ACA/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Todo-Shared-ACA/core/monitor/applicationinsights.bicep create mode 100644 Environments/Todo-Shared-ACA/core/monitor/loganalytics.bicep create mode 100644 Environments/Todo-Shared-ACA/core/monitor/monitoring.bicep create mode 100644 Environments/Todo-Shared-ACA/main.bicep create mode 100644 Environments/Todo-Shared-ACA/main.parameters.json create mode 100644 Environments/Todo-Shared-ACA/manifest.yaml diff --git a/Environments/Todo-Mongo-ACA/abbreviations.json b/Environments/Todo-Mongo-ACA/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Todo-Mongo-ACA/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Todo-Mongo-ACA/app/api.bicep b/Environments/Todo-Mongo-ACA/app/api.bicep new file mode 100644 index 00000000..afd7224a --- /dev/null +++ b/Environments/Todo-Mongo-ACA/app/api.bicep @@ -0,0 +1,79 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param applicationInsightsName string +param applicationInsightsResourceGroupName string +param containerAppsEnvironmentName string +param containerAppsEnvironmentResourceGroupName string +param containerRegistryName string +param keyVaultName string +param serviceName string = 'api' +param corsAcaUrl string +param exists bool + +resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +// Give the API access to KeyVault +module apiKeyVaultAccess '../core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: apiIdentity.properties.principalId + } +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + dependsOn: [ apiKeyVaultAccess ] + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: apiIdentity.name + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerAppsEnvironmentResourceGroupName: containerAppsEnvironmentResourceGroupName + containerRegistryName: containerRegistryName + containerCpuCoreCount: '1.0' + containerMemory: '2.0Gi' + env: [ + { + name: 'AZURE_CLIENT_ID' + value: apiIdentity.properties.clientId + } + { + name: 'AZURE_KEY_VAULT_ENDPOINT' + value: keyVault.properties.vaultUri + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'API_ALLOW_ORIGINS' + value: corsAcaUrl + } + ] + targetPort: 3100 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName + scope: resourceGroup(applicationInsightsResourceGroupName) +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Todo-Mongo-ACA/app/apim-api-policy.xml b/Environments/Todo-Mongo-ACA/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/Todo-Mongo-ACA/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
    *
    +
    + +
    *
    +
    +
    + + + + + + + Call to the @(context.Api.Name) + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
    diff --git a/Environments/Todo-Mongo-ACA/app/apim-api.bicep b/Environments/Todo-Mongo-ACA/app/apim-api.bicep new file mode 100644 index 00000000..b6008404 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('./openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/Todo-Mongo-ACA/app/db.bicep b/Environments/Todo-Mongo-ACA/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Mongo-ACA/app/openapi.yaml b/Environments/Todo-Mongo-ACA/app/openapi.yaml new file mode 100644 index 00000000..c42e99ca --- /dev/null +++ b/Environments/Todo-Mongo-ACA/app/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid diff --git a/Environments/Todo-Mongo-ACA/app/web.bicep b/Environments/Todo-Mongo-ACA/app/web.bicep new file mode 100644 index 00000000..9633e884 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/app/web.bicep @@ -0,0 +1,58 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param apiBaseUrl string +param applicationInsightsName string +param applicationInsightsResourceGroupName string +param containerAppsEnvironmentName string +param containerRegistryName string +param serviceName string = 'web' +param exists bool +param containerAppsEnvironmentResourceGroupName string + +resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: identityName + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerAppsEnvironmentResourceGroupName: containerAppsEnvironmentResourceGroupName + containerRegistryName: containerRegistryName + env: [ + { + name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'REACT_APP_API_BASE_URL' + value: apiBaseUrl + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + ] + targetPort: 80 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName + scope: resourceGroup(applicationInsightsResourceGroupName) +} + +output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId +output SERVICE_WEB_NAME string = app.outputs.name +output SERVICE_WEB_URI string = app.outputs.uri +output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json new file mode 100644 index 00000000..65c1d9f7 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -0,0 +1,4679 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5915759848307494038" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "apiContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "webContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "apimServiceName": { + "type": "string", + "defaultValue": "" + }, + "apiAppExists": { + "type": "bool", + "defaultValue": false + }, + "webAppExists": { + "type": "bool", + "defaultValue": false + }, + "useAPIM": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "webApiBaseUrl": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The base URL used by the web service for sending API requests" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "containerRegistryAdminUserEnabled": { + "value": true + }, + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14116108111976192358" + }, + "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "containerRegistryResourceGroupName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryAdminUserEnabled": { + "type": "bool", + "defaultValue": false + }, + "logAnalyticsWorkspaceName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4766903245227392386" + }, + "description": "Creates an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Application Insights resource" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-registry', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "adminUserEnabled": { + "value": "[parameters('containerRegistryAdminUserEnabled')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" + }, + "registryLoginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.loginServer.value]" + }, + "registryName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "web", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('webContainerAppName'))), createObject('value', parameters('webContainerAppName')), createObject('value', format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}web-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "apiBaseUrl": "[if(not(empty(parameters('webApiBaseUrl'))), createObject('value', parameters('webApiBaseUrl')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value))]", + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "exists": { + "value": "[parameters('webAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5244656399300381833" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "apiBaseUrl": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "web" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "env": { + "value": [ + { + "name": "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "REACT_APP_API_BASE_URL", + "value": "[parameters('apiBaseUrl')]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + } + ] + }, + "targetPort": { + "value": 80 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + } + } + ], + "outputs": { + "SERVICE_WEB_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_WEB_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_WEB_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apiContainerAppName'))), createObject('value', parameters('apiContainerAppName')), createObject('value', format('{0}api-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}api-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "corsAcaUrl": { + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "exists": { + "value": "[parameters('apiAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11092891629527222377" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "keyVaultName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "api" + }, + "corsAcaUrl": { + "type": "string" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "1.0" + }, + "containerMemory": { + "value": "2.0Gi" + }, + "env": { + "value": [ + { + "name": "AZURE_CLIENT_ID", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').clientId]" + }, + { + "name": "AZURE_KEY_VAULT_ENDPOINT", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "API_ALLOW_ORIGINS", + "value": "[parameters('corsAcaUrl')]" + } + ] + }, + "targetPort": { + "value": 3100 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]", + "[resourceId('Microsoft.Resources/deployments', 'api-keyvault-access')]" + ] + } + ], + "outputs": { + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_API_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_API_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5730728686647632614" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14549161001187918251" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8317058180807592714" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apimServiceName'))), createObject('value', parameters('apimServiceName')), createObject('value', format('{0}{1}', variables('abbrs').apiManagementService, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "3036576769636454145" + }, + "description": "Creates an Azure API Management instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "publisherEmail": { + "type": "string", + "defaultValue": "noreply@microsoft.com", + "minLength": 1, + "metadata": { + "description": "The email address of the owner of the service" + } + }, + "publisherName": { + "type": "string", + "defaultValue": "n/a", + "minLength": 1, + "metadata": { + "description": "The name of the owner of the service" + } + }, + "sku": { + "type": "string", + "defaultValue": "Consumption", + "allowedValues": [ + "Consumption", + "Developer", + "Standard", + "Premium" + ], + "metadata": { + "description": "The pricing tier of this API Management service" + } + }, + "skuCount": { + "type": "int", + "defaultValue": 0, + "allowedValues": [ + 0, + 1, + 2 + ], + "metadata": { + "description": "The instance size of this API Management service." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "Azure Application Insights Name" + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2021-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('azd-service-name', parameters('name')))]", + "sku": { + "name": "[parameters('sku')]", + "capacity": "[if(equals(parameters('sku'), 'Consumption'), 0, if(equals(parameters('sku'), 'Developer'), 1, parameters('skuCount')))]" + }, + "properties": { + "publisherEmail": "[parameters('publisherEmail')]", + "publisherName": "[parameters('publisherName')]", + "customProperties": "[if(equals(parameters('sku'), 'Consumption'), createObject(), createObject('Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30', 'false'))]" + } + }, + { + "condition": "[not(empty(parameters('applicationInsightsName')))]", + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'app-insights-logger')]", + "properties": { + "credentials": { + "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" + }, + "description": "Logger to Azure Application Insights", + "isBuffered": false, + "loggerType": "applicationInsights", + "resourceId": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" + ] + } + ], + "outputs": { + "apimServiceName": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-api-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(parameters('useAPIM'), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'apim-deployment'), '2022-09-01').outputs.apimServiceName.value), createObject('value', ''))]", + "apiName": { + "value": "todo-api" + }, + "apiDisplayName": { + "value": "Simple Todo API" + }, + "apiDescription": { + "value": "This is a simple Todo API" + }, + "apiPath": { + "value": "todo" + }, + "webFrontendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "apiBackendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6615097664318461925" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "apiName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Resource name to uniquely identify this API within the API Management service instance" + } + }, + "apiDisplayName": { + "type": "string", + "minLength": 1, + "maxLength": 300, + "metadata": { + "description": "The Display Name of the API" + } + }, + "apiDescription": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Description of the API. May include HTML formatting tags." + } + }, + "apiPath": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + } + }, + "webFrontendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the web frontend" + } + }, + "apiBackendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the backend service implementing this API." + } + }, + "apiAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Resource name for backend Web App or Function App" + } + } + }, + "variables": { + "$fxv#0": "\n\n \n \n \n \n \n {origin}\n \n \n PUT\n GET\n POST\n DELETE\n PATCH\n \n \n
    *
    \n
    \n \n
    *
    \n
    \n
    \n \n \n \n \n \n \n Call to the @(context.Api.Name)\n \n \n \n \n \n
    \n \n \n \n \n \n \n \n \n \n \n \n = 200 && context.Response.StatusCode < 300)\">\n \n \n \n \n \n \n \n = 400 && context.Response.StatusCode < 600)\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Failed to process the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n An unexpected error has occurred.\n \n \n
    \n", + "$fxv#1": "openapi: 3.0.0\ninfo:\n description: Simple Todo API\n version: 3.0.0\n title: Simple Todo API\n contact:\n email: azdevteam@microsoft.com\n\ncomponents:\n schemas:\n TodoItem:\n type: object\n required:\n - listId\n - name\n - description\n description: A task that needs to be completed\n properties:\n id:\n type: string\n listId:\n type: string\n name:\n type: string\n description:\n type: string\n state:\n $ref: \"#/components/schemas/TodoState\"\n dueDate:\n type: string\n format: date-time\n completedDate:\n type: string\n format: date-time\n TodoList:\n type: object\n required:\n - name\n properties:\n id:\n type: string\n name:\n type: string\n description:\n type: string\n description: \" A list of related Todo items\"\n TodoState:\n type: string\n enum:\n - todo\n - inprogress\n - done\n parameters:\n listId:\n in: path\n required: true\n name: listId\n description: The Todo list unique identifier\n schema:\n type: string\n itemId:\n in: path\n required: true\n name: itemId\n description: The Todo item unique identifier\n schema:\n type: string\n state:\n in: path\n required: true\n name: state\n description: The Todo item state\n schema:\n $ref: \"#/components/schemas/TodoState\"\n top:\n in: query\n required: false\n name: top\n description: The max number of items to returns in a result\n schema:\n type: number\n default: 20\n skip:\n in: query\n required: false\n name: skip\n description: The number of items to skip within the results\n schema:\n type: number\n default: 0\n\n requestBodies:\n TodoList:\n description: The Todo List\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: The Todo Item\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n\n responses:\n TodoList:\n description: A Todo list result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoListArray:\n description: An array of Todo lists\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: A Todo item result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n TodoItemArray:\n description: An array of Todo items\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoItem\"\n\npaths:\n /lists:\n get:\n operationId: GetLists\n summary: Gets an array of Todo lists\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoListArray\"\n post:\n operationId: CreateList\n summary: Creates a new Todo list\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoList\"\n 400:\n description: Invalid request schema\n /lists/{listId}:\n get:\n operationId: GetListById\n summary: Gets a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n put:\n operationId: UpdateListById\n summary: Updates a Todo list by unique identifier\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n 400:\n description: Todo list is invalid\n delete:\n operationId: DeleteListById\n summary: Deletes a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 204:\n description: Todo list deleted successfully\n 404:\n description: Todo list not found\n /lists/{listId}/items:\n post:\n operationId: CreateItem\n summary: Creates a new Todo item within a list\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list not found\n get:\n operationId: GetItemsByListId\n summary: Gets Todo items within the specified list\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list not found\n /lists/{listId}/items/{itemId}:\n get:\n operationId: GetItemById\n summary: Gets a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemById\n summary: Updates a Todo item by unique identifier\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 400:\n description: Todo item is invalid\n 404:\n description: Todo list or item not found\n delete:\n operationId: DeleteItemById\n summary: Deletes a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 204:\n description: Todo item deleted successfully\n 404:\n description: Todo list or item not found\n /lists/{listId}/items/state/{state}:\n get:\n operationId: GetItemsByListIdAndState\n summary: Gets a list of Todo items of a specific state\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemsStateByListId\n summary: Changes the state of the specified list items\n tags:\n - Items\n requestBody:\n description: unique identifiers of the Todo items to update\n content:\n application/json:\n schema:\n type: array\n items:\n description: The Todo item unique identifier\n type: string\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n responses:\n 204:\n description: Todo items updated\n 400:\n description: Update request is invalid\n", + "apiPolicyContent": "[replace(variables('$fxv#0'), '{origin}', parameters('webFrontendUrl'))]", + "appNameForBicep": "[if(not(empty(parameters('apiAppName'))), parameters('apiAppName'), 'placeholderName')]" + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), parameters('apiName'))]", + "properties": { + "description": "[parameters('apiDescription')]", + "displayName": "[parameters('apiDisplayName')]", + "path": "[parameters('apiPath')]", + "protocols": [ + "https" + ], + "subscriptionRequired": false, + "type": "http", + "format": "openapi", + "serviceUrl": "[parameters('apiBackendUrl')]", + "value": "[variables('$fxv#1')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'policy')]", + "properties": { + "format": "rawxml", + "value": "[variables('apiPolicyContent')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'applicationinsights')]", + "properties": { + "alwaysLog": "allErrors", + "backend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "frontend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "httpCorrelationProtocol": "W3C", + "logClientIp": true, + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('name'), 'app-insights-logger')]", + "metrics": true, + "sampling": { + "percentage": 100, + "samplingType": "fixed" + }, + "verbosity": "verbose" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "condition": "[not(empty(parameters('apiAppName')))]", + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/web', variables('appNameForBicep'))]", + "kind": "string", + "properties": { + "apiManagementConfig": { + "id": "[format('{0}/apis/{1}', resourceId('Microsoft.ApiManagement/service', parameters('name')), parameters('apiName'))]" + } + } + } + ], + "outputs": { + "SERVICE_API_URI": { + "type": "string", + "value": "[format('{0}/{1}', reference(resourceId('Microsoft.ApiManagement/service', parameters('name')), '2021-08-01').gatewayUrl, parameters('apiPath'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'apim-deployment')]", + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "API_CORS_ACA_URL": { + "type": "string", + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "APPLICATIONINSIGHTS_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_API_BASE_URL": { + "type": "string", + "value": "[if(parameters('useAPIM'), reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "REACT_APP_WEB_BASE_URL": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_NAME.value]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_NAME.value]" + }, + "USE_APIM": { + "type": "bool", + "value": "[parameters('useAPIM')]" + }, + "SERVICE_API_ENDPOINTS": { + "type": "array", + "value": "[if(parameters('useAPIM'), createArray(reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value), createArray())]" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep b/Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Todo-Mongo-ACA/core/database/postgresql/flexibleserver.bicep b/Environments/Todo-Mongo-ACA/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/Todo-Mongo-ACA/core/database/sqlserver/sqlserver.bicep b/Environments/Todo-Mongo-ACA/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/Todo-Mongo-ACA/core/gateway/apim.bicep b/Environments/Todo-Mongo-ACA/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/Todo-Mongo-ACA/core/host/aks-agent-pool.bicep b/Environments/Todo-Mongo-ACA/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/Todo-Mongo-ACA/core/host/aks-managed-cluster.bicep b/Environments/Todo-Mongo-ACA/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/Todo-Mongo-ACA/core/host/aks.bicep b/Environments/Todo-Mongo-ACA/core/host/aks.bicep new file mode 100644 index 00000000..536a534b --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/aks.bicep @@ -0,0 +1,280 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/Todo-Mongo-ACA/core/host/appservice-appsettings.bicep b/Environments/Todo-Mongo-ACA/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/Todo-Mongo-ACA/core/host/appservice.bicep b/Environments/Todo-Mongo-ACA/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Todo-Mongo-ACA/core/host/appserviceplan.bicep b/Environments/Todo-Mongo-ACA/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/Todo-Mongo-ACA/core/host/container-app-upsert.bicep b/Environments/Todo-Mongo-ACA/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..ad417f57 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/container-app-upsert.bicep @@ -0,0 +1,109 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('Resource Group Name of the environment for container apps') +param containerAppsEnvironmentResourceGroupName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerAppsEnvironmentResourceGroupName: containerAppsEnvironmentResourceGroupName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/Todo-Mongo-ACA/core/host/container-app.bicep b/Environments/Todo-Mongo-ACA/core/host/container-app.bicep new file mode 100644 index 00000000..75b73cc5 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/container-app.bicep @@ -0,0 +1,168 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('Resource Group Name of the environment for container apps') +param containerAppsEnvironmentResourceGroupName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + + + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName + scope: resourceGroup(containerAppsEnvironmentResourceGroupName) +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/Todo-Mongo-ACA/core/host/container-apps-environment.bicep b/Environments/Todo-Mongo-ACA/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Todo-Mongo-ACA/core/host/container-apps.bicep b/Environments/Todo-Mongo-ACA/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Todo-Mongo-ACA/core/host/container-registry.bicep b/Environments/Todo-Mongo-ACA/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/Todo-Mongo-ACA/core/host/functions.bicep b/Environments/Todo-Mongo-ACA/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/Todo-Mongo-ACA/core/host/staticwebapp.bicep b/Environments/Todo-Mongo-ACA/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/Todo-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep b/Environments/Todo-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Todo-Mongo-ACA/core/monitor/applicationinsights.bicep b/Environments/Todo-Mongo-ACA/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Todo-Mongo-ACA/core/monitor/loganalytics.bicep b/Environments/Todo-Mongo-ACA/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Todo-Mongo-ACA/core/monitor/monitoring.bicep b/Environments/Todo-Mongo-ACA/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Todo-Mongo-ACA/core/networking/cdn-endpoint.bicep b/Environments/Todo-Mongo-ACA/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/Todo-Mongo-ACA/core/networking/cdn-profile.bicep b/Environments/Todo-Mongo-ACA/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/Todo-Mongo-ACA/core/networking/cdn.bicep b/Environments/Todo-Mongo-ACA/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/Todo-Mongo-ACA/core/search/search-services.bicep b/Environments/Todo-Mongo-ACA/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/Todo-Mongo-ACA/core/security/aks-managed-cluster-access.bicep b/Environments/Todo-Mongo-ACA/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Todo-Mongo-ACA/core/security/keyvault-access.bicep b/Environments/Todo-Mongo-ACA/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Mongo-ACA/core/security/keyvault-secret.bicep b/Environments/Todo-Mongo-ACA/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Mongo-ACA/core/security/keyvault.bicep b/Environments/Todo-Mongo-ACA/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Todo-Mongo-ACA/core/security/registry-access.bicep b/Environments/Todo-Mongo-ACA/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Todo-Mongo-ACA/core/security/role.bicep b/Environments/Todo-Mongo-ACA/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep b/Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/Todo-Mongo-ACA/core/testing/loadtesting.bicep b/Environments/Todo-Mongo-ACA/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/Todo-Mongo-ACA/main.bicep b/Environments/Todo-Mongo-ACA/main.bicep new file mode 100644 index 00000000..bf0b94cb --- /dev/null +++ b/Environments/Todo-Mongo-ACA/main.bicep @@ -0,0 +1,158 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param apiContainerAppName string = '' +param containerAppsEnvironmentName string +param containerAppsEnvironmentResourceGroupName string = resourceGroup().name +param applicationInsightsName string = '' +param applicationInsightsResourceGroupName string = containerAppsEnvironmentResourceGroupName +param containerRegistryName string = '' +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param webContainerAppName string = '' +param apimServiceName string = '' +param apiAppExists bool = false +param webAppExists bool = false + +@description('Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API') +param useAPIM bool = false + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('The base URL used by the web service for sending API requests') +param webApiBaseUrl string = '' +param corsAcaUrl string +param containerRegistryAdminUserEnabled bool = false + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// Web frontend +module web './app/web.bicep' = { + name: 'web' + params: { + name: !empty(webContainerAppName) ? webContainerAppName : '${abbrs.appContainerApps}web-${resourceToken}' + location: location + tags: tags + identityName: '${abbrs.managedIdentityUserAssignedIdentities}web-${resourceToken}' + apiBaseUrl: !empty(webApiBaseUrl) ? webApiBaseUrl : api.outputs.SERVICE_API_URI + applicationInsightsName: applicationInsightsName + applicationInsightsResourceGroupName: applicationInsightsResourceGroupName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerAppsEnvironmentResourceGroupName: containerAppsEnvironmentResourceGroupName + containerRegistryName: containerRegistry.outputs.name + exists: webAppExists + } +} + +// Api backend +module api './app/api.bicep' = { + name: 'api' + params: { + name: !empty(apiContainerAppName) ? apiContainerAppName : '${abbrs.appContainerApps}api-${resourceToken}' + location: location + tags: tags + identityName: '${abbrs.managedIdentityUserAssignedIdentities}api-${resourceToken}' + applicationInsightsName: applicationInsightsName + applicationInsightsResourceGroupName: applicationInsightsResourceGroupName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerAppsEnvironmentResourceGroupName: containerAppsEnvironmentResourceGroupName + containerRegistryName: containerRegistry.outputs.name + keyVaultName: keyVault.outputs.name + corsAcaUrl: corsAcaUrl + exists: apiAppExists + } +} + +module containerRegistry './core/host/container-registry.bicep' = { + name: 'app-container-registry' + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + + +// Creates Azure API Management (APIM) service to mediate the requests between the frontend and the backend API +module apim './core/gateway/apim.bicep' = if (useAPIM) { + name: 'apim-deployment' + params: { + name: !empty(apimServiceName) ? apimServiceName : '${abbrs.apiManagementService}${resourceToken}' + location: location + tags: tags + applicationInsightsName: applicationInsightsName + } +} + +// Configures the API in the Azure API Management (APIM) service +module apimApi './app/apim-api.bicep' = if (useAPIM) { + name: 'apim-api-deployment' + params: { + name: useAPIM ? apim.outputs.apimServiceName : '' + apiName: 'todo-api' + apiDisplayName: 'Simple Todo API' + apiDescription: 'This is a simple Todo API' + apiPath: 'todo' + webFrontendUrl: web.outputs.SERVICE_WEB_URI + apiBackendUrl: api.outputs.SERVICE_API_URI + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output API_CORS_ACA_URL string = corsAcaUrl +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.outputs.loginServer +output AZURE_CONTAINER_REGISTRY_NAME string = containerRegistry.outputs.name +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output REACT_APP_API_BASE_URL string = useAPIM ? apimApi.outputs.SERVICE_API_URI : api.outputs.SERVICE_API_URI +output REACT_APP_WEB_BASE_URL string = web.outputs.SERVICE_WEB_URI +output SERVICE_API_NAME string = api.outputs.SERVICE_API_NAME +output SERVICE_WEB_NAME string = web.outputs.SERVICE_WEB_NAME +output USE_APIM bool = useAPIM +output SERVICE_API_ENDPOINTS array = useAPIM ? [ apimApi.outputs.SERVICE_API_URI, api.outputs.SERVICE_API_URI ]: [] +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Todo-Mongo-ACA/main.parameters.json b/Environments/Todo-Mongo-ACA/main.parameters.json new file mode 100644 index 00000000..8ddb71e6 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/main.parameters.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + }, + "apiAppExists": { + "value": "${SERVICE_API_RESOURCE_EXISTS=false}" + }, + "webAppExists": { + "value": "${SERVICE_WEB_RESOURCE_EXISTS=false}" + }, + "webApiBaseUrl": { + "value": "${REACT_APP_API_BASE_URL}" + }, + "useAPIM": { + "value": "${USE_APIM=false}" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Mongo-ACA/manifest.yaml b/Environments/Todo-Mongo-ACA/manifest.yaml new file mode 100644 index 00000000..d2b9ebb1 --- /dev/null +++ b/Environments/Todo-Mongo-ACA/manifest.yaml @@ -0,0 +1,43 @@ +name: Todo-Nodejs-Mongo-ACA +version: 1.0.0 +summary: Todo Nodejs Mongo ACA +description: Deploys a todo app with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + +- id: "corsAcaUrl" + name: "Cors ACA URL" + description: "Cors ACA URL that API Allow Origins" + type: string + required: true + +- id: "containerAppsEnvironmentName" + name: "Container Apps Environment Name (e.g. cae-env1)" + description: "Name of the Container Apps Environment" + type: string + required: true + +- id: "containerAppsEnvironmentResourceGroupName" + name: "Container Apps Environment Resource Group Name (e.g. rg-env1-aca)" + description: "Resource Group Name of the Container Apps Environment" + type: string + required: true + +- id: "applicationInsightsName" + name: "Application Insights Name (e.g. appinsights-abc123)" + description: "Application Insights Name" + type: string + required: true diff --git a/Environments/Todo-Shared-ACA/abbreviations.json b/Environments/Todo-Shared-ACA/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Todo-Shared-ACA/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Todo-Shared-ACA/app/api.bicep b/Environments/Todo-Shared-ACA/app/api.bicep new file mode 100644 index 00000000..1df68145 --- /dev/null +++ b/Environments/Todo-Shared-ACA/app/api.bicep @@ -0,0 +1,75 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param keyVaultName string +param serviceName string = 'api' +param corsAcaUrl string +param exists bool + +resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +// Give the API access to KeyVault +module apiKeyVaultAccess '../core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: apiIdentity.properties.principalId + } +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + dependsOn: [ apiKeyVaultAccess ] + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: apiIdentity.name + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: '1.0' + containerMemory: '2.0Gi' + env: [ + { + name: 'AZURE_CLIENT_ID' + value: apiIdentity.properties.clientId + } + { + name: 'AZURE_KEY_VAULT_ENDPOINT' + value: keyVault.properties.vaultUri + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'API_ALLOW_ORIGINS' + value: corsAcaUrl + } + ] + targetPort: 3100 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Todo-Shared-ACA/app/apim-api-policy.xml b/Environments/Todo-Shared-ACA/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/Todo-Shared-ACA/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
    *
    +
    + +
    *
    +
    +
    + + + + + + + Call to the @(context.Api.Name) + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
    diff --git a/Environments/Todo-Shared-ACA/app/apim-api.bicep b/Environments/Todo-Shared-ACA/app/apim-api.bicep new file mode 100644 index 00000000..b6008404 --- /dev/null +++ b/Environments/Todo-Shared-ACA/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('./openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/Todo-Shared-ACA/app/db.bicep b/Environments/Todo-Shared-ACA/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Todo-Shared-ACA/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Shared-ACA/app/openapi.yaml b/Environments/Todo-Shared-ACA/app/openapi.yaml new file mode 100644 index 00000000..c42e99ca --- /dev/null +++ b/Environments/Todo-Shared-ACA/app/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid diff --git a/Environments/Todo-Shared-ACA/app/web.bicep b/Environments/Todo-Shared-ACA/app/web.bicep new file mode 100644 index 00000000..2c692278 --- /dev/null +++ b/Environments/Todo-Shared-ACA/app/web.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param apiBaseUrl string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param serviceName string = 'web' +param exists bool + +resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: identityName + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + env: [ + { + name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'REACT_APP_API_BASE_URL' + value: apiBaseUrl + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + ] + targetPort: 80 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId +output SERVICE_WEB_NAME string = app.outputs.name +output SERVICE_WEB_URI string = app.outputs.uri +output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Todo-Shared-ACA/azuredeploy.json b/Environments/Todo-Shared-ACA/azuredeploy.json new file mode 100644 index 00000000..65c1d9f7 --- /dev/null +++ b/Environments/Todo-Shared-ACA/azuredeploy.json @@ -0,0 +1,4679 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5915759848307494038" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "apiContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "webContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "apimServiceName": { + "type": "string", + "defaultValue": "" + }, + "apiAppExists": { + "type": "bool", + "defaultValue": false + }, + "webAppExists": { + "type": "bool", + "defaultValue": false + }, + "useAPIM": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "webApiBaseUrl": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The base URL used by the web service for sending API requests" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "containerRegistryAdminUserEnabled": { + "value": true + }, + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14116108111976192358" + }, + "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "containerRegistryResourceGroupName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryAdminUserEnabled": { + "type": "bool", + "defaultValue": false + }, + "logAnalyticsWorkspaceName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4766903245227392386" + }, + "description": "Creates an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Application Insights resource" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-registry', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "adminUserEnabled": { + "value": "[parameters('containerRegistryAdminUserEnabled')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" + }, + "registryLoginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.loginServer.value]" + }, + "registryName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "web", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('webContainerAppName'))), createObject('value', parameters('webContainerAppName')), createObject('value', format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}web-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "apiBaseUrl": "[if(not(empty(parameters('webApiBaseUrl'))), createObject('value', parameters('webApiBaseUrl')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value))]", + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "exists": { + "value": "[parameters('webAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5244656399300381833" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "apiBaseUrl": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "web" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "env": { + "value": [ + { + "name": "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "REACT_APP_API_BASE_URL", + "value": "[parameters('apiBaseUrl')]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + } + ] + }, + "targetPort": { + "value": 80 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + } + } + ], + "outputs": { + "SERVICE_WEB_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_WEB_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_WEB_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apiContainerAppName'))), createObject('value', parameters('apiContainerAppName')), createObject('value', format('{0}api-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}api-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "corsAcaUrl": { + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "exists": { + "value": "[parameters('apiAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11092891629527222377" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "keyVaultName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "api" + }, + "corsAcaUrl": { + "type": "string" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "1.0" + }, + "containerMemory": { + "value": "2.0Gi" + }, + "env": { + "value": [ + { + "name": "AZURE_CLIENT_ID", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').clientId]" + }, + { + "name": "AZURE_KEY_VAULT_ENDPOINT", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "API_ALLOW_ORIGINS", + "value": "[parameters('corsAcaUrl')]" + } + ] + }, + "targetPort": { + "value": 3100 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]", + "[resourceId('Microsoft.Resources/deployments', 'api-keyvault-access')]" + ] + } + ], + "outputs": { + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_API_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_API_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5730728686647632614" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14549161001187918251" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8317058180807592714" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apimServiceName'))), createObject('value', parameters('apimServiceName')), createObject('value', format('{0}{1}', variables('abbrs').apiManagementService, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "3036576769636454145" + }, + "description": "Creates an Azure API Management instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "publisherEmail": { + "type": "string", + "defaultValue": "noreply@microsoft.com", + "minLength": 1, + "metadata": { + "description": "The email address of the owner of the service" + } + }, + "publisherName": { + "type": "string", + "defaultValue": "n/a", + "minLength": 1, + "metadata": { + "description": "The name of the owner of the service" + } + }, + "sku": { + "type": "string", + "defaultValue": "Consumption", + "allowedValues": [ + "Consumption", + "Developer", + "Standard", + "Premium" + ], + "metadata": { + "description": "The pricing tier of this API Management service" + } + }, + "skuCount": { + "type": "int", + "defaultValue": 0, + "allowedValues": [ + 0, + 1, + 2 + ], + "metadata": { + "description": "The instance size of this API Management service." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "Azure Application Insights Name" + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2021-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('azd-service-name', parameters('name')))]", + "sku": { + "name": "[parameters('sku')]", + "capacity": "[if(equals(parameters('sku'), 'Consumption'), 0, if(equals(parameters('sku'), 'Developer'), 1, parameters('skuCount')))]" + }, + "properties": { + "publisherEmail": "[parameters('publisherEmail')]", + "publisherName": "[parameters('publisherName')]", + "customProperties": "[if(equals(parameters('sku'), 'Consumption'), createObject(), createObject('Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30', 'false'))]" + } + }, + { + "condition": "[not(empty(parameters('applicationInsightsName')))]", + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'app-insights-logger')]", + "properties": { + "credentials": { + "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" + }, + "description": "Logger to Azure Application Insights", + "isBuffered": false, + "loggerType": "applicationInsights", + "resourceId": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" + ] + } + ], + "outputs": { + "apimServiceName": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-api-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(parameters('useAPIM'), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'apim-deployment'), '2022-09-01').outputs.apimServiceName.value), createObject('value', ''))]", + "apiName": { + "value": "todo-api" + }, + "apiDisplayName": { + "value": "Simple Todo API" + }, + "apiDescription": { + "value": "This is a simple Todo API" + }, + "apiPath": { + "value": "todo" + }, + "webFrontendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "apiBackendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6615097664318461925" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "apiName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Resource name to uniquely identify this API within the API Management service instance" + } + }, + "apiDisplayName": { + "type": "string", + "minLength": 1, + "maxLength": 300, + "metadata": { + "description": "The Display Name of the API" + } + }, + "apiDescription": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Description of the API. May include HTML formatting tags." + } + }, + "apiPath": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + } + }, + "webFrontendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the web frontend" + } + }, + "apiBackendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the backend service implementing this API." + } + }, + "apiAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Resource name for backend Web App or Function App" + } + } + }, + "variables": { + "$fxv#0": "\n\n \n \n \n \n \n {origin}\n \n \n PUT\n GET\n POST\n DELETE\n PATCH\n \n \n
    *
    \n
    \n \n
    *
    \n
    \n
    \n \n \n \n \n \n \n Call to the @(context.Api.Name)\n \n \n \n \n \n
    \n \n \n \n \n \n \n \n \n \n \n \n = 200 && context.Response.StatusCode < 300)\">\n \n \n \n \n \n \n \n = 400 && context.Response.StatusCode < 600)\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Failed to process the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n An unexpected error has occurred.\n \n \n
    \n", + "$fxv#1": "openapi: 3.0.0\ninfo:\n description: Simple Todo API\n version: 3.0.0\n title: Simple Todo API\n contact:\n email: azdevteam@microsoft.com\n\ncomponents:\n schemas:\n TodoItem:\n type: object\n required:\n - listId\n - name\n - description\n description: A task that needs to be completed\n properties:\n id:\n type: string\n listId:\n type: string\n name:\n type: string\n description:\n type: string\n state:\n $ref: \"#/components/schemas/TodoState\"\n dueDate:\n type: string\n format: date-time\n completedDate:\n type: string\n format: date-time\n TodoList:\n type: object\n required:\n - name\n properties:\n id:\n type: string\n name:\n type: string\n description:\n type: string\n description: \" A list of related Todo items\"\n TodoState:\n type: string\n enum:\n - todo\n - inprogress\n - done\n parameters:\n listId:\n in: path\n required: true\n name: listId\n description: The Todo list unique identifier\n schema:\n type: string\n itemId:\n in: path\n required: true\n name: itemId\n description: The Todo item unique identifier\n schema:\n type: string\n state:\n in: path\n required: true\n name: state\n description: The Todo item state\n schema:\n $ref: \"#/components/schemas/TodoState\"\n top:\n in: query\n required: false\n name: top\n description: The max number of items to returns in a result\n schema:\n type: number\n default: 20\n skip:\n in: query\n required: false\n name: skip\n description: The number of items to skip within the results\n schema:\n type: number\n default: 0\n\n requestBodies:\n TodoList:\n description: The Todo List\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: The Todo Item\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n\n responses:\n TodoList:\n description: A Todo list result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoListArray:\n description: An array of Todo lists\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: A Todo item result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n TodoItemArray:\n description: An array of Todo items\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoItem\"\n\npaths:\n /lists:\n get:\n operationId: GetLists\n summary: Gets an array of Todo lists\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoListArray\"\n post:\n operationId: CreateList\n summary: Creates a new Todo list\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoList\"\n 400:\n description: Invalid request schema\n /lists/{listId}:\n get:\n operationId: GetListById\n summary: Gets a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n put:\n operationId: UpdateListById\n summary: Updates a Todo list by unique identifier\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n 400:\n description: Todo list is invalid\n delete:\n operationId: DeleteListById\n summary: Deletes a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 204:\n description: Todo list deleted successfully\n 404:\n description: Todo list not found\n /lists/{listId}/items:\n post:\n operationId: CreateItem\n summary: Creates a new Todo item within a list\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list not found\n get:\n operationId: GetItemsByListId\n summary: Gets Todo items within the specified list\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list not found\n /lists/{listId}/items/{itemId}:\n get:\n operationId: GetItemById\n summary: Gets a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemById\n summary: Updates a Todo item by unique identifier\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 400:\n description: Todo item is invalid\n 404:\n description: Todo list or item not found\n delete:\n operationId: DeleteItemById\n summary: Deletes a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 204:\n description: Todo item deleted successfully\n 404:\n description: Todo list or item not found\n /lists/{listId}/items/state/{state}:\n get:\n operationId: GetItemsByListIdAndState\n summary: Gets a list of Todo items of a specific state\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemsStateByListId\n summary: Changes the state of the specified list items\n tags:\n - Items\n requestBody:\n description: unique identifiers of the Todo items to update\n content:\n application/json:\n schema:\n type: array\n items:\n description: The Todo item unique identifier\n type: string\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n responses:\n 204:\n description: Todo items updated\n 400:\n description: Update request is invalid\n", + "apiPolicyContent": "[replace(variables('$fxv#0'), '{origin}', parameters('webFrontendUrl'))]", + "appNameForBicep": "[if(not(empty(parameters('apiAppName'))), parameters('apiAppName'), 'placeholderName')]" + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), parameters('apiName'))]", + "properties": { + "description": "[parameters('apiDescription')]", + "displayName": "[parameters('apiDisplayName')]", + "path": "[parameters('apiPath')]", + "protocols": [ + "https" + ], + "subscriptionRequired": false, + "type": "http", + "format": "openapi", + "serviceUrl": "[parameters('apiBackendUrl')]", + "value": "[variables('$fxv#1')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'policy')]", + "properties": { + "format": "rawxml", + "value": "[variables('apiPolicyContent')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'applicationinsights')]", + "properties": { + "alwaysLog": "allErrors", + "backend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "frontend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "httpCorrelationProtocol": "W3C", + "logClientIp": true, + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('name'), 'app-insights-logger')]", + "metrics": true, + "sampling": { + "percentage": 100, + "samplingType": "fixed" + }, + "verbosity": "verbose" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "condition": "[not(empty(parameters('apiAppName')))]", + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/web', variables('appNameForBicep'))]", + "kind": "string", + "properties": { + "apiManagementConfig": { + "id": "[format('{0}/apis/{1}', resourceId('Microsoft.ApiManagement/service', parameters('name')), parameters('apiName'))]" + } + } + } + ], + "outputs": { + "SERVICE_API_URI": { + "type": "string", + "value": "[format('{0}/{1}', reference(resourceId('Microsoft.ApiManagement/service', parameters('name')), '2021-08-01').gatewayUrl, parameters('apiPath'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'apim-deployment')]", + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "API_CORS_ACA_URL": { + "type": "string", + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "APPLICATIONINSIGHTS_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_API_BASE_URL": { + "type": "string", + "value": "[if(parameters('useAPIM'), reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "REACT_APP_WEB_BASE_URL": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_NAME.value]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_NAME.value]" + }, + "USE_APIM": { + "type": "bool", + "value": "[parameters('useAPIM')]" + }, + "SERVICE_API_ENDPOINTS": { + "type": "array", + "value": "[if(parameters('useAPIM'), createArray(reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value), createArray())]" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Shared-ACA/core/host/container-apps-environment.bicep b/Environments/Todo-Shared-ACA/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Todo-Shared-ACA/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Todo-Shared-ACA/core/host/container-apps.bicep b/Environments/Todo-Shared-ACA/core/host/container-apps.bicep new file mode 100644 index 00000000..b180d264 --- /dev/null +++ b/Environments/Todo-Shared-ACA/core/host/container-apps.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id diff --git a/Environments/Todo-Shared-ACA/core/monitor/applicationinsights-dashboard.bicep b/Environments/Todo-Shared-ACA/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Todo-Shared-ACA/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Todo-Shared-ACA/core/monitor/applicationinsights.bicep b/Environments/Todo-Shared-ACA/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Todo-Shared-ACA/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Todo-Shared-ACA/core/monitor/loganalytics.bicep b/Environments/Todo-Shared-ACA/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Todo-Shared-ACA/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Todo-Shared-ACA/core/monitor/monitoring.bicep b/Environments/Todo-Shared-ACA/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Todo-Shared-ACA/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Todo-Shared-ACA/main.bicep b/Environments/Todo-Shared-ACA/main.bicep new file mode 100644 index 00000000..8864f5ca --- /dev/null +++ b/Environments/Todo-Shared-ACA/main.bicep @@ -0,0 +1,59 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param containerAppsEnvironmentName string = '' +param logAnalyticsName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } +var apiContainerAppNameOrDefault = '${abbrs.appContainerApps}web-${resourceToken}' +var corsAcaUrl = 'https://${apiContainerAppNameOrDefault}.${containerApps.outputs.defaultDomain}' + +// Container apps host (including container registry) +module containerApps './core/host/container-apps.bicep' = { + name: 'container-apps' + params: { + name: 'app' + location: location + tags: tags + containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output API_CORS_ACA_URL string = corsAcaUrl +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName +output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName + +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId + +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Todo-Shared-ACA/main.parameters.json b/Environments/Todo-Shared-ACA/main.parameters.json new file mode 100644 index 00000000..8f7787be --- /dev/null +++ b/Environments/Todo-Shared-ACA/main.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Shared-ACA/manifest.yaml b/Environments/Todo-Shared-ACA/manifest.yaml new file mode 100644 index 00000000..0dff2279 --- /dev/null +++ b/Environments/Todo-Shared-ACA/manifest.yaml @@ -0,0 +1,19 @@ +name: Todo-Shared-ACA +version: 1.0.0 +summary: Todo Shared ACA +description: Deploys base env (Container environment and monitoring) for todo app +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true From cdbb5cfe169fc96ba3294b044ba120a9b140abcd Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 1 Mar 2024 02:45:08 +0000 Subject: [PATCH 064/112] Rebuild ARM templates --- Environments/Todo-Mongo-ACA/azuredeploy.json | 4387 +++++------------ Environments/Todo-Shared-ACA/azuredeploy.json | 2880 +---------- 2 files changed, 1419 insertions(+), 5848 deletions(-) diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index 65c1d9f7..d4d8456a 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "5915759848307494038" + "templateHash": "7624760505230248688" } }, "parameters": { @@ -29,17 +29,20 @@ "type": "string", "defaultValue": "" }, - "applicationInsightsDashboardName": { + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerAppsEnvironmentResourceGroupName": { "type": "string", - "defaultValue": "" + "defaultValue": "[resourceGroup().name]" }, "applicationInsightsName": { "type": "string", "defaultValue": "" }, - "containerAppsEnvironmentName": { + "applicationInsightsResourceGroupName": { "type": "string", - "defaultValue": "" + "defaultValue": "[parameters('containerAppsEnvironmentResourceGroupName')]" }, "containerRegistryName": { "type": "string", @@ -57,10 +60,6 @@ "type": "string", "defaultValue": "" }, - "logAnalyticsName": { - "type": "string", - "defaultValue": "" - }, "webContainerAppName": { "type": "string", "defaultValue": "" @@ -97,6 +96,13 @@ "metadata": { "description": "The base URL used by the web service for sending API requests" } + }, + "corsAcaUrl": { + "type": "string" + }, + "containerRegistryAdminUserEnabled": { + "type": "bool", + "defaultValue": false } }, "variables": { @@ -240,39 +246,47 @@ "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", "tags": { "azd-env-name": "[parameters('environmentName')]" - }, - "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + } }, "resources": [ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "container-apps", + "name": "web", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "app" - }, + "name": "[if(not(empty(parameters('webContainerAppName'))), createObject('value', parameters('webContainerAppName')), createObject('value', format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", "location": { "value": "[parameters('location')]" }, "tags": { "value": "[variables('tags')]" }, - "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", - "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", - "containerRegistryAdminUserEnabled": { - "value": true - }, - "logAnalyticsWorkspaceName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + "identityName": { + "value": "[format('{0}web-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" }, + "apiBaseUrl": "[if(not(empty(parameters('webApiBaseUrl'))), createObject('value', parameters('webApiBaseUrl')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value))]", "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + "value": "[parameters('applicationInsightsName')]" + }, + "applicationInsightsResourceGroupName": { + "value": "[parameters('applicationInsightsResourceGroupName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerAppsEnvironmentResourceGroupName": { + "value": "[parameters('containerAppsEnvironmentResourceGroupName')]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app-container-registry'), '2022-09-01').outputs.name.value]" + }, + "exists": { + "value": "[parameters('webAppExists')]" } }, "template": { @@ -282,9 +296,8 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "14116108111976192358" - }, - "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + "templateHash": "12761856845851580026" + } }, "parameters": { "name": { @@ -298,33 +311,46 @@ "type": "object", "defaultValue": {} }, + "identityName": { + "type": "string" + }, + "apiBaseUrl": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsResourceGroupName": { + "type": "string" + }, "containerAppsEnvironmentName": { "type": "string" }, "containerRegistryName": { "type": "string" }, - "containerRegistryResourceGroupName": { + "serviceName": { "type": "string", - "defaultValue": "" + "defaultValue": "web" }, - "containerRegistryAdminUserEnabled": { - "type": "bool", - "defaultValue": false + "exists": { + "type": "bool" }, - "logAnalyticsWorkspaceName": { + "containerAppsEnvironmentResourceGroupName": { "type": "string" - }, - "applicationInsightsName": { - "type": "string", - "defaultValue": "" } }, "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "name": "[format('{0}-container-app', parameters('serviceName'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -332,19 +358,50 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('containerAppsEnvironmentName')]" + "value": "[parameters('name')]" }, "location": { "value": "[parameters('location')]" }, "tags": { - "value": "[parameters('tags')]" + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerAppsEnvironmentResourceGroupName": { + "value": "[parameters('containerAppsEnvironmentResourceGroupName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" }, - "logAnalyticsWorkspaceName": { - "value": "[parameters('logAnalyticsWorkspaceName')]" + "env": { + "value": [ + { + "name": "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('applicationInsightsResourceGroupName')), 'Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "REACT_APP_API_BASE_URL", + "value": "[parameters('apiBaseUrl')]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('applicationInsightsResourceGroupName')), 'Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + } + ] }, - "applicationInsightsName": { - "value": "[parameters('applicationInsightsName')]" + "targetPort": { + "value": 80 } }, "template": { @@ -354,9 +411,9 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "4766903245227392386" + "templateHash": "12494944888544926830" }, - "description": "Creates an Azure Container Apps environment." + "description": "Creates or updates an existing Azure Container App." }, "parameters": { "name": { @@ -370,1402 +427,139 @@ "type": "object", "defaultValue": {} }, - "applicationInsightsName": { + "containerAppsEnvironmentName": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Name of the Application Insights resource" + "description": "The environment name for the container apps" } }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, + "containerAppsEnvironmentResourceGroupName": { + "type": "string", "metadata": { - "description": "Specifies if Dapr is enabled" + "description": "Resource Group Name of the environment for container apps" } }, - "logAnalyticsWorkspaceName": { + "containerCpuCoreCount": { "type": "string", + "defaultValue": "0.5", "metadata": { - "description": "Name of the Log Analytics workspace" + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" } - } - }, - "resources": [ - { - "type": "Microsoft.App/managedEnvironments", - "apiVersion": "2023-04-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "appLogsConfiguration": { - "destination": "log-analytics", - "logAnalyticsConfiguration": { - "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", - "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" - } - }, - "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" }, - "id": { + "containerMemory": { "type": "string", - "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-container-registry', parameters('name'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('containerRegistryName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "adminUserEnabled": { - "value": "[parameters('containerRegistryAdminUserEnabled')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } }, - "description": "Creates an Azure Container Registry." - }, - "parameters": { - "name": { - "type": "string" + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } }, - "location": { + "containerRegistryName": { "type": "string", - "defaultValue": "[resourceGroup().location]" + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } }, - "tags": { - "type": "object", - "defaultValue": {} + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } }, - "adminUserEnabled": { + "daprEnabled": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Indicates whether admin user is enabled" + "description": "Enable or disable Dapr for the container app" } }, - "anonymousPullEnabled": { - "type": "bool", - "defaultValue": false, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", "metadata": { - "description": "Indicates whether anonymous pull is enabled" + "description": "The Dapr app ID" } }, - "dataEndpointEnabled": { + "exists": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Indicates whether data endpoint is enabled" + "description": "Specifies if the resource already exists" } }, - "encryption": { - "type": "object", - "defaultValue": { - "status": "disabled" - }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Encryption settings" + "description": "Specifies if Ingress is enabled for the container app" } }, - "networkRuleBypassOptions": { + "identityType": { "type": "string", - "defaultValue": "AzureServices", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], "metadata": { - "description": "Options for bypassing network rules" + "description": "The type of identity for the resource" } }, - "publicNetworkAccess": { + "identityName": { "type": "string", - "defaultValue": "Enabled", + "defaultValue": "", "metadata": { - "description": "Public network access setting" + "description": "The name of the user-assigned identity" } }, - "sku": { - "type": "object", - "defaultValue": { - "name": "Basic" - }, + "imageName": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "SKU settings" + "description": "The name of the container image" } }, - "zoneRedundancy": { - "type": "string", - "defaultValue": "Disabled", + "secrets": { + "type": "array", + "defaultValue": [], "metadata": { - "description": "Zone redundancy setting" + "description": "The secrets required for the container" } }, - "workspaceId": { - "type": "string", - "defaultValue": "", + "env": { + "type": "array", + "defaultValue": [], "metadata": { - "description": "The log analytics workspace ID used for logging and monitoring" - } - } - }, - "resources": [ - { - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2022-02-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", - "properties": { - "adminUserEnabled": "[parameters('adminUserEnabled')]", - "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", - "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", - "encryption": "[parameters('encryption')]", - "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]", - "zoneRedundancy": "[parameters('zoneRedundancy')]" - } - }, - { - "condition": "[not(empty(parameters('workspaceId')))]", - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", - "name": "registry-diagnostics", - "properties": { - "workspaceId": "[parameters('workspaceId')]", - "logs": [ - { - "category": "ContainerRegistryRepositoryEvents", - "enabled": true - }, - { - "category": "ContainerRegistryLoginEvents", - "enabled": true - } - ], - "metrics": [ - { - "category": "AllMetrics", - "enabled": true, - "timeGrain": "PT1M" - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" - ] - } - ], - "outputs": { - "loginServer": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" - }, - "environmentName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" - }, - "environmentId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" - }, - "registryLoginServer": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.loginServer.value]" - }, - "registryName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.name.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "web", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('webContainerAppName'))), createObject('value', parameters('webContainerAppName')), createObject('value', format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "identityName": { - "value": "[format('{0}web-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" - }, - "apiBaseUrl": "[if(not(empty(parameters('webApiBaseUrl'))), createObject('value', parameters('webApiBaseUrl')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value))]", - "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" - }, - "containerAppsEnvironmentName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" - }, - "containerRegistryName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" - }, - "exists": { - "value": "[parameters('webAppExists')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5244656399300381833" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "identityName": { - "type": "string" - }, - "apiBaseUrl": { - "type": "string" - }, - "applicationInsightsName": { - "type": "string" - }, - "containerAppsEnvironmentName": { - "type": "string" - }, - "containerRegistryName": { - "type": "string" - }, - "serviceName": { - "type": "string", - "defaultValue": "web" - }, - "exists": { - "type": "bool" - } - }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[parameters('identityName')]", - "location": "[parameters('location')]" - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-container-app', parameters('serviceName'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" - }, - "identityType": { - "value": "UserAssigned" - }, - "identityName": { - "value": "[parameters('identityName')]" - }, - "exists": { - "value": "[parameters('exists')]" - }, - "containerAppsEnvironmentName": { - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "env": { - "value": [ - { - "name": "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" - }, - { - "name": "REACT_APP_API_BASE_URL", - "value": "[parameters('apiBaseUrl')]" - }, - { - "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" - } - ] - }, - "targetPort": { - "value": 80 - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" - }, - "description": "Creates or updates an existing Azure Container App." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "containerAppsEnvironmentName": { - "type": "string", - "metadata": { - "description": "The environment name for the container apps" - } - }, - "containerCpuCoreCount": { - "type": "string", - "defaultValue": "0.5", - "metadata": { - "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" - } - }, - "containerMaxReplicas": { - "type": "int", - "defaultValue": 10, - "minValue": 1, - "metadata": { - "description": "The maximum number of replicas to run. Must be at least 1." - } - }, - "containerMemory": { - "type": "string", - "defaultValue": "1.0Gi", - "metadata": { - "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" - } - }, - "containerMinReplicas": { - "type": "int", - "defaultValue": 1, - "minValue": 1, - "metadata": { - "description": "The minimum number of replicas to run. Must be at least 1." - } - }, - "containerName": { - "type": "string", - "defaultValue": "main", - "metadata": { - "description": "The name of the container" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container registry" - } - }, - "daprAppProtocol": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "http", - "grpc" - ], - "metadata": { - "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" - } - }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable or disable Dapr for the container app" - } - }, - "daprAppId": { - "type": "string", - "defaultValue": "[parameters('containerName')]", - "metadata": { - "description": "The Dapr app ID" - } - }, - "exists": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Specifies if the resource already exists" - } - }, - "ingressEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if Ingress is enabled for the container app" - } - }, - "identityType": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "SystemAssigned", - "UserAssigned" - ], - "metadata": { - "description": "The type of identity for the resource" - } - }, - "identityName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the user-assigned identity" - } - }, - "imageName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container image" - } - }, - "secrets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The secrets required for the container" - } - }, - "env": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The environment variables for the container" - } - }, - "external": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if the resource ingress is exposed externally" - } - }, - "serviceBinds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The service binds associated with the container" - } - }, - "targetPort": { - "type": "int", - "defaultValue": 80, - "metadata": { - "description": "The target port for the container" - } - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-update', deployment().name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "identityType": { - "value": "[parameters('identityType')]" - }, - "identityName": { - "value": "[parameters('identityName')]" - }, - "ingressEnabled": { - "value": "[parameters('ingressEnabled')]" - }, - "containerName": { - "value": "[parameters('containerName')]" - }, - "containerAppsEnvironmentName": { - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "containerCpuCoreCount": { - "value": "[parameters('containerCpuCoreCount')]" - }, - "containerMemory": { - "value": "[parameters('containerMemory')]" - }, - "containerMinReplicas": { - "value": "[parameters('containerMinReplicas')]" - }, - "containerMaxReplicas": { - "value": "[parameters('containerMaxReplicas')]" - }, - "daprEnabled": { - "value": "[parameters('daprEnabled')]" - }, - "daprAppId": { - "value": "[parameters('daprAppId')]" - }, - "daprAppProtocol": { - "value": "[parameters('daprAppProtocol')]" - }, - "secrets": { - "value": "[parameters('secrets')]" - }, - "external": { - "value": "[parameters('external')]" - }, - "env": { - "value": "[parameters('env')]" - }, - "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", - "targetPort": { - "value": "[parameters('targetPort')]" - }, - "serviceBinds": { - "value": "[parameters('serviceBinds')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1912096201798605494" - }, - "description": "Creates a container app in an Azure Container App environment." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "allowedOrigins": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Allowed origins" - } - }, - "containerAppsEnvironmentName": { - "type": "string", - "metadata": { - "description": "Name of the environment for container apps" - } - }, - "containerCpuCoreCount": { - "type": "string", - "defaultValue": "0.5", - "metadata": { - "description": "CPU cores allocated to a single container instance, e.g., 0.5" - } - }, - "containerMaxReplicas": { - "type": "int", - "defaultValue": 10, - "minValue": 1, - "metadata": { - "description": "The maximum number of replicas to run. Must be at least 1." - } - }, - "containerMemory": { - "type": "string", - "defaultValue": "1.0Gi", - "metadata": { - "description": "Memory allocated to a single container instance, e.g., 1Gi" - } - }, - "containerMinReplicas": { - "type": "int", - "defaultValue": 1, - "metadata": { - "description": "The minimum number of replicas to run. Must be at least 1." - } - }, - "containerName": { - "type": "string", - "defaultValue": "main", - "metadata": { - "description": "The name of the container" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container registry" - } - }, - "daprAppProtocol": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "http", - "grpc" - ], - "metadata": { - "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" - } - }, - "daprAppId": { - "type": "string", - "defaultValue": "[parameters('containerName')]", - "metadata": { - "description": "The Dapr app ID" - } - }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable Dapr" - } - }, - "env": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The environment variables for the container" - } - }, - "external": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if the resource ingress is exposed externally" - } - }, - "identityName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the user-assigned identity" - } - }, - "identityType": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "SystemAssigned", - "UserAssigned" - ], - "metadata": { - "description": "The type of identity for the resource" - } - }, - "imageName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container image" - } - }, - "ingressEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if Ingress is enabled for the container app" - } - }, - "revisionMode": { - "type": "string", - "defaultValue": "Single" - }, - "secrets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The secrets required for the container" - } - }, - "serviceBinds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The service binds associated with the container" - } - }, - "serviceType": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container apps add-on to use. e.g. redis" - } - }, - "targetPort": { - "type": "int", - "defaultValue": 80, - "metadata": { - "description": "The target port for the container" - } - } - }, - "variables": { - "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", - "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" - }, - "resources": [ - { - "type": "Microsoft.App/containerApps", - "apiVersion": "2023-04-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": { - "type": "[variables('normalizedIdentityType')]", - "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" - }, - "properties": { - "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", - "configuration": { - "activeRevisionsMode": "[parameters('revisionMode')]", - "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", - "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", - "secrets": "[parameters('secrets')]", - "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", - "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" - }, - "template": { - "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", - "containers": [ - { - "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", - "name": "[parameters('containerName')]", - "env": "[parameters('env')]", - "resources": { - "cpu": "[json(parameters('containerCpuCoreCount'))]", - "memory": "[parameters('containerMemory')]" - } - } - ], - "scale": { - "minReplicas": "[parameters('containerMinReplicas')]", - "maxReplicas": "[parameters('containerMaxReplicas')]" - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" - ] - }, - { - "condition": "[variables('usePrivateRegistry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-registry-access', deployment().name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" - }, - "description": "Assigns ACR Pull permissions to access an Azure Container Registry." - }, - "parameters": { - "containerRegistryName": { - "type": "string" - }, - "principalId": { - "type": "string" - } - }, - "variables": { - "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", - "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", - "properties": { - "roleDefinitionId": "[variables('acrPullRole')]", - "principalType": "ServicePrincipal", - "principalId": "[parameters('principalId')]" - } - } - ] - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" - }, - "identityPrincipalId": { - "type": "string", - "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" - }, - "imageName": { - "type": "string", - "value": "[parameters('imageName')]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "serviceBind": { - "type": "object", - "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" - }, - "uri": { - "type": "string", - "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" - } - } - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" - }, - "imageName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" - }, - "name": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" - }, - "uri": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" - } - } - } - } - } - ], - "outputs": { - "SERVICE_WEB_IDENTITY_PRINCIPAL_ID": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" - }, - "SERVICE_WEB_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" - }, - "SERVICE_WEB_URI": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" - }, - "SERVICE_WEB_IMAGE_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'api')]", - "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "api", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('apiContainerAppName'))), createObject('value', parameters('apiContainerAppName')), createObject('value', format('{0}api-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "identityName": { - "value": "[format('{0}api-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" - }, - "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" - }, - "containerAppsEnvironmentName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" - }, - "containerRegistryName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" - }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, - "corsAcaUrl": { - "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" - }, - "exists": { - "value": "[parameters('apiAppExists')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11092891629527222377" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "identityName": { - "type": "string" - }, - "applicationInsightsName": { - "type": "string" - }, - "containerAppsEnvironmentName": { - "type": "string" - }, - "containerRegistryName": { - "type": "string" - }, - "keyVaultName": { - "type": "string" - }, - "serviceName": { - "type": "string", - "defaultValue": "api" - }, - "corsAcaUrl": { - "type": "string" - }, - "exists": { - "type": "bool" - } - }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[parameters('identityName')]", - "location": "[parameters('location')]" - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "api-keyvault-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "principalId": { - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" - }, - "description": "Assigns an Azure Key Vault access policy." - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "add" - }, - "keyVaultName": { - "type": "string" - }, - "permissions": { - "type": "object", - "defaultValue": { - "secrets": [ - "get", - "list" - ] - } - }, - "principalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "properties": { - "accessPolicies": [ - { - "objectId": "[parameters('principalId')]", - "tenantId": "[subscription().tenantId]", - "permissions": "[parameters('permissions')]" - } - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-container-app', parameters('serviceName'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" - }, - "identityType": { - "value": "UserAssigned" - }, - "identityName": { - "value": "[parameters('identityName')]" - }, - "exists": { - "value": "[parameters('exists')]" - }, - "containerAppsEnvironmentName": { - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "containerCpuCoreCount": { - "value": "1.0" - }, - "containerMemory": { - "value": "2.0Gi" - }, - "env": { - "value": [ - { - "name": "AZURE_CLIENT_ID", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').clientId]" - }, - { - "name": "AZURE_KEY_VAULT_ENDPOINT", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" - }, - { - "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" - }, - { - "name": "API_ALLOW_ORIGINS", - "value": "[parameters('corsAcaUrl')]" - } - ] - }, - "targetPort": { - "value": 3100 - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" - }, - "description": "Creates or updates an existing Azure Container App." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "containerAppsEnvironmentName": { - "type": "string", - "metadata": { - "description": "The environment name for the container apps" - } - }, - "containerCpuCoreCount": { - "type": "string", - "defaultValue": "0.5", - "metadata": { - "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" - } - }, - "containerMaxReplicas": { - "type": "int", - "defaultValue": 10, - "minValue": 1, - "metadata": { - "description": "The maximum number of replicas to run. Must be at least 1." - } - }, - "containerMemory": { - "type": "string", - "defaultValue": "1.0Gi", - "metadata": { - "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" - } - }, - "containerMinReplicas": { - "type": "int", - "defaultValue": 1, - "minValue": 1, - "metadata": { - "description": "The minimum number of replicas to run. Must be at least 1." - } - }, - "containerName": { - "type": "string", - "defaultValue": "main", - "metadata": { - "description": "The name of the container" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container registry" - } - }, - "daprAppProtocol": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "http", - "grpc" - ], - "metadata": { - "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" - } - }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable or disable Dapr for the container app" - } - }, - "daprAppId": { - "type": "string", - "defaultValue": "[parameters('containerName')]", - "metadata": { - "description": "The Dapr app ID" - } - }, - "exists": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Specifies if the resource already exists" - } - }, - "ingressEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if Ingress is enabled for the container app" - } - }, - "identityType": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "SystemAssigned", - "UserAssigned" - ], - "metadata": { - "description": "The type of identity for the resource" - } - }, - "identityName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the user-assigned identity" - } - }, - "imageName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container image" - } - }, - "secrets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The secrets required for the container" - } - }, - "env": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The environment variables for the container" + "description": "The environment variables for the container" } }, "external": { @@ -1825,6 +619,9 @@ "containerAppsEnvironmentName": { "value": "[parameters('containerAppsEnvironmentName')]" }, + "containerAppsEnvironmentResourceGroupName": { + "value": "[parameters('containerAppsEnvironmentResourceGroupName')]" + }, "containerRegistryName": { "value": "[parameters('containerRegistryName')]" }, @@ -1873,7 +670,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "1912096201798605494" + "templateHash": "10348832124348087726" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1902,6 +699,12 @@ "description": "Name of the environment for container apps" } }, + "containerAppsEnvironmentResourceGroupName": { + "type": "string", + "metadata": { + "description": "Resource Group Name of the environment for container apps" + } + }, "containerCpuCoreCount": { "type": "string", "defaultValue": "0.5", @@ -2066,7 +869,7 @@ "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" }, "properties": { - "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "managedEnvironmentId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerAppsEnvironmentResourceGroupName')), 'Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", "configuration": { "activeRevisionsMode": "[parameters('revisionMode')]", "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", @@ -2156,7 +959,7 @@ "outputs": { "defaultDomain": { "type": "string", - "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerAppsEnvironmentResourceGroupName')), 'Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" }, "identityPrincipalId": { "type": "string", @@ -2202,27 +1005,23 @@ } } } - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]", - "[resourceId('Microsoft.Resources/deployments', 'api-keyvault-access')]" - ] + } } ], "outputs": { - "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "SERVICE_WEB_IDENTITY_PRINCIPAL_ID": { "type": "string", "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" }, - "SERVICE_API_NAME": { + "SERVICE_WEB_NAME": { "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" }, - "SERVICE_API_URI": { + "SERVICE_WEB_URI": { "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" }, - "SERVICE_API_IMAGE_NAME": { + "SERVICE_WEB_IMAGE_NAME": { "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" } @@ -2230,33 +1029,53 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'app-container-registry')]" ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "cosmos", + "name": "api", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", - "databaseName": { - "value": "[parameters('cosmosDatabaseName')]" - }, + "name": "[if(not(empty(parameters('apiContainerAppName'))), createObject('value', parameters('apiContainerAppName')), createObject('value', format('{0}api-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", "location": { "value": "[parameters('location')]" }, "tags": { "value": "[variables('tags')]" }, + "identityName": { + "value": "[format('{0}api-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + }, + "applicationInsightsResourceGroupName": { + "value": "[parameters('applicationInsightsResourceGroupName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerAppsEnvironmentResourceGroupName": { + "value": "[parameters('containerAppsEnvironmentResourceGroupName')]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app-container-registry'), '2022-09-01').outputs.name.value]" + }, "keyVaultName": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "corsAcaUrl": { + "value": "[parameters('corsAcaUrl')]" + }, + "exists": { + "value": "[parameters('apiAppExists')]" } }, "template": { @@ -2266,11 +1085,11 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "templateHash": "12086032597939785" } }, "parameters": { - "accountName": { + "name": { "type": "string" }, "location": { @@ -2281,63 +1100,181 @@ "type": "object", "defaultValue": {} }, - "collections": { - "type": "array", - "defaultValue": [ - { - "name": "TodoList", - "id": "TodoList", - "shardKey": "Hash", - "indexKey": "_id" - }, - { - "name": "TodoItem", - "id": "TodoItem", - "shardKey": "Hash", - "indexKey": "_id" - } - ] + "identityName": { + "type": "string" }, - "databaseName": { - "type": "string", - "defaultValue": "" + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsResourceGroupName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerAppsEnvironmentResourceGroupName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" }, "keyVaultName": { "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "api" + }, + "corsAcaUrl": { + "type": "string" + }, + "exists": { + "type": "bool" } }, - "variables": { - "defaultDatabaseName": "Todo", - "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" - }, "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "cosmos-mongo", + "name": "api-keyvault-access", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "accountName": { - "value": "[parameters('accountName')]" + "keyVaultName": { + "value": "[parameters('keyVaultName')]" }, - "databaseName": { - "value": "[variables('actualDatabaseName')]" + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" }, "location": { "value": "[parameters('location')]" }, - "collections": { - "value": "[parameters('collections')]" + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerAppsEnvironmentResourceGroupName": { + "value": "[parameters('containerAppsEnvironmentResourceGroupName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "1.0" + }, + "containerMemory": { + "value": "2.0Gi" }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" + "env": { + "value": [ + { + "name": "AZURE_CLIENT_ID", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').clientId]" + }, + { + "name": "AZURE_KEY_VAULT_ENDPOINT", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('applicationInsightsResourceGroupName')), 'Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "API_ALLOW_ORIGINS", + "value": "[parameters('corsAcaUrl')]" + } + ] }, - "tags": { - "value": "[parameters('tags')]" + "targetPort": { + "value": 3100 } }, "template": { @@ -2347,15 +1284,12 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "templateHash": "12494944888544926830" }, - "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + "description": "Creates or updates an existing Azure Container App." }, "parameters": { - "accountName": { - "type": "string" - }, - "databaseName": { + "name": { "type": "string" }, "location": { @@ -2366,66 +1300,168 @@ "type": "object", "defaultValue": {} }, - "collections": { - "type": "array", - "defaultValue": [] + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } }, - "connectionStringKey": { + "containerAppsEnvironmentResourceGroupName": { "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + "metadata": { + "description": "Resource Group Name of the environment for container apps" + } }, - "keyVaultName": { - "type": "string" + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } } }, "resources": [ - { - "copy": { - "name": "list", - "count": "[length(parameters('collections'))]" - }, - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", - "properties": { - "resource": { - "id": "[parameters('collections')[copyIndex()].id]", - "shardKey": { - "_id": "[parameters('collections')[copyIndex()].shardKey]" - }, - "indexes": [ - { - "key": { - "keys": [ - "[parameters('collections')[copyIndex()].indexKey]" - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" - ] - }, - { - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": { - "id": "[parameters('databaseName')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" - ] - }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "cosmos-mongo-account", + "name": "[format('{0}-update', deployment().name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2433,19 +1469,71 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('accountName')]" + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" }, - "location": { - "value": "[parameters('location')]" + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" + "containerAppsEnvironmentResourceGroupName": { + "value": "[parameters('containerAppsEnvironmentResourceGroupName')]" }, - "tags": { - "value": "[parameters('tags')]" + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" } }, "template": { @@ -2455,9 +1543,9 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "templateHash": "10348832124348087726" }, - "description": "Creates an Azure Cosmos DB for MongoDB account." + "description": "Creates a container app in an Azure Container App environment." }, "parameters": { "name": { @@ -2471,43 +1559,236 @@ "type": "object", "defaultValue": {} }, - "keyVaultName": { - "type": "string" + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerAppsEnvironmentResourceGroupName": { + "type": "string", + "metadata": { + "description": "Resource Group Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerAppsEnvironmentResourceGroupName')), 'Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - } - }, - "resources": [ { + "condition": "[variables('usePrivateRegistry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "cosmos-account", + "name": "[format('{0}-registry-access', deployment().name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "kind": { - "value": "MongoDB" + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" }, - "tags": { - "value": "[parameters('tags')]" - } + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2516,114 +1797,62 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "templateHash": "15144906240959446537" }, - "description": "Creates an Azure Cosmos DB account." + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, "parameters": { - "name": { + "containerRegistryName": { "type": "string" }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - }, - "keyVaultName": { + "principalId": { "type": "string" - }, - "kind": { - "type": "string", - "allowedValues": [ - "GlobalDocumentDB", - "MongoDB", - "Parse" - ] } }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, "resources": [ { - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2022-08-15", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", "properties": { - "consistencyPolicy": { - "defaultConsistencyLevel": "Session" - }, - "locations": [ - { - "locationName": "[parameters('location')]", - "failoverPriority": 0, - "isZoneRedundant": false - } - ], - "databaseAccountOfferType": "Standard", - "enableAutomaticFailover": false, - "enableMultipleWriteLocations": false, - "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", - "capabilities": [ - { - "name": "EnableServerless" - } - ] + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" } - }, - { - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", - "properties": { - "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" - }, - "dependsOn": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" - ] - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[parameters('connectionStringKey')]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" - }, - "id": { - "type": "string", - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" } - } + ] } } } ], "outputs": { - "connectionStringKey": { + "defaultDomain": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerAppsEnvironmentResourceGroupName')), 'Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" }, - "endpoint": { + "identityPrincipalId": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" }, - "id": { + "imageName": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" } } } @@ -2631,62 +1860,77 @@ } ], "outputs": { - "connectionStringKey": { + "defaultDomain": { "type": "string", - "value": "[parameters('connectionStringKey')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" }, - "databaseName": { + "imageName": { "type": "string", - "value": "[parameters('databaseName')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" }, - "endpoint": { + "name": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" } } } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]", + "[resourceId('Microsoft.Resources/deployments', 'api-keyvault-access')]" + ] } ], "outputs": { - "connectionStringKey": { + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" }, - "databaseName": { + "SERVICE_API_NAME": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" }, - "endpoint": { + "SERVICE_API_URI": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_API_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" } } } }, "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'app-container-registry')]", "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "keyvault", + "name": "app-container-registry", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "name": { + "value": "[parameters('containerRegistryName')]" + }, "location": { "value": "[parameters('location')]" }, + "adminUserEnabled": { + "value": "[parameters('containerRegistryAdminUserEnabled')]" + }, "tags": { "value": "[variables('tags')]" - }, - "principalId": { - "value": "[parameters('principalId')]" } }, "template": { @@ -2696,9 +1940,9 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "17948623451174129396" + "templateHash": "12834334744516280883" }, - "description": "Creates an Azure Key Vault." + "description": "Creates an Azure Container Registry." }, "parameters": { "name": { @@ -2712,32 +1956,127 @@ "type": "object", "defaultValue": {} }, - "principalId": { + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { "type": "string", - "defaultValue": "" + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } } }, "resources": [ { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] } ], "outputs": { - "endpoint": { + "loginServer": { "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" }, "name": { "type": "string", @@ -2750,22 +2089,26 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "monitoring", + "name": "cosmos", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, "location": { "value": "[parameters('location')]" }, "tags": { "value": "[variables('tags')]" }, - "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", - "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", - "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2774,21 +2117,13 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "10041669792322197047" - }, - "description": "Creates an Application Insights instance and a Log Analytics workspace." + "templateHash": "5730728686647632614" + } }, "parameters": { - "logAnalyticsName": { - "type": "string" - }, - "applicationInsightsName": { + "accountName": { "type": "string" }, - "applicationInsightsDashboardName": { - "type": "string", - "defaultValue": "" - }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]" @@ -2796,108 +2131,64 @@ "tags": { "type": "object", "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" } }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, "resources": [ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "loganalytics", + "name": "cosmos-mongo", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[parameters('logAnalyticsName')]" + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" }, "location": { "value": "[parameters('location')]" }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" - }, - "description": "Creates a Log Analytics workspace." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - } - }, - "resources": [ - { - "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-12-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "retentionInDays": 30, - "features": { - "searchVersion": 1 - }, - "sku": { - "name": "PerGB2018" - } - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "applicationinsights", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('applicationInsightsName')]" + "collections": { + "value": "[parameters('collections')]" }, - "location": { - "value": "[parameters('location')]" + "keyVaultName": { + "value": "[parameters('keyVaultName')]" }, "tags": { "value": "[parameters('tags')]" - }, - "dashboardName": { - "value": "[parameters('applicationInsightsDashboardName')]" - }, - "logAnalyticsWorkspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" } }, "template": { @@ -2907,17 +2198,16 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "templateHash": "14549161001187918251" }, - "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, "parameters": { - "name": { + "accountName": { "type": "string" }, - "dashboardName": { - "type": "string", - "defaultValue": "" + "databaseName": { + "type": "string" }, "location": { "type": "string", @@ -2927,28 +2217,66 @@ "type": "object", "defaultValue": {} }, - "logAnalyticsWorkspaceId": { + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { "type": "string" } }, "resources": [ { - "type": "Microsoft.Insights/components", - "apiVersion": "2020-02-02", - "name": "[parameters('name')]", - "location": "[parameters('location')]", + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", "tags": "[parameters('tags')]", - "kind": "web", "properties": { - "Application_Type": "web", - "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" - } + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] }, { - "condition": "[not(empty(parameters('dashboardName')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "application-insights-dashboard", + "name": "cosmos-mongo-account", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2956,13 +2284,19 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('dashboardName')]" + "value": "[parameters('accountName')]" }, "location": { "value": "[parameters('location')]" }, - "applicationInsightsName": { - "value": "[parameters('name')]" + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" } }, "template": { @@ -2972,17 +2306,14 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "templateHash": "8317058180807592714" }, - "description": "Creates a dashboard for an Application Insights instance." + "description": "Creates an Azure Cosmos DB for MongoDB account." }, "parameters": { "name": { "type": "string" }, - "applicationInsightsName": { - "type": "string" - }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]" @@ -2990,1265 +2321,278 @@ "tags": { "type": "object", "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" } }, "resources": [ { - "type": "Microsoft.Portal/dashboards", - "apiVersion": "2020-09-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", "properties": { - "lenses": [ - { - "order": 0, - "parts": [ - { - "position": { - "x": 0, - "y": 0, - "colSpan": 2, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "id", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", - "asset": { - "idInputName": "id", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "overview" - } - }, - { - "position": { - "x": 2, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "ProactiveDetection" - } - }, - { - "position": { - "x": 3, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - } - ], - "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 4, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-04T01:20:33.345Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 5, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-08T18:47:35.237Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "ConfigurationId", - "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 0, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Usage", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 3, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-04T01:22:35.782Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - } - ], - "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 4, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Reliability", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 7, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "DataModel", - "value": { - "version": "1.0.0", - "timeContext": { - "durationMs": 86400000, - "createdTime": "2018-05-04T23:42:40.072Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - "isOptional": true - }, - { - "name": "ConfigurationId", - "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", - "isOptional": true - } - ], - "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", - "isAdapter": true, - "asset": { - "idInputName": "ResourceId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "failures" - } - }, - { - "position": { - "x": 8, - "y": 1, - "colSpan": 3, - "rowSpan": 1 + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Responsiveness\r\n", - "title": "", - "subtitle": "" - } - } + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false } - } - }, - { - "position": { - "x": 11, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "DataModel", - "value": { - "version": "1.0.0", - "timeContext": { - "durationMs": 86400000, - "createdTime": "2018-05-04T23:43:37.804Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - "isOptional": true - }, - { - "name": "ConfigurationId", - "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", - "isOptional": true - } - ], - "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", - "isAdapter": true, - "asset": { - "idInputName": "ResourceId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "performance" - } - }, - { - "position": { - "x": 12, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Browser", - "title": "", - "subtitle": "" - } - } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" } - } - }, - { - "position": { - "x": 15, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "MetricsExplorerJsonDefinitionId", - "value": "BrowserPerformanceTimelineMetrics" - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "createdTime": "2018-05-08T12:16:27.534Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "CurrentFilter", - "value": { - "eventTypes": [ - 4, - 1, - 3, - 5, - 2, - 6, - 13 - ], - "typeFacets": {}, - "isPermissive": false - } - }, - { - "name": "id", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "browser" - } - }, - { - "position": { - "x": 0, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "sessions/count", - "aggregationType": 5, - "namespace": "microsoft.insights/components/kusto", - "metricVisualization": { - "displayName": "Sessions", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "users/count", - "aggregationType": 5, - "namespace": "microsoft.insights/components/kusto", - "metricVisualization": { - "displayName": "Users", - "color": "#7E58FF" - } - } - ], - "title": "Unique sessions and users", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "segmentationUsers" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "requests/failed", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Failed requests", - "color": "#EC008C" - } - } - ], - "title": "Failed requests", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "failures" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "requests/duration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Server response time", - "color": "#00BCF2" - } - } - ], - "title": "Server response time", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "performance" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 12, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/networkDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Page load network connect time", - "color": "#7E58FF" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/processingDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Client processing time", - "color": "#44F1C8" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/sendDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Send request time", - "color": "#EB9371" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/receiveDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Receiving response time", - "color": "#0672F1" - } - } - ], - "title": "Average page load time breakdown", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 0, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "availabilityResults/availabilityPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Availability", - "color": "#47BDF5" - } - } - ], - "title": "Average availability", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "availability" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "exceptions/server", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Server exceptions", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "dependencies/failed", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Dependency failures", - "color": "#7E58FF" - } - } - ], - "title": "Server exceptions and Dependency failures", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processorCpuPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Processor time", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processCpuPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Process CPU", - "color": "#7E58FF" - } - } - ], - "title": "Average processor and process CPU utilization", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 12, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "exceptions/browser", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Browser exceptions", - "color": "#47BDF5" - } - } - ], - "title": "Browser exceptions", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 0, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "availabilityResults/count", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Availability test results count", - "color": "#47BDF5" - } - } - ], - "title": "Availability test results count", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processIOBytesPerSecond", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Process IO rate", - "color": "#47BDF5" - } - } - ], - "title": "Average process I/O rate", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/memoryAvailableBytes", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Available memory", - "color": "#47BDF5" - } - } - ], - "title": "Average available memory", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } + ] } - ] + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } } - ] + } } } - ] + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } } - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/components', parameters('name'))]" - ] + } } ], "outputs": { - "connectionString": { + "connectionStringKey": { "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + "value": "[parameters('connectionStringKey')]" }, - "instrumentationKey": { + "databaseName": { "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + "value": "[parameters('databaseName')]" }, - "name": { + "endpoint": { "type": "string", - "value": "[parameters('name')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" } } } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" - ] + } } ], "outputs": { - "applicationInsightsConnectionString": { + "connectionStringKey": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" }, - "applicationInsightsInstrumentationKey": { + "databaseName": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" }, - "applicationInsightsName": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} }, - "logAnalyticsWorkspaceId": { + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" }, - "logAnalyticsWorkspaceName": { + "name": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + "value": "[parameters('name')]" } } } @@ -4273,7 +2617,7 @@ "value": "[variables('tags')]" }, "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + "value": "[parameters('applicationInsightsName')]" } }, "template": { @@ -4390,10 +2734,7 @@ } } } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] + } }, { "condition": "[parameters('useAPIM')]", @@ -4609,27 +2950,15 @@ }, "API_CORS_ACA_URL": { "type": "string", - "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" - }, - "APPLICATIONINSIGHTS_CONNECTION_STRING": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" - }, - "APPLICATIONINSIGHTS_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" - }, - "AZURE_CONTAINER_ENVIRONMENT_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + "value": "[parameters('corsAcaUrl')]" }, "AZURE_CONTAINER_REGISTRY_ENDPOINT": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryLoginServer.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app-container-registry'), '2022-09-01').outputs.loginServer.value]" }, "AZURE_CONTAINER_REGISTRY_NAME": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app-container-registry'), '2022-09-01').outputs.name.value]" }, "AZURE_KEY_VAULT_ENDPOINT": { "type": "string", @@ -4651,10 +2980,6 @@ "type": "string", "value": "[if(parameters('useAPIM'), reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" }, - "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" - }, "REACT_APP_WEB_BASE_URL": { "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" @@ -4674,6 +2999,14 @@ "SERVICE_API_ENDPOINTS": { "type": "array", "value": "[if(parameters('useAPIM'), createArray(reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value), createArray())]" + }, + "registryLoginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app-container-registry'), '2022-09-01').outputs.loginServer.value]" + }, + "registryName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'app-container-registry'), '2022-09-01').outputs.name.value]" } } } \ No newline at end of file diff --git a/Environments/Todo-Shared-ACA/azuredeploy.json b/Environments/Todo-Shared-ACA/azuredeploy.json index 65c1d9f7..94f1cb0a 100644 --- a/Environments/Todo-Shared-ACA/azuredeploy.json +++ b/Environments/Todo-Shared-ACA/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "5915759848307494038" + "templateHash": "7010794115259852587" } }, "parameters": { @@ -25,10 +25,6 @@ "description": "Primary location for all resources" } }, - "apiContainerAppName": { - "type": "string", - "defaultValue": "" - }, "applicationInsightsDashboardName": { "type": "string", "defaultValue": "" @@ -41,62 +37,9 @@ "type": "string", "defaultValue": "" }, - "containerRegistryName": { - "type": "string", - "defaultValue": "" - }, - "cosmosAccountName": { - "type": "string", - "defaultValue": "" - }, - "cosmosDatabaseName": { - "type": "string", - "defaultValue": "" - }, - "keyVaultName": { - "type": "string", - "defaultValue": "" - }, "logAnalyticsName": { "type": "string", "defaultValue": "" - }, - "webContainerAppName": { - "type": "string", - "defaultValue": "" - }, - "apimServiceName": { - "type": "string", - "defaultValue": "" - }, - "apiAppExists": { - "type": "bool", - "defaultValue": false - }, - "webAppExists": { - "type": "bool", - "defaultValue": false - }, - "useAPIM": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API" - } - }, - "principalId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Id of the user or app to assign application roles" - } - }, - "webApiBaseUrl": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The base URL used by the web service for sending API requests" - } } }, "variables": { @@ -264,10 +207,6 @@ "value": "[variables('tags')]" }, "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", - "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", - "containerRegistryAdminUserEnabled": { - "value": true - }, "logAnalyticsWorkspaceName": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" }, @@ -282,7 +221,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "14116108111976192358" + "templateHash": "10266908178814002202" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -301,17 +240,6 @@ "containerAppsEnvironmentName": { "type": "string" }, - "containerRegistryName": { - "type": "string" - }, - "containerRegistryResourceGroupName": { - "type": "string", - "defaultValue": "" - }, - "containerRegistryAdminUserEnabled": { - "type": "bool", - "defaultValue": false - }, "logAnalyticsWorkspaceName": { "type": "string" }, @@ -380,2372 +308,73 @@ "daprEnabled": { "type": "bool", "defaultValue": false, - "metadata": { - "description": "Specifies if Dapr is enabled" - } - }, - "logAnalyticsWorkspaceName": { - "type": "string", - "metadata": { - "description": "Name of the Log Analytics workspace" - } - } - }, - "resources": [ - { - "type": "Microsoft.App/managedEnvironments", - "apiVersion": "2023-04-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "appLogsConfiguration": { - "destination": "log-analytics", - "logAnalyticsConfiguration": { - "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", - "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" - } - }, - "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" - }, - "id": { - "type": "string", - "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-container-registry', parameters('name'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('containerRegistryName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "adminUserEnabled": { - "value": "[parameters('containerRegistryAdminUserEnabled')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" - }, - "description": "Creates an Azure Container Registry." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "adminUserEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates whether admin user is enabled" - } - }, - "anonymousPullEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates whether anonymous pull is enabled" - } - }, - "dataEndpointEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates whether data endpoint is enabled" - } - }, - "encryption": { - "type": "object", - "defaultValue": { - "status": "disabled" - }, - "metadata": { - "description": "Encryption settings" - } - }, - "networkRuleBypassOptions": { - "type": "string", - "defaultValue": "AzureServices", - "metadata": { - "description": "Options for bypassing network rules" - } - }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "Enabled", - "metadata": { - "description": "Public network access setting" - } - }, - "sku": { - "type": "object", - "defaultValue": { - "name": "Basic" - }, - "metadata": { - "description": "SKU settings" - } - }, - "zoneRedundancy": { - "type": "string", - "defaultValue": "Disabled", - "metadata": { - "description": "Zone redundancy setting" - } - }, - "workspaceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The log analytics workspace ID used for logging and monitoring" - } - } - }, - "resources": [ - { - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2022-02-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", - "properties": { - "adminUserEnabled": "[parameters('adminUserEnabled')]", - "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", - "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", - "encryption": "[parameters('encryption')]", - "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]", - "zoneRedundancy": "[parameters('zoneRedundancy')]" - } - }, - { - "condition": "[not(empty(parameters('workspaceId')))]", - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", - "name": "registry-diagnostics", - "properties": { - "workspaceId": "[parameters('workspaceId')]", - "logs": [ - { - "category": "ContainerRegistryRepositoryEvents", - "enabled": true - }, - { - "category": "ContainerRegistryLoginEvents", - "enabled": true - } - ], - "metrics": [ - { - "category": "AllMetrics", - "enabled": true, - "timeGrain": "PT1M" - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" - ] - } - ], - "outputs": { - "loginServer": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" - }, - "environmentName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" - }, - "environmentId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" - }, - "registryLoginServer": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.loginServer.value]" - }, - "registryName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.name.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "web", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('webContainerAppName'))), createObject('value', parameters('webContainerAppName')), createObject('value', format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "identityName": { - "value": "[format('{0}web-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" - }, - "apiBaseUrl": "[if(not(empty(parameters('webApiBaseUrl'))), createObject('value', parameters('webApiBaseUrl')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value))]", - "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" - }, - "containerAppsEnvironmentName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" - }, - "containerRegistryName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" - }, - "exists": { - "value": "[parameters('webAppExists')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5244656399300381833" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "identityName": { - "type": "string" - }, - "apiBaseUrl": { - "type": "string" - }, - "applicationInsightsName": { - "type": "string" - }, - "containerAppsEnvironmentName": { - "type": "string" - }, - "containerRegistryName": { - "type": "string" - }, - "serviceName": { - "type": "string", - "defaultValue": "web" - }, - "exists": { - "type": "bool" - } - }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[parameters('identityName')]", - "location": "[parameters('location')]" - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-container-app', parameters('serviceName'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" - }, - "identityType": { - "value": "UserAssigned" - }, - "identityName": { - "value": "[parameters('identityName')]" - }, - "exists": { - "value": "[parameters('exists')]" - }, - "containerAppsEnvironmentName": { - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "env": { - "value": [ - { - "name": "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" - }, - { - "name": "REACT_APP_API_BASE_URL", - "value": "[parameters('apiBaseUrl')]" - }, - { - "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" - } - ] - }, - "targetPort": { - "value": 80 - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" - }, - "description": "Creates or updates an existing Azure Container App." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "containerAppsEnvironmentName": { - "type": "string", - "metadata": { - "description": "The environment name for the container apps" - } - }, - "containerCpuCoreCount": { - "type": "string", - "defaultValue": "0.5", - "metadata": { - "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" - } - }, - "containerMaxReplicas": { - "type": "int", - "defaultValue": 10, - "minValue": 1, - "metadata": { - "description": "The maximum number of replicas to run. Must be at least 1." - } - }, - "containerMemory": { - "type": "string", - "defaultValue": "1.0Gi", - "metadata": { - "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" - } - }, - "containerMinReplicas": { - "type": "int", - "defaultValue": 1, - "minValue": 1, - "metadata": { - "description": "The minimum number of replicas to run. Must be at least 1." - } - }, - "containerName": { - "type": "string", - "defaultValue": "main", - "metadata": { - "description": "The name of the container" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container registry" - } - }, - "daprAppProtocol": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "http", - "grpc" - ], - "metadata": { - "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" - } - }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable or disable Dapr for the container app" - } - }, - "daprAppId": { - "type": "string", - "defaultValue": "[parameters('containerName')]", - "metadata": { - "description": "The Dapr app ID" - } - }, - "exists": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Specifies if the resource already exists" - } - }, - "ingressEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if Ingress is enabled for the container app" - } - }, - "identityType": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "SystemAssigned", - "UserAssigned" - ], - "metadata": { - "description": "The type of identity for the resource" - } - }, - "identityName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the user-assigned identity" - } - }, - "imageName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container image" - } - }, - "secrets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The secrets required for the container" - } - }, - "env": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The environment variables for the container" - } - }, - "external": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if the resource ingress is exposed externally" - } - }, - "serviceBinds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The service binds associated with the container" - } - }, - "targetPort": { - "type": "int", - "defaultValue": 80, - "metadata": { - "description": "The target port for the container" - } - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-update', deployment().name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "identityType": { - "value": "[parameters('identityType')]" - }, - "identityName": { - "value": "[parameters('identityName')]" - }, - "ingressEnabled": { - "value": "[parameters('ingressEnabled')]" - }, - "containerName": { - "value": "[parameters('containerName')]" - }, - "containerAppsEnvironmentName": { - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "containerCpuCoreCount": { - "value": "[parameters('containerCpuCoreCount')]" - }, - "containerMemory": { - "value": "[parameters('containerMemory')]" - }, - "containerMinReplicas": { - "value": "[parameters('containerMinReplicas')]" - }, - "containerMaxReplicas": { - "value": "[parameters('containerMaxReplicas')]" - }, - "daprEnabled": { - "value": "[parameters('daprEnabled')]" - }, - "daprAppId": { - "value": "[parameters('daprAppId')]" - }, - "daprAppProtocol": { - "value": "[parameters('daprAppProtocol')]" - }, - "secrets": { - "value": "[parameters('secrets')]" - }, - "external": { - "value": "[parameters('external')]" - }, - "env": { - "value": "[parameters('env')]" - }, - "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", - "targetPort": { - "value": "[parameters('targetPort')]" - }, - "serviceBinds": { - "value": "[parameters('serviceBinds')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1912096201798605494" - }, - "description": "Creates a container app in an Azure Container App environment." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "allowedOrigins": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Allowed origins" - } - }, - "containerAppsEnvironmentName": { - "type": "string", - "metadata": { - "description": "Name of the environment for container apps" - } - }, - "containerCpuCoreCount": { - "type": "string", - "defaultValue": "0.5", - "metadata": { - "description": "CPU cores allocated to a single container instance, e.g., 0.5" - } - }, - "containerMaxReplicas": { - "type": "int", - "defaultValue": 10, - "minValue": 1, - "metadata": { - "description": "The maximum number of replicas to run. Must be at least 1." - } - }, - "containerMemory": { - "type": "string", - "defaultValue": "1.0Gi", - "metadata": { - "description": "Memory allocated to a single container instance, e.g., 1Gi" - } - }, - "containerMinReplicas": { - "type": "int", - "defaultValue": 1, - "metadata": { - "description": "The minimum number of replicas to run. Must be at least 1." - } - }, - "containerName": { - "type": "string", - "defaultValue": "main", - "metadata": { - "description": "The name of the container" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container registry" - } - }, - "daprAppProtocol": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "http", - "grpc" - ], - "metadata": { - "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" - } - }, - "daprAppId": { - "type": "string", - "defaultValue": "[parameters('containerName')]", - "metadata": { - "description": "The Dapr app ID" - } - }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable Dapr" - } - }, - "env": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The environment variables for the container" - } - }, - "external": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if the resource ingress is exposed externally" - } - }, - "identityName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the user-assigned identity" - } - }, - "identityType": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "SystemAssigned", - "UserAssigned" - ], - "metadata": { - "description": "The type of identity for the resource" - } - }, - "imageName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container image" - } - }, - "ingressEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if Ingress is enabled for the container app" - } - }, - "revisionMode": { - "type": "string", - "defaultValue": "Single" - }, - "secrets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The secrets required for the container" - } - }, - "serviceBinds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The service binds associated with the container" - } - }, - "serviceType": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container apps add-on to use. e.g. redis" - } - }, - "targetPort": { - "type": "int", - "defaultValue": 80, - "metadata": { - "description": "The target port for the container" - } - } - }, - "variables": { - "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", - "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" - }, - "resources": [ - { - "type": "Microsoft.App/containerApps", - "apiVersion": "2023-04-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": { - "type": "[variables('normalizedIdentityType')]", - "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" - }, - "properties": { - "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", - "configuration": { - "activeRevisionsMode": "[parameters('revisionMode')]", - "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", - "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", - "secrets": "[parameters('secrets')]", - "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", - "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" - }, - "template": { - "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", - "containers": [ - { - "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", - "name": "[parameters('containerName')]", - "env": "[parameters('env')]", - "resources": { - "cpu": "[json(parameters('containerCpuCoreCount'))]", - "memory": "[parameters('containerMemory')]" - } - } - ], - "scale": { - "minReplicas": "[parameters('containerMinReplicas')]", - "maxReplicas": "[parameters('containerMaxReplicas')]" - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" - ] - }, - { - "condition": "[variables('usePrivateRegistry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-registry-access', deployment().name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" - }, - "description": "Assigns ACR Pull permissions to access an Azure Container Registry." - }, - "parameters": { - "containerRegistryName": { - "type": "string" - }, - "principalId": { - "type": "string" - } - }, - "variables": { - "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", - "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", - "properties": { - "roleDefinitionId": "[variables('acrPullRole')]", - "principalType": "ServicePrincipal", - "principalId": "[parameters('principalId')]" - } - } - ] - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" - }, - "identityPrincipalId": { - "type": "string", - "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" - }, - "imageName": { - "type": "string", - "value": "[parameters('imageName')]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "serviceBind": { - "type": "object", - "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" - }, - "uri": { - "type": "string", - "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" - } - } - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" - }, - "imageName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" - }, - "name": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" - }, - "uri": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" - } - } - } - } - } - ], - "outputs": { - "SERVICE_WEB_IDENTITY_PRINCIPAL_ID": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" - }, - "SERVICE_WEB_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" - }, - "SERVICE_WEB_URI": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" - }, - "SERVICE_WEB_IMAGE_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'api')]", - "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "api", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('apiContainerAppName'))), createObject('value', parameters('apiContainerAppName')), createObject('value', format('{0}api-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "identityName": { - "value": "[format('{0}api-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" - }, - "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" - }, - "containerAppsEnvironmentName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" - }, - "containerRegistryName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" - }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, - "corsAcaUrl": { - "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" - }, - "exists": { - "value": "[parameters('apiAppExists')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11092891629527222377" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "identityName": { - "type": "string" - }, - "applicationInsightsName": { - "type": "string" - }, - "containerAppsEnvironmentName": { - "type": "string" - }, - "containerRegistryName": { - "type": "string" - }, - "keyVaultName": { - "type": "string" - }, - "serviceName": { - "type": "string", - "defaultValue": "api" - }, - "corsAcaUrl": { - "type": "string" - }, - "exists": { - "type": "bool" - } - }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[parameters('identityName')]", - "location": "[parameters('location')]" - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "api-keyvault-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "principalId": { - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" - }, - "description": "Assigns an Azure Key Vault access policy." - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "add" - }, - "keyVaultName": { - "type": "string" - }, - "permissions": { - "type": "object", - "defaultValue": { - "secrets": [ - "get", - "list" - ] - } - }, - "principalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "properties": { - "accessPolicies": [ - { - "objectId": "[parameters('principalId')]", - "tenantId": "[subscription().tenantId]", - "permissions": "[parameters('permissions')]" - } - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-container-app', parameters('serviceName'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" - }, - "identityType": { - "value": "UserAssigned" - }, - "identityName": { - "value": "[parameters('identityName')]" - }, - "exists": { - "value": "[parameters('exists')]" - }, - "containerAppsEnvironmentName": { - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "containerCpuCoreCount": { - "value": "1.0" - }, - "containerMemory": { - "value": "2.0Gi" - }, - "env": { - "value": [ - { - "name": "AZURE_CLIENT_ID", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').clientId]" - }, - { - "name": "AZURE_KEY_VAULT_ENDPOINT", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" - }, - { - "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" - }, - { - "name": "API_ALLOW_ORIGINS", - "value": "[parameters('corsAcaUrl')]" - } - ] - }, - "targetPort": { - "value": 3100 - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" - }, - "description": "Creates or updates an existing Azure Container App." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "containerAppsEnvironmentName": { - "type": "string", - "metadata": { - "description": "The environment name for the container apps" - } - }, - "containerCpuCoreCount": { - "type": "string", - "defaultValue": "0.5", - "metadata": { - "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" - } - }, - "containerMaxReplicas": { - "type": "int", - "defaultValue": 10, - "minValue": 1, - "metadata": { - "description": "The maximum number of replicas to run. Must be at least 1." - } - }, - "containerMemory": { - "type": "string", - "defaultValue": "1.0Gi", - "metadata": { - "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" - } - }, - "containerMinReplicas": { - "type": "int", - "defaultValue": 1, - "minValue": 1, - "metadata": { - "description": "The minimum number of replicas to run. Must be at least 1." - } - }, - "containerName": { - "type": "string", - "defaultValue": "main", - "metadata": { - "description": "The name of the container" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container registry" - } - }, - "daprAppProtocol": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "http", - "grpc" - ], - "metadata": { - "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" - } - }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable or disable Dapr for the container app" - } - }, - "daprAppId": { - "type": "string", - "defaultValue": "[parameters('containerName')]", - "metadata": { - "description": "The Dapr app ID" - } - }, - "exists": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Specifies if the resource already exists" - } - }, - "ingressEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if Ingress is enabled for the container app" - } - }, - "identityType": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "SystemAssigned", - "UserAssigned" - ], - "metadata": { - "description": "The type of identity for the resource" - } - }, - "identityName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the user-assigned identity" - } - }, - "imageName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container image" - } - }, - "secrets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The secrets required for the container" - } - }, - "env": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The environment variables for the container" - } - }, - "external": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if the resource ingress is exposed externally" - } - }, - "serviceBinds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The service binds associated with the container" - } - }, - "targetPort": { - "type": "int", - "defaultValue": 80, - "metadata": { - "description": "The target port for the container" - } - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-update', deployment().name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "identityType": { - "value": "[parameters('identityType')]" - }, - "identityName": { - "value": "[parameters('identityName')]" - }, - "ingressEnabled": { - "value": "[parameters('ingressEnabled')]" - }, - "containerName": { - "value": "[parameters('containerName')]" - }, - "containerAppsEnvironmentName": { - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "containerCpuCoreCount": { - "value": "[parameters('containerCpuCoreCount')]" - }, - "containerMemory": { - "value": "[parameters('containerMemory')]" - }, - "containerMinReplicas": { - "value": "[parameters('containerMinReplicas')]" - }, - "containerMaxReplicas": { - "value": "[parameters('containerMaxReplicas')]" - }, - "daprEnabled": { - "value": "[parameters('daprEnabled')]" - }, - "daprAppId": { - "value": "[parameters('daprAppId')]" - }, - "daprAppProtocol": { - "value": "[parameters('daprAppProtocol')]" - }, - "secrets": { - "value": "[parameters('secrets')]" - }, - "external": { - "value": "[parameters('external')]" - }, - "env": { - "value": "[parameters('env')]" - }, - "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", - "targetPort": { - "value": "[parameters('targetPort')]" - }, - "serviceBinds": { - "value": "[parameters('serviceBinds')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1912096201798605494" - }, - "description": "Creates a container app in an Azure Container App environment." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "allowedOrigins": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Allowed origins" - } - }, - "containerAppsEnvironmentName": { - "type": "string", - "metadata": { - "description": "Name of the environment for container apps" - } - }, - "containerCpuCoreCount": { - "type": "string", - "defaultValue": "0.5", - "metadata": { - "description": "CPU cores allocated to a single container instance, e.g., 0.5" - } - }, - "containerMaxReplicas": { - "type": "int", - "defaultValue": 10, - "minValue": 1, - "metadata": { - "description": "The maximum number of replicas to run. Must be at least 1." - } - }, - "containerMemory": { - "type": "string", - "defaultValue": "1.0Gi", - "metadata": { - "description": "Memory allocated to a single container instance, e.g., 1Gi" - } - }, - "containerMinReplicas": { - "type": "int", - "defaultValue": 1, - "metadata": { - "description": "The minimum number of replicas to run. Must be at least 1." - } - }, - "containerName": { - "type": "string", - "defaultValue": "main", - "metadata": { - "description": "The name of the container" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container registry" - } - }, - "daprAppProtocol": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "http", - "grpc" - ], - "metadata": { - "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" - } - }, - "daprAppId": { - "type": "string", - "defaultValue": "[parameters('containerName')]", - "metadata": { - "description": "The Dapr app ID" - } - }, - "daprEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable Dapr" - } - }, - "env": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The environment variables for the container" - } - }, - "external": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if the resource ingress is exposed externally" - } - }, - "identityName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the user-assigned identity" - } - }, - "identityType": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "SystemAssigned", - "UserAssigned" - ], - "metadata": { - "description": "The type of identity for the resource" - } - }, - "imageName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container image" - } - }, - "ingressEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies if Ingress is enabled for the container app" - } - }, - "revisionMode": { - "type": "string", - "defaultValue": "Single" - }, - "secrets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The secrets required for the container" - } - }, - "serviceBinds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The service binds associated with the container" - } - }, - "serviceType": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the container apps add-on to use. e.g. redis" - } - }, - "targetPort": { - "type": "int", - "defaultValue": 80, - "metadata": { - "description": "The target port for the container" - } - } - }, - "variables": { - "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", - "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" - }, - "resources": [ - { - "type": "Microsoft.App/containerApps", - "apiVersion": "2023-04-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": { - "type": "[variables('normalizedIdentityType')]", - "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" - }, - "properties": { - "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", - "configuration": { - "activeRevisionsMode": "[parameters('revisionMode')]", - "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", - "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", - "secrets": "[parameters('secrets')]", - "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", - "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" - }, - "template": { - "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", - "containers": [ - { - "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", - "name": "[parameters('containerName')]", - "env": "[parameters('env')]", - "resources": { - "cpu": "[json(parameters('containerCpuCoreCount'))]", - "memory": "[parameters('containerMemory')]" - } - } - ], - "scale": { - "minReplicas": "[parameters('containerMinReplicas')]", - "maxReplicas": "[parameters('containerMaxReplicas')]" - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" - ] - }, - { - "condition": "[variables('usePrivateRegistry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-registry-access', deployment().name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" - }, - "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" - }, - "description": "Assigns ACR Pull permissions to access an Azure Container Registry." - }, - "parameters": { - "containerRegistryName": { - "type": "string" - }, - "principalId": { - "type": "string" - } - }, - "variables": { - "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", - "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", - "properties": { - "roleDefinitionId": "[variables('acrPullRole')]", - "principalType": "ServicePrincipal", - "principalId": "[parameters('principalId')]" - } - } - ] - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" - }, - "identityPrincipalId": { - "type": "string", - "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" - }, - "imageName": { - "type": "string", - "value": "[parameters('imageName')]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "serviceBind": { - "type": "object", - "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" - }, - "uri": { - "type": "string", - "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" - } - } - } - } - } - ], - "outputs": { - "defaultDomain": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" - }, - "imageName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" - }, - "name": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" - }, - "uri": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]", - "[resourceId('Microsoft.Resources/deployments', 'api-keyvault-access')]" - ] - } - ], - "outputs": { - "SERVICE_API_IDENTITY_PRINCIPAL_ID": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" - }, - "SERVICE_API_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" - }, - "SERVICE_API_URI": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" - }, - "SERVICE_API_IMAGE_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", - "databaseName": { - "value": "[parameters('cosmosDatabaseName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" - } - }, - "parameters": { - "accountName": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "collections": { - "type": "array", - "defaultValue": [ - { - "name": "TodoList", - "id": "TodoList", - "shardKey": "Hash", - "indexKey": "_id" - }, - { - "name": "TodoItem", - "id": "TodoItem", - "shardKey": "Hash", - "indexKey": "_id" - } - ] - }, - "databaseName": { - "type": "string", - "defaultValue": "" - }, - "keyVaultName": { - "type": "string" - } - }, - "variables": { - "defaultDatabaseName": "Todo", - "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-mongo", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": { - "value": "[parameters('accountName')]" - }, - "databaseName": { - "value": "[variables('actualDatabaseName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "collections": { - "value": "[parameters('collections')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" - }, - "description": "Creates an Azure Cosmos DB for MongoDB account with a database." - }, - "parameters": { - "accountName": { - "type": "string" - }, - "databaseName": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "collections": { - "type": "array", - "defaultValue": [] - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - }, - "keyVaultName": { - "type": "string" - } - }, - "resources": [ - { - "copy": { - "name": "list", - "count": "[length(parameters('collections'))]" - }, - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", - "properties": { - "resource": { - "id": "[parameters('collections')[copyIndex()].id]", - "shardKey": { - "_id": "[parameters('collections')[copyIndex()].shardKey]" - }, - "indexes": [ - { - "key": { - "keys": [ - "[parameters('collections')[copyIndex()].indexKey]" - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" - ] - }, - { - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": { - "id": "[parameters('databaseName')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-mongo-account", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('accountName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" - }, - "description": "Creates an Azure Cosmos DB for MongoDB account." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "keyVaultName": { - "type": "string" - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-account", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "kind": { - "value": "MongoDB" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" - }, - "description": "Creates an Azure Cosmos DB account." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - }, - "keyVaultName": { - "type": "string" - }, - "kind": { - "type": "string", - "allowedValues": [ - "GlobalDocumentDB", - "MongoDB", - "Parse" - ] - } - }, - "resources": [ - { - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2022-08-15", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "consistencyPolicy": { - "defaultConsistencyLevel": "Session" - }, - "locations": [ - { - "locationName": "[parameters('location')]", - "failoverPriority": 0, - "isZoneRedundant": false - } - ], - "databaseAccountOfferType": "Standard", - "enableAutomaticFailover": false, - "enableMultipleWriteLocations": false, - "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", - "capabilities": [ - { - "name": "EnableServerless" - } - ] - } - }, - { - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", - "properties": { - "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" - }, - "dependsOn": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" - ] - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[parameters('connectionStringKey')]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" - }, - "id": { - "type": "string", - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" - }, - "id": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" - } - } - } - } - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[parameters('connectionStringKey')]" - }, - "databaseName": { - "type": "string", - "value": "[parameters('databaseName')]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" - } - } - } - } - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" - }, - "databaseName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "keyvault", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "principalId": { - "value": "[parameters('principalId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" - }, - "description": "Creates an Azure Key Vault." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "principalId": { - "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } } } ], "outputs": { - "endpoint": { + "defaultDomain": { "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" }, - "name": { + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { "type": "string", - "value": "[parameters('name')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" } } } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] }, { "type": "Microsoft.Resources/deployments", @@ -4253,360 +1882,9 @@ } } } - }, - { - "condition": "[parameters('useAPIM')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "apim-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('apimServiceName'))), createObject('value', parameters('apimServiceName')), createObject('value', format('{0}{1}', variables('abbrs').apiManagementService, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "applicationInsightsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3036576769636454145" - }, - "description": "Creates an Azure API Management instance." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "publisherEmail": { - "type": "string", - "defaultValue": "noreply@microsoft.com", - "minLength": 1, - "metadata": { - "description": "The email address of the owner of the service" - } - }, - "publisherName": { - "type": "string", - "defaultValue": "n/a", - "minLength": 1, - "metadata": { - "description": "The name of the owner of the service" - } - }, - "sku": { - "type": "string", - "defaultValue": "Consumption", - "allowedValues": [ - "Consumption", - "Developer", - "Standard", - "Premium" - ], - "metadata": { - "description": "The pricing tier of this API Management service" - } - }, - "skuCount": { - "type": "int", - "defaultValue": 0, - "allowedValues": [ - 0, - 1, - 2 - ], - "metadata": { - "description": "The instance size of this API Management service." - } - }, - "applicationInsightsName": { - "type": "string", - "metadata": { - "description": "Azure Application Insights Name" - } - } - }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2021-08-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[union(parameters('tags'), createObject('azd-service-name', parameters('name')))]", - "sku": { - "name": "[parameters('sku')]", - "capacity": "[if(equals(parameters('sku'), 'Consumption'), 0, if(equals(parameters('sku'), 'Developer'), 1, parameters('skuCount')))]" - }, - "properties": { - "publisherEmail": "[parameters('publisherEmail')]", - "publisherName": "[parameters('publisherName')]", - "customProperties": "[if(equals(parameters('sku'), 'Consumption'), createObject(), createObject('Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30', 'false'))]" - } - }, - { - "condition": "[not(empty(parameters('applicationInsightsName')))]", - "type": "Microsoft.ApiManagement/service/loggers", - "apiVersion": "2021-12-01-preview", - "name": "[format('{0}/{1}', parameters('name'), 'app-insights-logger')]", - "properties": { - "credentials": { - "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" - }, - "description": "Logger to Azure Application Insights", - "isBuffered": false, - "loggerType": "applicationInsights", - "resourceId": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" - }, - "dependsOn": [ - "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" - ] - } - ], - "outputs": { - "apimServiceName": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] - }, - { - "condition": "[parameters('useAPIM')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "apim-api-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(parameters('useAPIM'), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'apim-deployment'), '2022-09-01').outputs.apimServiceName.value), createObject('value', ''))]", - "apiName": { - "value": "todo-api" - }, - "apiDisplayName": { - "value": "Simple Todo API" - }, - "apiDescription": { - "value": "This is a simple Todo API" - }, - "apiPath": { - "value": "todo" - }, - "webFrontendUrl": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" - }, - "apiBackendUrl": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6615097664318461925" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "apiName": { - "type": "string", - "minLength": 1, - "metadata": { - "description": "Resource name to uniquely identify this API within the API Management service instance" - } - }, - "apiDisplayName": { - "type": "string", - "minLength": 1, - "maxLength": 300, - "metadata": { - "description": "The Display Name of the API" - } - }, - "apiDescription": { - "type": "string", - "minLength": 1, - "metadata": { - "description": "Description of the API. May include HTML formatting tags." - } - }, - "apiPath": { - "type": "string", - "minLength": 1, - "metadata": { - "description": "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." - } - }, - "webFrontendUrl": { - "type": "string", - "metadata": { - "description": "Absolute URL of the web frontend" - } - }, - "apiBackendUrl": { - "type": "string", - "metadata": { - "description": "Absolute URL of the backend service implementing this API." - } - }, - "apiAppName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Resource name for backend Web App or Function App" - } - } - }, - "variables": { - "$fxv#0": "\n\n \n \n \n \n \n {origin}\n \n \n PUT\n GET\n POST\n DELETE\n PATCH\n \n \n
    *
    \n
    \n \n
    *
    \n
    \n
    \n \n \n \n \n \n \n Call to the @(context.Api.Name)\n \n \n \n \n \n
    \n \n \n \n \n \n \n \n \n \n \n \n = 200 && context.Response.StatusCode < 300)\">\n \n \n \n \n \n \n \n = 400 && context.Response.StatusCode < 600)\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Failed to process the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n An unexpected error has occurred.\n \n \n
    \n", - "$fxv#1": "openapi: 3.0.0\ninfo:\n description: Simple Todo API\n version: 3.0.0\n title: Simple Todo API\n contact:\n email: azdevteam@microsoft.com\n\ncomponents:\n schemas:\n TodoItem:\n type: object\n required:\n - listId\n - name\n - description\n description: A task that needs to be completed\n properties:\n id:\n type: string\n listId:\n type: string\n name:\n type: string\n description:\n type: string\n state:\n $ref: \"#/components/schemas/TodoState\"\n dueDate:\n type: string\n format: date-time\n completedDate:\n type: string\n format: date-time\n TodoList:\n type: object\n required:\n - name\n properties:\n id:\n type: string\n name:\n type: string\n description:\n type: string\n description: \" A list of related Todo items\"\n TodoState:\n type: string\n enum:\n - todo\n - inprogress\n - done\n parameters:\n listId:\n in: path\n required: true\n name: listId\n description: The Todo list unique identifier\n schema:\n type: string\n itemId:\n in: path\n required: true\n name: itemId\n description: The Todo item unique identifier\n schema:\n type: string\n state:\n in: path\n required: true\n name: state\n description: The Todo item state\n schema:\n $ref: \"#/components/schemas/TodoState\"\n top:\n in: query\n required: false\n name: top\n description: The max number of items to returns in a result\n schema:\n type: number\n default: 20\n skip:\n in: query\n required: false\n name: skip\n description: The number of items to skip within the results\n schema:\n type: number\n default: 0\n\n requestBodies:\n TodoList:\n description: The Todo List\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: The Todo Item\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n\n responses:\n TodoList:\n description: A Todo list result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoListArray:\n description: An array of Todo lists\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: A Todo item result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n TodoItemArray:\n description: An array of Todo items\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoItem\"\n\npaths:\n /lists:\n get:\n operationId: GetLists\n summary: Gets an array of Todo lists\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoListArray\"\n post:\n operationId: CreateList\n summary: Creates a new Todo list\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoList\"\n 400:\n description: Invalid request schema\n /lists/{listId}:\n get:\n operationId: GetListById\n summary: Gets a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n put:\n operationId: UpdateListById\n summary: Updates a Todo list by unique identifier\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n 400:\n description: Todo list is invalid\n delete:\n operationId: DeleteListById\n summary: Deletes a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 204:\n description: Todo list deleted successfully\n 404:\n description: Todo list not found\n /lists/{listId}/items:\n post:\n operationId: CreateItem\n summary: Creates a new Todo item within a list\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list not found\n get:\n operationId: GetItemsByListId\n summary: Gets Todo items within the specified list\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list not found\n /lists/{listId}/items/{itemId}:\n get:\n operationId: GetItemById\n summary: Gets a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemById\n summary: Updates a Todo item by unique identifier\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 400:\n description: Todo item is invalid\n 404:\n description: Todo list or item not found\n delete:\n operationId: DeleteItemById\n summary: Deletes a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 204:\n description: Todo item deleted successfully\n 404:\n description: Todo list or item not found\n /lists/{listId}/items/state/{state}:\n get:\n operationId: GetItemsByListIdAndState\n summary: Gets a list of Todo items of a specific state\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemsStateByListId\n summary: Changes the state of the specified list items\n tags:\n - Items\n requestBody:\n description: unique identifiers of the Todo items to update\n content:\n application/json:\n schema:\n type: array\n items:\n description: The Todo item unique identifier\n type: string\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n responses:\n 204:\n description: Todo items updated\n 400:\n description: Update request is invalid\n", - "apiPolicyContent": "[replace(variables('$fxv#0'), '{origin}', parameters('webFrontendUrl'))]", - "appNameForBicep": "[if(not(empty(parameters('apiAppName'))), parameters('apiAppName'), 'placeholderName')]" - }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/apis", - "apiVersion": "2021-12-01-preview", - "name": "[format('{0}/{1}', parameters('name'), parameters('apiName'))]", - "properties": { - "description": "[parameters('apiDescription')]", - "displayName": "[parameters('apiDisplayName')]", - "path": "[parameters('apiPath')]", - "protocols": [ - "https" - ], - "subscriptionRequired": false, - "type": "http", - "format": "openapi", - "serviceUrl": "[parameters('apiBackendUrl')]", - "value": "[variables('$fxv#1')]" - } - }, - { - "type": "Microsoft.ApiManagement/service/apis/policies", - "apiVersion": "2021-12-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'policy')]", - "properties": { - "format": "rawxml", - "value": "[variables('apiPolicyContent')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" - ] - }, - { - "type": "Microsoft.ApiManagement/service/apis/diagnostics", - "apiVersion": "2021-12-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'applicationinsights')]", - "properties": { - "alwaysLog": "allErrors", - "backend": { - "request": { - "body": { - "bytes": 1024 - } - }, - "response": { - "body": { - "bytes": 1024 - } - } - }, - "frontend": { - "request": { - "body": { - "bytes": 1024 - } - }, - "response": { - "body": { - "bytes": 1024 - } - } - }, - "httpCorrelationProtocol": "W3C", - "logClientIp": true, - "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('name'), 'app-insights-logger')]", - "metrics": true, - "sampling": { - "percentage": 100, - "samplingType": "fixed" - }, - "verbosity": "verbose" - }, - "dependsOn": [ - "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" - ] - }, - { - "condition": "[not(empty(parameters('apiAppName')))]", - "type": "Microsoft.Web/sites/config", - "apiVersion": "2022-03-01", - "name": "[format('{0}/web', variables('appNameForBicep'))]", - "kind": "string", - "properties": { - "apiManagementConfig": { - "id": "[format('{0}/apis/{1}', resourceId('Microsoft.ApiManagement/service', parameters('name')), parameters('apiName'))]" - } - } - } - ], - "outputs": { - "SERVICE_API_URI": { - "type": "string", - "value": "[format('{0}/{1}', reference(resourceId('Microsoft.ApiManagement/service', parameters('name')), '2021-08-01').gatewayUrl, parameters('apiPath'))]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'api')]", - "[resourceId('Microsoft.Resources/deployments', 'apim-deployment')]", - "[resourceId('Microsoft.Resources/deployments', 'web')]" - ] } ], "outputs": { - "AZURE_COSMOS_CONNECTION_STRING_KEY": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" - }, - "AZURE_COSMOS_DATABASE_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" - }, "API_CORS_ACA_URL": { "type": "string", "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" @@ -4623,22 +1901,6 @@ "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" }, - "AZURE_CONTAINER_REGISTRY_ENDPOINT": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryLoginServer.value]" - }, - "AZURE_CONTAINER_REGISTRY_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" - }, - "AZURE_KEY_VAULT_ENDPOINT": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" - }, - "AZURE_KEY_VAULT_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, "AZURE_LOCATION": { "type": "string", "value": "[parameters('location')]" @@ -4647,33 +1909,9 @@ "type": "string", "value": "[tenant().tenantId]" }, - "REACT_APP_API_BASE_URL": { - "type": "string", - "value": "[if(parameters('useAPIM'), reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" - }, "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" - }, - "REACT_APP_WEB_BASE_URL": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" - }, - "SERVICE_API_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_NAME.value]" - }, - "SERVICE_WEB_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_NAME.value]" - }, - "USE_APIM": { - "type": "bool", - "value": "[parameters('useAPIM')]" - }, - "SERVICE_API_ENDPOINTS": { - "type": "array", - "value": "[if(parameters('useAPIM'), createArray(reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value), createArray())]" } } } \ No newline at end of file From dbf57f0d82e82aa92787c507cc2c1c5d6e0b6163 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 1 Mar 2024 10:57:02 +0800 Subject: [PATCH 065/112] change ade template name for ACA individual resource --- Environments/Todo-Mongo-ACA/manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/Todo-Mongo-ACA/manifest.yaml b/Environments/Todo-Mongo-ACA/manifest.yaml index d2b9ebb1..bce88e22 100644 --- a/Environments/Todo-Mongo-ACA/manifest.yaml +++ b/Environments/Todo-Mongo-ACA/manifest.yaml @@ -1,4 +1,4 @@ -name: Todo-Nodejs-Mongo-ACA +name: Todo-Mongo-ACA version: 1.0.0 summary: Todo Nodejs Mongo ACA description: Deploys a todo app with Nodejs and Mongo From 3efa497916b266f96b920706affab9de19c14865 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 1 Mar 2024 11:40:46 +0800 Subject: [PATCH 066/112] update container registry name --- Environments/Todo-Mongo-ACA/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/Todo-Mongo-ACA/main.bicep b/Environments/Todo-Mongo-ACA/main.bicep index bf0b94cb..99ce83c7 100644 --- a/Environments/Todo-Mongo-ACA/main.bicep +++ b/Environments/Todo-Mongo-ACA/main.bicep @@ -80,7 +80,7 @@ module api './app/api.bicep' = { module containerRegistry './core/host/container-registry.bicep' = { name: 'app-container-registry' params: { - name: containerRegistryName + name: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' location: location adminUserEnabled: containerRegistryAdminUserEnabled tags: tags From 86e9d2615fb105071654585f7ea94a5f7dd0460e Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 1 Mar 2024 03:42:28 +0000 Subject: [PATCH 067/112] Rebuild ARM templates --- Environments/Todo-Mongo-ACA/azuredeploy.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index d4d8456a..c7957e74 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "7624760505230248688" + "templateHash": "14453627926966060746" } }, "parameters": { @@ -1920,9 +1920,7 @@ }, "mode": "Incremental", "parameters": { - "name": { - "value": "[parameters('containerRegistryName')]" - }, + "name": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", "location": { "value": "[parameters('location')]" }, From c5c4f09e9e0c1773b2e3a5a5cb59473357a6dbc3 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 1 Mar 2024 13:14:21 +0800 Subject: [PATCH 068/112] put keyvault fro shared aks to mongo part --- Environments/Todo-Mongo-AKS/app/db.bicep | 2 -- .../core/database/cosmos/cosmos-account.bicep | 17 +++++----- .../cosmos/cosmos-connection-string.bicep | 15 --------- .../cosmos/mongo/cosmos-mongo-account.bicep | 2 -- .../cosmos/mongo/cosmos-mongo-db.bicep | 2 -- .../security/aks-managed-cluster-access.bicep | 19 ++++++++++++ .../core/security/keyvault-access.bicep | 22 +++++++++++++ .../core/security/keyvault-secret.bicep | 31 +++++++++++++++++++ .../core/security/keyvault.bicep | 26 ++++++++++++++++ .../core/security/registry-access.bicep | 19 ++++++++++++ .../Todo-Mongo-AKS/core/security/role.bicep | 21 +++++++++++++ Environments/Todo-Mongo-AKS/main.bicep | 29 ++++++++++++++--- .../Todo-Shared-AKS/core/host/aks.bicep | 12 ------- Environments/Todo-Shared-AKS/main.bicep | 18 ----------- 14 files changed, 172 insertions(+), 63 deletions(-) delete mode 100644 Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep create mode 100644 Environments/Todo-Mongo-AKS/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Todo-Mongo-AKS/core/security/keyvault-access.bicep create mode 100644 Environments/Todo-Mongo-AKS/core/security/keyvault-secret.bicep create mode 100644 Environments/Todo-Mongo-AKS/core/security/keyvault.bicep create mode 100644 Environments/Todo-Mongo-AKS/core/security/registry-access.bicep create mode 100644 Environments/Todo-Mongo-AKS/core/security/role.bicep diff --git a/Environments/Todo-Mongo-AKS/app/db.bicep b/Environments/Todo-Mongo-AKS/app/db.bicep index 8d828333..938949c6 100644 --- a/Environments/Todo-Mongo-AKS/app/db.bicep +++ b/Environments/Todo-Mongo-AKS/app/db.bicep @@ -18,7 +18,6 @@ param collections array = [ ] param databaseName string = '' param keyVaultName string -param keyVaultResourceGroupName string // Because databaseName is optional in main.bicep, we make sure the database name is set here. var defaultDatabaseName = 'Todo' @@ -32,7 +31,6 @@ module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { location: location collections: collections keyVaultName: keyVaultName - keyVaultResourceGroupName: keyVaultResourceGroupName tags: tags } } diff --git a/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep index 3832b3e4..6f8747f5 100644 --- a/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep @@ -5,7 +5,6 @@ param tags object = {} param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' param keyVaultName string -param keyVaultResourceGroupName string @allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) param kind string @@ -32,16 +31,18 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { } } -module cosmosConnectionStringModule './cosmos-connection-string.bicep' = { - name: 'cosmosConnectionStringModule' - scope: resourceGroup(keyVaultResourceGroupName) - params: { - keyVaultName: keyVaultName - connectionStringKey: connectionStringKey - connectionString: cosmos.listConnectionStrings().connectionStrings[0].connectionString +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString } } +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + output connectionStringKey string = connectionStringKey output endpoint string = cosmos.properties.documentEndpoint output id string = cosmos.id diff --git a/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep deleted file mode 100644 index 5a96f353..00000000 --- a/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-connection-string.bicep +++ /dev/null @@ -1,15 +0,0 @@ -param keyVaultName string -param connectionStringKey string -param connectionString string - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - -resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: connectionStringKey - properties: { - value: connectionString - } -} diff --git a/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep index 665e6a5c..4aafbf38 100644 --- a/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep +++ b/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -4,7 +4,6 @@ param location string = resourceGroup().location param tags object = {} param keyVaultName string -param keyVaultResourceGroupName string param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' module cosmos '../../cosmos/cosmos-account.bicep' = { @@ -14,7 +13,6 @@ module cosmos '../../cosmos/cosmos-account.bicep' = { location: location connectionStringKey: connectionStringKey keyVaultName: keyVaultName - keyVaultResourceGroupName: keyVaultResourceGroupName kind: 'MongoDB' tags: tags } diff --git a/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep index dd7d0aa3..2a670578 100644 --- a/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep +++ b/Environments/Todo-Mongo-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -7,7 +7,6 @@ param tags object = {} param collections array = [] param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' param keyVaultName string -param keyVaultResourceGroupName string module cosmos 'cosmos-mongo-account.bicep' = { name: 'cosmos-mongo-account' @@ -15,7 +14,6 @@ module cosmos 'cosmos-mongo-account.bicep' = { name: accountName location: location keyVaultName: keyVaultName - keyVaultResourceGroupName: keyVaultResourceGroupName tags: tags connectionStringKey: connectionStringKey } diff --git a/Environments/Todo-Mongo-AKS/core/security/aks-managed-cluster-access.bicep b/Environments/Todo-Mongo-AKS/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Todo-Mongo-AKS/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Todo-Mongo-AKS/core/security/keyvault-access.bicep b/Environments/Todo-Mongo-AKS/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Todo-Mongo-AKS/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Mongo-AKS/core/security/keyvault-secret.bicep b/Environments/Todo-Mongo-AKS/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Todo-Mongo-AKS/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Mongo-AKS/core/security/keyvault.bicep b/Environments/Todo-Mongo-AKS/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Todo-Mongo-AKS/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Todo-Mongo-AKS/core/security/registry-access.bicep b/Environments/Todo-Mongo-AKS/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Todo-Mongo-AKS/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Todo-Mongo-AKS/core/security/role.bicep b/Environments/Todo-Mongo-AKS/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Todo-Mongo-AKS/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index c70b89ac..5720b3b3 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -9,8 +9,9 @@ param location string = resourceGroup().location param cosmosAccountName string = '' param cosmosDatabaseName string = '' -param keyvaultName string = '' -param keyVaultResourceGroupName string = resourceGroup().name +param keyVaultName string = '' +param principalId string = '' +param aksClusterIdentityObjectId string var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) @@ -24,8 +25,26 @@ module cosmos './app/db.bicep' = { databaseName: cosmosDatabaseName location: location tags: tags - keyVaultName: keyvaultName - keyVaultResourceGroupName: keyVaultResourceGroupName + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +module clusterKeyVaultAccess './core/security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: aksClusterIdentityObjectId } } @@ -36,3 +55,5 @@ output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName // App outputs output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name diff --git a/Environments/Todo-Shared-AKS/core/host/aks.bicep b/Environments/Todo-Shared-AKS/core/host/aks.bicep index 536a534b..4a36262c 100644 --- a/Environments/Todo-Shared-AKS/core/host/aks.bicep +++ b/Environments/Todo-Shared-AKS/core/host/aks.bicep @@ -8,9 +8,6 @@ param containerRegistryName string @description('The name of the connected log analytics workspace') param logAnalyticsName string = '' -@description('The name of the keyvault to grant access') -param keyVaultName string - @description('The Azure region/location for the AKS resources') param location string = resourceGroup().location @@ -212,15 +209,6 @@ module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enable } } -// Give the AKS Cluster access to KeyVault -module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { - name: 'cluster-keyvault-access' - params: { - keyVaultName: keyVaultName - principalId: managedCluster.outputs.clusterIdentity.objectId - } -} - // Helpers for node pool configuration var nodePoolBase = { osType: 'Linux' diff --git a/Environments/Todo-Shared-AKS/main.bicep b/Environments/Todo-Shared-AKS/main.bicep index bb7d4593..c283ef31 100644 --- a/Environments/Todo-Shared-AKS/main.bicep +++ b/Environments/Todo-Shared-AKS/main.bicep @@ -15,12 +15,8 @@ param containerRegistryName string = '' param applicationInsightsDashboardName string = '' param applicationInsightsName string = '' -param keyVaultName string = '' param logAnalyticsName string = '' -@description('Id of the user or app to assign application roles') -param principalId string = '' - var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } @@ -33,18 +29,6 @@ module aks './core/host/aks.bicep' = { name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName - keyVaultName: keyVault.outputs.name - } -} - -// Store secrets in a keyvault -module keyVault './core/security/keyvault.bicep' = { - name: 'keyvault' - params: { - name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' - location: location - tags: tags - principalId: principalId } } @@ -62,8 +46,6 @@ module monitoring './core/monitor/monitoring.bicep' = { // App outputs output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString -output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint -output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName From 9c5faabb00a57fa13b118bed5561664514e219e1 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 1 Mar 2024 05:15:59 +0000 Subject: [PATCH 069/112] Rebuild ARM templates --- Environments/Todo-Mongo-AKS/azuredeploy.json | 258 ++++++++++++------ Environments/Todo-Shared-AKS/azuredeploy.json | 183 +------------ 2 files changed, 176 insertions(+), 265 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index dfe17bd2..e800bedc 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "11686091938214168014" + "templateHash": "10034134246524106464" } }, "parameters": { @@ -33,13 +33,16 @@ "type": "string", "defaultValue": "" }, - "keyvaultName": { + "keyVaultName": { "type": "string", "defaultValue": "" }, - "keyVaultResourceGroupName": { + "principalId": { "type": "string", - "defaultValue": "[resourceGroup().name]" + "defaultValue": "" + }, + "aksClusterIdentityObjectId": { + "type": "string" } }, "variables": { @@ -207,10 +210,7 @@ "value": "[variables('tags')]" }, "keyVaultName": { - "value": "[parameters('keyvaultName')]" - }, - "keyVaultResourceGroupName": { - "value": "[parameters('keyVaultResourceGroupName')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" } }, "template": { @@ -220,7 +220,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "850519296196313996" + "templateHash": "5730728686647632614" } }, "parameters": { @@ -258,9 +258,6 @@ }, "keyVaultName": { "type": "string" - }, - "keyVaultResourceGroupName": { - "type": "string" } }, "variables": { @@ -293,9 +290,6 @@ "keyVaultName": { "value": "[parameters('keyVaultName')]" }, - "keyVaultResourceGroupName": { - "value": "[parameters('keyVaultResourceGroupName')]" - }, "tags": { "value": "[parameters('tags')]" } @@ -307,7 +301,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "13509848683723089892" + "templateHash": "14549161001187918251" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -336,9 +330,6 @@ }, "keyVaultName": { "type": "string" - }, - "keyVaultResourceGroupName": { - "type": "string" } }, "resources": [ @@ -404,9 +395,6 @@ "keyVaultName": { "value": "[parameters('keyVaultName')]" }, - "keyVaultResourceGroupName": { - "value": "[parameters('keyVaultResourceGroupName')]" - }, "tags": { "value": "[parameters('tags')]" }, @@ -421,7 +409,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "6834223066721695440" + "templateHash": "8317058180807592714" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -440,9 +428,6 @@ "keyVaultName": { "type": "string" }, - "keyVaultResourceGroupName": { - "type": "string" - }, "connectionStringKey": { "type": "string", "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" @@ -471,9 +456,6 @@ "keyVaultName": { "value": "[parameters('keyVaultName')]" }, - "keyVaultResourceGroupName": { - "value": "[parameters('keyVaultResourceGroupName')]" - }, "kind": { "value": "MongoDB" }, @@ -488,7 +470,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "263326484500044406" + "templateHash": "13614361263700788271" }, "description": "Creates an Azure Cosmos DB account." }, @@ -511,9 +493,6 @@ "keyVaultName": { "type": "string" }, - "keyVaultResourceGroupName": { - "type": "string" - }, "kind": { "type": "string", "allowedValues": [ @@ -554,58 +533,11 @@ } }, { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmosConnectionStringModule", - "resourceGroup": "[parameters('keyVaultResourceGroupName')]", + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - }, - "connectionString": { - "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16039028337730776547" - } - }, - "parameters": { - "keyVaultName": { - "type": "string" - }, - "connectionStringKey": { - "type": "string" - }, - "connectionString": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", - "properties": { - "value": "[parameters('connectionString')]" - } - } - ] - } + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" }, "dependsOn": [ "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" @@ -685,6 +617,156 @@ } } } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[parameters('aksClusterIdentityObjectId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } } } ], @@ -704,6 +786,14 @@ "AZURE_TENANT_ID": { "type": "string", "value": "[tenant().tenantId]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" } } } \ No newline at end of file diff --git a/Environments/Todo-Shared-AKS/azuredeploy.json b/Environments/Todo-Shared-AKS/azuredeploy.json index 8277c2be..0b774988 100644 --- a/Environments/Todo-Shared-AKS/azuredeploy.json +++ b/Environments/Todo-Shared-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "9109697828467853480" + "templateHash": "11426766062419716265" } }, "parameters": { @@ -47,20 +47,9 @@ "type": "string", "defaultValue": "" }, - "keyVaultName": { - "type": "string", - "defaultValue": "" - }, "logAnalyticsName": { "type": "string", "defaultValue": "" - }, - "principalId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Id of the user or app to assign application roles" - } } }, "variables": { @@ -224,9 +213,6 @@ "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", "logAnalyticsName": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" - }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" } }, "template": { @@ -236,7 +222,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "4109929557657560885" + "templateHash": "13278918148041449332" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -260,12 +246,6 @@ "description": "The name of the connected log analytics workspace" } }, - "keyVaultName": { - "type": "string", - "metadata": { - "description": "The name of the keyvault to grant access" - } - }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", @@ -1194,77 +1174,6 @@ "dependsOn": [ "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cluster-keyvault-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "principalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" - }, - "description": "Assigns an Azure Key Vault access policy." - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "add" - }, - "keyVaultName": { - "type": "string" - }, - "permissions": { - "type": "object", - "defaultValue": { - "secrets": [ - "get", - "list" - ] - } - }, - "principalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "properties": { - "accessPolicies": [ - { - "objectId": "[parameters('principalId')]", - "tenantId": "[subscription().tenantId]", - "permissions": "[parameters('permissions')]" - } - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" - ] } ], "outputs": { @@ -1300,89 +1209,9 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" ] }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "keyvault", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "principalId": { - "value": "[parameters('principalId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" - }, - "description": "Creates an Azure Key Vault." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "principalId": { - "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" - } - } - ], - "outputs": { - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -2896,14 +2725,6 @@ "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" }, - "AZURE_KEY_VAULT_ENDPOINT": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" - }, - "AZURE_KEY_VAULT_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, "AZURE_LOCATION": { "type": "string", "value": "[parameters('location')]" From 9418e9754693a7e565e8567074ebbb6896229b92 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 1 Mar 2024 14:19:48 +0800 Subject: [PATCH 070/112] no longer need principal id --- Environments/Todo-Shared-AKS/manifest.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Environments/Todo-Shared-AKS/manifest.yaml b/Environments/Todo-Shared-AKS/manifest.yaml index b7fa8d99..b7ea48bd 100644 --- a/Environments/Todo-Shared-AKS/manifest.yaml +++ b/Environments/Todo-Shared-AKS/manifest.yaml @@ -18,11 +18,4 @@ parameters: type: string required: true -- id: "principalId" - name: "principalId (e.g. )" - description: "principal id that have the permission (get list) to access key vault" - type: string - required: false - default: '' - From ae03d532ad65e898da1476680ba6bf2e63759e58 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 1 Mar 2024 14:54:51 +0800 Subject: [PATCH 071/112] update aks cluster identity object id --- Environments/Todo-Mongo-AKS/manifest.yaml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/manifest.yaml b/Environments/Todo-Mongo-AKS/manifest.yaml index c78120e0..06485057 100644 --- a/Environments/Todo-Mongo-AKS/manifest.yaml +++ b/Environments/Todo-Mongo-AKS/manifest.yaml @@ -18,14 +18,8 @@ parameters: type: string required: true -- id: "keyvaultName" - name: "keyvaultName (e.g. kv-abc123)" - description: "keyvault name that store the secret for mongo connection string" +- id: "aksClusterIdentityObjectId" + name: "AKS Cluster Identity Object Id" + description: "Object Id of the identity used by the AKS cluster to access the KeyVault" type: string required: true - -- id: "keyvaultResourceGroupName" - name: "keyvaultResourceGroupName (e.g. rg-abc123)" - description: "keyvault group name contains the key vault above" - type: string - required: true \ No newline at end of file From 0aefa4fc5494949793b329b0b2a75e4e9557fa10 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 1 Mar 2024 15:29:53 +0800 Subject: [PATCH 072/112] fix error --- Environments/Todo-Mongo-AKS/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index 5720b3b3..378c06a8 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -43,7 +43,7 @@ module keyVault './core/security/keyvault.bicep' = { module clusterKeyVaultAccess './core/security/keyvault-access.bicep' = { name: 'cluster-keyvault-access' params: { - keyVaultName: keyVaultName + keyVaultName: keyVault.outputs.name principalId: aksClusterIdentityObjectId } } From 6f7cf58e28bdc59ccdbaad50f6af6e28bc726229 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 1 Mar 2024 07:31:32 +0000 Subject: [PATCH 073/112] Rebuild ARM templates --- Environments/Todo-Mongo-AKS/azuredeploy.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index e800bedc..84d2235d 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "10034134246524106464" + "templateHash": "2845459636497042502" } }, "parameters": { @@ -712,7 +712,7 @@ "mode": "Incremental", "parameters": { "keyVaultName": { - "value": "[parameters('keyVaultName')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" }, "principalId": { "value": "[parameters('aksClusterIdentityObjectId')]" @@ -767,7 +767,10 @@ } ] } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] } ], "outputs": { From 53b7688e19bc8971157b24f1d6831934b41bb063 Mon Sep 17 00:00:00 2001 From: Mark Weitzel Date: Sun, 3 Mar 2024 22:32:40 -0500 Subject: [PATCH 074/112] updated environment definitions --- .../App-Base-WebApp-ACA/abbreviations.json | 136 + .../App-Base-WebApp-ACA/app/api.bicep | 75 + .../app/apim-api-policy.xml | 92 + .../App-Base-WebApp-ACA/app/apim-api.bicep | 120 + Environments/App-Base-WebApp-ACA/app/db.bicep | 40 + .../App-Base-WebApp-ACA/app/openapi.yaml | 312 ++ .../App-Base-WebApp-ACA/app/web.bicep | 54 + .../App-Base-WebApp-ACA/azuredeploy.json | 4679 +++++++++++++++++ .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 + .../core/gateway/apim.bicep | 79 + .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 + .../App-Base-WebApp-ACA/core/host/aks.bicep | 280 + .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 + .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 + .../core/host/container-app.bicep | 162 + .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 + .../core/host/functions.bicep | 86 + .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + Environments/App-Base-WebApp-ACA/main.bicep | 178 + .../App-Base-WebApp-ACA/main.parameters.json | 27 + .../App-Base-WebApp-ACA/manifest.yaml | 19 + .../App-Base-WebApp-AKS/abbreviations.json | 136 + Environments/App-Base-WebApp-AKS/app/db.bicep | 40 + .../App-Base-WebApp-AKS/azuredeploy.json | 3384 ++++++++++++ .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 + .../core/gateway/apim.bicep | 79 + .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 + .../App-Base-WebApp-AKS/core/host/aks.bicep | 280 + .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 + .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 + .../core/host/container-app.bicep | 162 + .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 + .../core/host/functions.bicep | 86 + .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + Environments/App-Base-WebApp-AKS/main.bicep | 91 + .../App-Base-WebApp-AKS/main.parameters.json | 15 + .../App-Base-WebApp-AKS/manifest.yaml | 28 + .../abbreviations.json | 136 + .../Contoso-Base-Shared-ACA-Dev/app/api.bicep | 75 + .../app/apim-api-policy.xml | 92 + .../app/apim-api.bicep | 120 + .../Contoso-Base-Shared-ACA-Dev/app/db.bicep | 40 + .../app/openapi.yaml | 312 ++ .../Contoso-Base-Shared-ACA-Dev/app/web.bicep | 54 + .../azuredeploy.json | 1917 +++++++ .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 23 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../Contoso-Base-Shared-ACA-Dev/main.bicep | 59 + .../main.parameters.json | 12 + .../Contoso-Base-Shared-ACA-Dev/manifest.yaml | 19 + .../abbreviations.json | 136 + .../app/api.bicep | 75 + .../app/apim-api-policy.xml | 92 + .../app/apim-api.bicep | 120 + .../Contoso-Base-Shared-ACA-Prod/app/db.bicep | 40 + .../app/openapi.yaml | 312 ++ .../app/web.bicep | 54 + .../azuredeploy.json | 1917 +++++++ .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 23 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../Contoso-Base-Shared-ACA-Prod/main.bicep | 59 + .../main.parameters.json | 12 + .../manifest.yaml | 19 + .../abbreviations.json | 136 + .../app/api.bicep | 75 + .../app/apim-api-policy.xml | 92 + .../app/apim-api.bicep | 120 + .../Contoso-Base-Shared-ACA-Test/app/db.bicep | 40 + .../app/openapi.yaml | 312 ++ .../app/web.bicep | 54 + .../azuredeploy.json | 1917 +++++++ .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 23 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../Contoso-Base-Shared-ACA-Test/main.bicep | 59 + .../main.parameters.json | 12 + .../manifest.yaml | 19 + .../abbreviations.json | 136 + .../Contoso-Base-Shared-AKS-Dev/app/db.bicep | 40 + .../azuredeploy.json | 2757 ++++++++++ .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 + .../core/gateway/apim.bicep | 79 + .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 + .../core/host/aks.bicep | 268 + .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 + .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 + .../core/host/container-app.bicep | 162 + .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 + .../core/host/functions.bicep | 86 + .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + .../Contoso-Base-Shared-AKS-Dev/main.bicep | 55 + .../main.parameters.json | 15 + .../Contoso-Base-Shared-AKS-Dev/manifest.yaml | 21 + .../abbreviations.json | 136 + .../Contoso-Base-Shared-AKS-Prod/app/db.bicep | 40 + .../azuredeploy.json | 2757 ++++++++++ .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 + .../core/gateway/apim.bicep | 79 + .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 + .../core/host/aks.bicep | 268 + .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 + .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 + .../core/host/container-app.bicep | 162 + .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 + .../core/host/functions.bicep | 86 + .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + .../Contoso-Base-Shared-AKS-Prod/main.bicep | 55 + .../main.parameters.json | 15 + .../manifest.yaml | 21 + .../abbreviations.json | 136 + .../Contoso-Base-Shared-AKS-Test/app/db.bicep | 40 + .../azuredeploy.json | 2757 ++++++++++ .../core/ai/cognitiveservices.bicep | 53 + .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 + .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../database/postgresql/flexibleserver.bicep | 65 + .../core/database/sqlserver/sqlserver.bicep | 130 + .../core/gateway/apim.bicep | 79 + .../core/host/aks-agent-pool.bicep | 18 + .../core/host/aks-managed-cluster.bicep | 140 + .../core/host/aks.bicep | 268 + .../core/host/appservice-appsettings.bicep | 17 + .../core/host/appservice.bicep | 123 + .../core/host/appserviceplan.bicep | 22 + .../core/host/container-app-upsert.bicep | 105 + .../core/host/container-app.bicep | 162 + .../host/container-apps-environment.bicep | 41 + .../core/host/container-apps.bicep | 40 + .../core/host/container-registry.bicep | 83 + .../core/host/functions.bicep | 86 + .../core/host/staticwebapp.bicep | 22 + .../applicationinsights-dashboard.bicep | 1236 +++++ .../core/monitor/applicationinsights.bicep | 30 + .../core/monitor/loganalytics.bicep | 22 + .../core/monitor/monitoring.bicep | 32 + .../core/networking/cdn-endpoint.bicep | 52 + .../core/networking/cdn-profile.bicep | 34 + .../core/networking/cdn.bicep | 42 + .../core/search/search-services.bicep | 68 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../core/storage/storage-account.bicep | 64 + .../core/testing/loadtesting.bicep | 15 + .../Contoso-Base-Shared-AKS-Test/main.bicep | 55 + .../main.parameters.json | 15 + .../manifest.yaml | 21 + 286 files changed, 48437 insertions(+) create mode 100644 Environments/App-Base-WebApp-ACA/abbreviations.json create mode 100644 Environments/App-Base-WebApp-ACA/app/api.bicep create mode 100644 Environments/App-Base-WebApp-ACA/app/apim-api-policy.xml create mode 100644 Environments/App-Base-WebApp-ACA/app/apim-api.bicep create mode 100644 Environments/App-Base-WebApp-ACA/app/db.bicep create mode 100644 Environments/App-Base-WebApp-ACA/app/openapi.yaml create mode 100644 Environments/App-Base-WebApp-ACA/app/web.bicep create mode 100644 Environments/App-Base-WebApp-ACA/azuredeploy.json create mode 100644 Environments/App-Base-WebApp-ACA/core/ai/cognitiveservices.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/gateway/apim.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/aks-agent-pool.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/aks-managed-cluster.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/aks.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/appservice-appsettings.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/appservice.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/appserviceplan.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/container-app-upsert.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/container-app.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/container-apps-environment.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/container-apps.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/container-registry.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/functions.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/host/staticwebapp.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/monitor/loganalytics.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/monitor/monitoring.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/networking/cdn-endpoint.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/networking/cdn-profile.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/networking/cdn.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/search/search-services.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/security/keyvault-access.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/security/keyvault-secret.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/security/keyvault.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/security/registry-access.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/security/role.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/storage/storage-account.bicep create mode 100644 Environments/App-Base-WebApp-ACA/core/testing/loadtesting.bicep create mode 100644 Environments/App-Base-WebApp-ACA/main.bicep create mode 100644 Environments/App-Base-WebApp-ACA/main.parameters.json create mode 100644 Environments/App-Base-WebApp-ACA/manifest.yaml create mode 100644 Environments/App-Base-WebApp-AKS/abbreviations.json create mode 100644 Environments/App-Base-WebApp-AKS/app/db.bicep create mode 100644 Environments/App-Base-WebApp-AKS/azuredeploy.json create mode 100644 Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/aks.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/appservice.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-app.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/functions.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/search/search-services.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/security/keyvault-access.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/security/keyvault-secret.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/security/keyvault.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/security/registry-access.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/security/role.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep create mode 100644 Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep create mode 100644 Environments/App-Base-WebApp-AKS/main.bicep create mode 100644 Environments/App-Base-WebApp-AKS/main.parameters.json create mode 100644 Environments/App-Base-WebApp-AKS/manifest.yaml create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/abbreviations.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/app/api.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api-policy.xml create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/app/db.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/app/openapi.yaml create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/app/web.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps-environment.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/loganalytics.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/monitoring.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/main.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/main.parameters.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Dev/manifest.yaml create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/abbreviations.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/app/api.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api-policy.xml create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/app/db.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/app/openapi.yaml create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/app/web.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps-environment.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/loganalytics.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/monitoring.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/main.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/main.parameters.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Prod/manifest.yaml create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/abbreviations.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/app/api.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/app/apim-api-policy.xml create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/app/apim-api.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/app/db.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/app/openapi.yaml create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/app/web.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps-environment.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/core/monitor/loganalytics.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/core/monitor/monitoring.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/main.bicep create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/main.parameters.json create mode 100644 Environments/Contoso-Base-Shared-ACA-Test/manifest.yaml create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/abbreviations.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/app/db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/ai/cognitiveservices.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/gateway/apim.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-agent-pool.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-managed-cluster.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice-appsettings.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/appserviceplan.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app-upsert.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps-environment.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-registry.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/functions.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/host/staticwebapp.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/loganalytics.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/monitoring.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-endpoint.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-profile.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/search/search-services.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-secret.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/security/registry-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/security/role.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/storage/storage-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/core/testing/loadtesting.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/main.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/main.parameters.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Dev/manifest.yaml create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/abbreviations.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/app/db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/ai/cognitiveservices.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/gateway/apim.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-agent-pool.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-managed-cluster.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice-appsettings.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/appserviceplan.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app-upsert.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps-environment.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-registry.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/functions.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/host/staticwebapp.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/loganalytics.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/monitoring.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-endpoint.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-profile.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/search/search-services.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-secret.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/security/registry-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/security/role.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/storage/storage-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/core/testing/loadtesting.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/main.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/main.parameters.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Prod/manifest.yaml create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/abbreviations.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/app/db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/ai/cognitiveservices.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/postgresql/flexibleserver.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/database/sqlserver/sqlserver.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/gateway/apim.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-agent-pool.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-managed-cluster.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/aks.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice-appsettings.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/appserviceplan.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app-upsert.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps-environment.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/container-registry.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/functions.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/host/staticwebapp.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights-dashboard.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/monitor/loganalytics.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/monitor/monitoring.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-endpoint.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-profile.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/search/search-services.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-secret.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/security/registry-access.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/security/role.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/storage/storage-account.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/core/testing/loadtesting.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/main.bicep create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/main.parameters.json create mode 100644 Environments/Contoso-Base-Shared-AKS-Test/manifest.yaml diff --git a/Environments/App-Base-WebApp-ACA/abbreviations.json b/Environments/App-Base-WebApp-ACA/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/App-Base-WebApp-ACA/app/api.bicep b/Environments/App-Base-WebApp-ACA/app/api.bicep new file mode 100644 index 00000000..1df68145 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/app/api.bicep @@ -0,0 +1,75 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param keyVaultName string +param serviceName string = 'api' +param corsAcaUrl string +param exists bool + +resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +// Give the API access to KeyVault +module apiKeyVaultAccess '../core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: apiIdentity.properties.principalId + } +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + dependsOn: [ apiKeyVaultAccess ] + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: apiIdentity.name + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: '1.0' + containerMemory: '2.0Gi' + env: [ + { + name: 'AZURE_CLIENT_ID' + value: apiIdentity.properties.clientId + } + { + name: 'AZURE_KEY_VAULT_ENDPOINT' + value: keyVault.properties.vaultUri + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'API_ALLOW_ORIGINS' + value: corsAcaUrl + } + ] + targetPort: 3100 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/App-Base-WebApp-ACA/app/apim-api-policy.xml b/Environments/App-Base-WebApp-ACA/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
    *
    +
    + +
    *
    +
    +
    + + + + + + + Call to the @(context.Api.Name) + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
    diff --git a/Environments/App-Base-WebApp-ACA/app/apim-api.bicep b/Environments/App-Base-WebApp-ACA/app/apim-api.bicep new file mode 100644 index 00000000..b6008404 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('./openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/App-Base-WebApp-ACA/app/db.bicep b/Environments/App-Base-WebApp-ACA/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/App-Base-WebApp-ACA/app/openapi.yaml b/Environments/App-Base-WebApp-ACA/app/openapi.yaml new file mode 100644 index 00000000..c42e99ca --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/app/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid diff --git a/Environments/App-Base-WebApp-ACA/app/web.bicep b/Environments/App-Base-WebApp-ACA/app/web.bicep new file mode 100644 index 00000000..2c692278 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/app/web.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param apiBaseUrl string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param serviceName string = 'web' +param exists bool + +resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: identityName + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + env: [ + { + name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'REACT_APP_API_BASE_URL' + value: apiBaseUrl + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + ] + targetPort: 80 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId +output SERVICE_WEB_NAME string = app.outputs.name +output SERVICE_WEB_URI string = app.outputs.uri +output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/App-Base-WebApp-ACA/azuredeploy.json b/Environments/App-Base-WebApp-ACA/azuredeploy.json new file mode 100644 index 00000000..65c1d9f7 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/azuredeploy.json @@ -0,0 +1,4679 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5915759848307494038" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "apiContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "webContainerAppName": { + "type": "string", + "defaultValue": "" + }, + "apimServiceName": { + "type": "string", + "defaultValue": "" + }, + "apiAppExists": { + "type": "bool", + "defaultValue": false + }, + "webAppExists": { + "type": "bool", + "defaultValue": false + }, + "useAPIM": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "webApiBaseUrl": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The base URL used by the web service for sending API requests" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "containerRegistryAdminUserEnabled": { + "value": true + }, + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14116108111976192358" + }, + "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "containerRegistryResourceGroupName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryAdminUserEnabled": { + "type": "bool", + "defaultValue": false + }, + "logAnalyticsWorkspaceName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4766903245227392386" + }, + "description": "Creates an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Application Insights resource" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-registry', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "adminUserEnabled": { + "value": "[parameters('containerRegistryAdminUserEnabled')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" + }, + "registryLoginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.loginServer.value]" + }, + "registryName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-registry', parameters('name'))), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "web", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('webContainerAppName'))), createObject('value', parameters('webContainerAppName')), createObject('value', format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}web-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "apiBaseUrl": "[if(not(empty(parameters('webApiBaseUrl'))), createObject('value', parameters('webApiBaseUrl')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value))]", + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "exists": { + "value": "[parameters('webAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5244656399300381833" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "apiBaseUrl": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "web" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "env": { + "value": [ + { + "name": "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "REACT_APP_API_BASE_URL", + "value": "[parameters('apiBaseUrl')]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + } + ] + }, + "targetPort": { + "value": 80 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + } + } + ], + "outputs": { + "SERVICE_WEB_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_WEB_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_WEB_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apiContainerAppName'))), createObject('value', parameters('apiContainerAppName')), createObject('value', format('{0}api-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "identityName": { + "value": "[format('{0}api-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "containerAppsEnvironmentName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "corsAcaUrl": { + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "exists": { + "value": "[parameters('apiAppExists')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11092891629527222377" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "identityName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "containerRegistryName": { + "type": "string" + }, + "keyVaultName": { + "type": "string" + }, + "serviceName": { + "type": "string", + "defaultValue": "api" + }, + "corsAcaUrl": { + "type": "string" + }, + "exists": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-app', parameters('serviceName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[union(parameters('tags'), createObject('azd-service-name', parameters('serviceName')))]" + }, + "identityType": { + "value": "UserAssigned" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "exists": { + "value": "[parameters('exists')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "1.0" + }, + "containerMemory": { + "value": "2.0Gi" + }, + "env": { + "value": [ + { + "name": "AZURE_CLIENT_ID", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').clientId]" + }, + { + "name": "AZURE_KEY_VAULT_ENDPOINT", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-07-01').vaultUri]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + }, + { + "name": "API_ALLOW_ORIGINS", + "value": "[parameters('corsAcaUrl')]" + } + ] + }, + "targetPort": { + "value": 3100 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17242409915151931414" + }, + "description": "Creates or updates an existing Azure Container App." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The environment name for the container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "The number of CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "The amount of memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Dapr for the container app" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "exists": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if the resource already exists" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-update', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "identityType": { + "value": "[parameters('identityType')]" + }, + "identityName": { + "value": "[parameters('identityName')]" + }, + "ingressEnabled": { + "value": "[parameters('ingressEnabled')]" + }, + "containerName": { + "value": "[parameters('containerName')]" + }, + "containerAppsEnvironmentName": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "containerCpuCoreCount": { + "value": "[parameters('containerCpuCoreCount')]" + }, + "containerMemory": { + "value": "[parameters('containerMemory')]" + }, + "containerMinReplicas": { + "value": "[parameters('containerMinReplicas')]" + }, + "containerMaxReplicas": { + "value": "[parameters('containerMaxReplicas')]" + }, + "daprEnabled": { + "value": "[parameters('daprEnabled')]" + }, + "daprAppId": { + "value": "[parameters('daprAppId')]" + }, + "daprAppProtocol": { + "value": "[parameters('daprAppProtocol')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + }, + "external": { + "value": "[parameters('external')]" + }, + "env": { + "value": "[parameters('env')]" + }, + "imageName": "[if(not(empty(parameters('imageName'))), createObject('value', parameters('imageName')), if(parameters('exists'), createObject('value', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').template.containers[0].image), createObject('value', '')))]", + "targetPort": { + "value": "[parameters('targetPort')]" + }, + "serviceBinds": { + "value": "[parameters('serviceBinds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1912096201798605494" + }, + "description": "Creates a container app in an Azure Container App environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "allowedOrigins": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Allowed origins" + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Name of the environment for container apps" + } + }, + "containerCpuCoreCount": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "CPU cores allocated to a single container instance, e.g., 0.5" + } + }, + "containerMaxReplicas": { + "type": "int", + "defaultValue": 10, + "minValue": 1, + "metadata": { + "description": "The maximum number of replicas to run. Must be at least 1." + } + }, + "containerMemory": { + "type": "string", + "defaultValue": "1.0Gi", + "metadata": { + "description": "Memory allocated to a single container instance, e.g., 1Gi" + } + }, + "containerMinReplicas": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The minimum number of replicas to run. Must be at least 1." + } + }, + "containerName": { + "type": "string", + "defaultValue": "main", + "metadata": { + "description": "The name of the container" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container registry" + } + }, + "daprAppProtocol": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "http", + "grpc" + ], + "metadata": { + "description": "The protocol used by Dapr to connect to the app, e.g., http or grpc" + } + }, + "daprAppId": { + "type": "string", + "defaultValue": "[parameters('containerName')]", + "metadata": { + "description": "The Dapr app ID" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Dapr" + } + }, + "env": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The environment variables for the container" + } + }, + "external": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if the resource ingress is exposed externally" + } + }, + "identityName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the user-assigned identity" + } + }, + "identityType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned", + "UserAssigned" + ], + "metadata": { + "description": "The type of identity for the resource" + } + }, + "imageName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container image" + } + }, + "ingressEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies if Ingress is enabled for the container app" + } + }, + "revisionMode": { + "type": "string", + "defaultValue": "Single" + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The secrets required for the container" + } + }, + "serviceBinds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The service binds associated with the container" + } + }, + "serviceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the container apps add-on to use. e.g. redis" + } + }, + "targetPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "The target port for the container" + } + } + }, + "variables": { + "usePrivateRegistry": "[and(not(empty(parameters('identityName'))), not(empty(parameters('containerRegistryName'))))]", + "normalizedIdentityType": "[if(not(empty(parameters('identityName'))), 'UserAssigned', parameters('identityType'))]" + }, + "resources": [ + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "[variables('normalizedIdentityType')]", + "userAssignedIdentities": "[if(and(not(empty(parameters('identityName'))), equals(variables('normalizedIdentityType'), 'UserAssigned')), createObject(format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))), createObject()), null())]" + }, + "properties": { + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]", + "configuration": { + "activeRevisionsMode": "[parameters('revisionMode')]", + "ingress": "[if(parameters('ingressEnabled'), createObject('external', parameters('external'), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'corsPolicy', createObject('allowedOrigins', union(createArray('https://portal.azure.com', 'https://ms.portal.azure.com'), parameters('allowedOrigins')))), null())]", + "dapr": "[if(parameters('daprEnabled'), createObject('enabled', true(), 'appId', parameters('daprAppId'), 'appProtocol', parameters('daprAppProtocol'), 'appPort', if(parameters('ingressEnabled'), parameters('targetPort'), 0)), createObject('enabled', false()))]", + "secrets": "[parameters('secrets')]", + "service": "[if(not(empty(parameters('serviceType'))), createObject('type', parameters('serviceType')), null())]", + "registries": "[if(variables('usePrivateRegistry'), createArray(createObject('server', format('{0}.azurecr.io', parameters('containerRegistryName')), 'identity', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))), createArray())]" + }, + "template": { + "serviceBinds": "[if(not(empty(parameters('serviceBinds'))), parameters('serviceBinds'), null())]", + "containers": [ + { + "image": "[if(not(empty(parameters('imageName'))), parameters('imageName'), 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest')]", + "name": "[parameters('containerName')]", + "env": "[parameters('env')]", + "resources": { + "cpu": "[json(parameters('containerCpuCoreCount'))]", + "memory": "[parameters('containerMemory')]" + } + } + ], + "scale": { + "minReplicas": "[parameters('containerMinReplicas')]", + "maxReplicas": "[parameters('containerMaxReplicas')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-registry-access', deployment().name))]" + ] + }, + { + "condition": "[variables('usePrivateRegistry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-access', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" + }, + "principalId": "[if(variables('usePrivateRegistry'), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2023-04-01-preview').defaultDomain]" + }, + "identityPrincipalId": { + "type": "string", + "value": "[if(equals(variables('normalizedIdentityType'), 'None'), '', if(empty(parameters('identityName')), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview', 'full').identity.principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId))]" + }, + "imageName": { + "type": "string", + "value": "[parameters('imageName')]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "serviceBind": { + "type": "object", + "value": "[if(not(empty(parameters('serviceType'))), createObject('serviceId', resourceId('Microsoft.App/containerApps', parameters('name')), 'name', parameters('name')), createObject())]" + }, + "uri": { + "type": "string", + "value": "[if(parameters('ingressEnabled'), format('https://{0}', reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2023-04-01-preview').configuration.ingress.fqdn), '')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.defaultDomain.value]" + }, + "imageName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.imageName.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-update', deployment().name)), '2022-09-01').outputs.uri.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]", + "[resourceId('Microsoft.Resources/deployments', 'api-keyvault-access')]" + ] + } + ], + "outputs": { + "SERVICE_API_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.name.value]" + }, + "SERVICE_API_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.uri.value]" + }, + "SERVICE_API_IMAGE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-app', parameters('serviceName'))), '2022-09-01').outputs.imageName.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-apps')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5730728686647632614" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14549161001187918251" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8317058180807592714" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('apimServiceName'))), createObject('value', parameters('apimServiceName')), createObject('value', format('{0}{1}', variables('abbrs').apiManagementService, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "3036576769636454145" + }, + "description": "Creates an Azure API Management instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "publisherEmail": { + "type": "string", + "defaultValue": "noreply@microsoft.com", + "minLength": 1, + "metadata": { + "description": "The email address of the owner of the service" + } + }, + "publisherName": { + "type": "string", + "defaultValue": "n/a", + "minLength": 1, + "metadata": { + "description": "The name of the owner of the service" + } + }, + "sku": { + "type": "string", + "defaultValue": "Consumption", + "allowedValues": [ + "Consumption", + "Developer", + "Standard", + "Premium" + ], + "metadata": { + "description": "The pricing tier of this API Management service" + } + }, + "skuCount": { + "type": "int", + "defaultValue": 0, + "allowedValues": [ + 0, + 1, + 2 + ], + "metadata": { + "description": "The instance size of this API Management service." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "Azure Application Insights Name" + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2021-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('azd-service-name', parameters('name')))]", + "sku": { + "name": "[parameters('sku')]", + "capacity": "[if(equals(parameters('sku'), 'Consumption'), 0, if(equals(parameters('sku'), 'Developer'), 1, parameters('skuCount')))]" + }, + "properties": { + "publisherEmail": "[parameters('publisherEmail')]", + "publisherName": "[parameters('publisherName')]", + "customProperties": "[if(equals(parameters('sku'), 'Consumption'), createObject(), createObject('Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11', 'false', 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30', 'false'))]" + } + }, + { + "condition": "[not(empty(parameters('applicationInsightsName')))]", + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'app-insights-logger')]", + "properties": { + "credentials": { + "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" + }, + "description": "Logger to Azure Application Insights", + "isBuffered": false, + "loggerType": "applicationInsights", + "resourceId": "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" + ] + } + ], + "outputs": { + "apimServiceName": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "condition": "[parameters('useAPIM')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "apim-api-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(parameters('useAPIM'), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'apim-deployment'), '2022-09-01').outputs.apimServiceName.value), createObject('value', ''))]", + "apiName": { + "value": "todo-api" + }, + "apiDisplayName": { + "value": "Simple Todo API" + }, + "apiDescription": { + "value": "This is a simple Todo API" + }, + "apiPath": { + "value": "todo" + }, + "webFrontendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "apiBackendUrl": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6615097664318461925" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "apiName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Resource name to uniquely identify this API within the API Management service instance" + } + }, + "apiDisplayName": { + "type": "string", + "minLength": 1, + "maxLength": 300, + "metadata": { + "description": "The Display Name of the API" + } + }, + "apiDescription": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Description of the API. May include HTML formatting tags." + } + }, + "apiPath": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + } + }, + "webFrontendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the web frontend" + } + }, + "apiBackendUrl": { + "type": "string", + "metadata": { + "description": "Absolute URL of the backend service implementing this API." + } + }, + "apiAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Resource name for backend Web App or Function App" + } + } + }, + "variables": { + "$fxv#0": "\n\n \n \n \n \n \n {origin}\n \n \n PUT\n GET\n POST\n DELETE\n PATCH\n \n \n
    *
    \n
    \n \n
    *
    \n
    \n
    \n \n \n \n \n \n \n Call to the @(context.Api.Name)\n \n \n \n \n \n
    \n \n \n \n \n \n \n \n \n \n \n \n = 200 && context.Response.StatusCode < 300)\">\n \n \n \n \n \n \n \n = 400 && context.Response.StatusCode < 600)\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Failed to process the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n An unexpected error has occurred.\n \n \n
    \n", + "$fxv#1": "openapi: 3.0.0\ninfo:\n description: Simple Todo API\n version: 3.0.0\n title: Simple Todo API\n contact:\n email: azdevteam@microsoft.com\n\ncomponents:\n schemas:\n TodoItem:\n type: object\n required:\n - listId\n - name\n - description\n description: A task that needs to be completed\n properties:\n id:\n type: string\n listId:\n type: string\n name:\n type: string\n description:\n type: string\n state:\n $ref: \"#/components/schemas/TodoState\"\n dueDate:\n type: string\n format: date-time\n completedDate:\n type: string\n format: date-time\n TodoList:\n type: object\n required:\n - name\n properties:\n id:\n type: string\n name:\n type: string\n description:\n type: string\n description: \" A list of related Todo items\"\n TodoState:\n type: string\n enum:\n - todo\n - inprogress\n - done\n parameters:\n listId:\n in: path\n required: true\n name: listId\n description: The Todo list unique identifier\n schema:\n type: string\n itemId:\n in: path\n required: true\n name: itemId\n description: The Todo item unique identifier\n schema:\n type: string\n state:\n in: path\n required: true\n name: state\n description: The Todo item state\n schema:\n $ref: \"#/components/schemas/TodoState\"\n top:\n in: query\n required: false\n name: top\n description: The max number of items to returns in a result\n schema:\n type: number\n default: 20\n skip:\n in: query\n required: false\n name: skip\n description: The number of items to skip within the results\n schema:\n type: number\n default: 0\n\n requestBodies:\n TodoList:\n description: The Todo List\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: The Todo Item\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n\n responses:\n TodoList:\n description: A Todo list result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoListArray:\n description: An array of Todo lists\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: A Todo item result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n TodoItemArray:\n description: An array of Todo items\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoItem\"\n\npaths:\n /lists:\n get:\n operationId: GetLists\n summary: Gets an array of Todo lists\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoListArray\"\n post:\n operationId: CreateList\n summary: Creates a new Todo list\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoList\"\n 400:\n description: Invalid request schema\n /lists/{listId}:\n get:\n operationId: GetListById\n summary: Gets a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n put:\n operationId: UpdateListById\n summary: Updates a Todo list by unique identifier\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n 400:\n description: Todo list is invalid\n delete:\n operationId: DeleteListById\n summary: Deletes a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 204:\n description: Todo list deleted successfully\n 404:\n description: Todo list not found\n /lists/{listId}/items:\n post:\n operationId: CreateItem\n summary: Creates a new Todo item within a list\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list not found\n get:\n operationId: GetItemsByListId\n summary: Gets Todo items within the specified list\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list not found\n /lists/{listId}/items/{itemId}:\n get:\n operationId: GetItemById\n summary: Gets a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemById\n summary: Updates a Todo item by unique identifier\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 400:\n description: Todo item is invalid\n 404:\n description: Todo list or item not found\n delete:\n operationId: DeleteItemById\n summary: Deletes a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 204:\n description: Todo item deleted successfully\n 404:\n description: Todo list or item not found\n /lists/{listId}/items/state/{state}:\n get:\n operationId: GetItemsByListIdAndState\n summary: Gets a list of Todo items of a specific state\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemsStateByListId\n summary: Changes the state of the specified list items\n tags:\n - Items\n requestBody:\n description: unique identifiers of the Todo items to update\n content:\n application/json:\n schema:\n type: array\n items:\n description: The Todo item unique identifier\n type: string\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n responses:\n 204:\n description: Todo items updated\n 400:\n description: Update request is invalid\n", + "apiPolicyContent": "[replace(variables('$fxv#0'), '{origin}', parameters('webFrontendUrl'))]", + "appNameForBicep": "[if(not(empty(parameters('apiAppName'))), parameters('apiAppName'), 'placeholderName')]" + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}', parameters('name'), parameters('apiName'))]", + "properties": { + "description": "[parameters('apiDescription')]", + "displayName": "[parameters('apiDisplayName')]", + "path": "[parameters('apiPath')]", + "protocols": [ + "https" + ], + "subscriptionRequired": false, + "type": "http", + "format": "openapi", + "serviceUrl": "[parameters('apiBackendUrl')]", + "value": "[variables('$fxv#1')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/policies", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'policy')]", + "properties": { + "format": "rawxml", + "value": "[variables('apiPolicyContent')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2021-12-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'applicationinsights')]", + "properties": { + "alwaysLog": "allErrors", + "backend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "frontend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "httpCorrelationProtocol": "W3C", + "logClientIp": true, + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('name'), 'app-insights-logger')]", + "metrics": true, + "sampling": { + "percentage": 100, + "samplingType": "fixed" + }, + "verbosity": "verbose" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "condition": "[not(empty(parameters('apiAppName')))]", + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/web', variables('appNameForBicep'))]", + "kind": "string", + "properties": { + "apiManagementConfig": { + "id": "[format('{0}/apis/{1}', resourceId('Microsoft.ApiManagement/service', parameters('name')), parameters('apiName'))]" + } + } + } + ], + "outputs": { + "SERVICE_API_URI": { + "type": "string", + "value": "[format('{0}/{1}', reference(resourceId('Microsoft.ApiManagement/service', parameters('name')), '2021-08-01').gatewayUrl, parameters('apiPath'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'api')]", + "[resourceId('Microsoft.Resources/deployments', 'apim-deployment')]", + "[resourceId('Microsoft.Resources/deployments', 'web')]" + ] + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "API_CORS_ACA_URL": { + "type": "string", + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "APPLICATIONINSIGHTS_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.registryName.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_API_BASE_URL": { + "type": "string", + "value": "[if(parameters('useAPIM'), reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value)]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "REACT_APP_WEB_BASE_URL": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_URI.value]" + }, + "SERVICE_API_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_NAME.value]" + }, + "SERVICE_WEB_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'web'), '2022-09-01').outputs.SERVICE_WEB_NAME.value]" + }, + "USE_APIM": { + "type": "bool", + "value": "[parameters('useAPIM')]" + }, + "SERVICE_API_ENDPOINTS": { + "type": "array", + "value": "[if(parameters('useAPIM'), createArray(reference(resourceId('Microsoft.Resources/deployments', 'apim-api-deployment'), '2022-09-01').outputs.SERVICE_API_URI.value, reference(resourceId('Microsoft.Resources/deployments', 'api'), '2022-09-01').outputs.SERVICE_API_URI.value), createArray())]" + } + } +} \ No newline at end of file diff --git a/Environments/App-Base-WebApp-ACA/core/ai/cognitiveservices.bicep b/Environments/App-Base-WebApp-ACA/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/App-Base-WebApp-ACA/core/database/postgresql/flexibleserver.bicep b/Environments/App-Base-WebApp-ACA/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/App-Base-WebApp-ACA/core/database/sqlserver/sqlserver.bicep b/Environments/App-Base-WebApp-ACA/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/App-Base-WebApp-ACA/core/gateway/apim.bicep b/Environments/App-Base-WebApp-ACA/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/App-Base-WebApp-ACA/core/host/aks-agent-pool.bicep b/Environments/App-Base-WebApp-ACA/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/App-Base-WebApp-ACA/core/host/aks-managed-cluster.bicep b/Environments/App-Base-WebApp-ACA/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/App-Base-WebApp-ACA/core/host/aks.bicep b/Environments/App-Base-WebApp-ACA/core/host/aks.bicep new file mode 100644 index 00000000..536a534b --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/aks.bicep @@ -0,0 +1,280 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/App-Base-WebApp-ACA/core/host/appservice-appsettings.bicep b/Environments/App-Base-WebApp-ACA/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/App-Base-WebApp-ACA/core/host/appservice.bicep b/Environments/App-Base-WebApp-ACA/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/App-Base-WebApp-ACA/core/host/appserviceplan.bicep b/Environments/App-Base-WebApp-ACA/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/App-Base-WebApp-ACA/core/host/container-app-upsert.bicep b/Environments/App-Base-WebApp-ACA/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/App-Base-WebApp-ACA/core/host/container-app.bicep b/Environments/App-Base-WebApp-ACA/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/App-Base-WebApp-ACA/core/host/container-apps-environment.bicep b/Environments/App-Base-WebApp-ACA/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/App-Base-WebApp-ACA/core/host/container-apps.bicep b/Environments/App-Base-WebApp-ACA/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/App-Base-WebApp-ACA/core/host/container-registry.bicep b/Environments/App-Base-WebApp-ACA/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/App-Base-WebApp-ACA/core/host/functions.bicep b/Environments/App-Base-WebApp-ACA/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/App-Base-WebApp-ACA/core/host/staticwebapp.bicep b/Environments/App-Base-WebApp-ACA/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights-dashboard.bicep b/Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights.bicep b/Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/App-Base-WebApp-ACA/core/monitor/loganalytics.bicep b/Environments/App-Base-WebApp-ACA/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/App-Base-WebApp-ACA/core/monitor/monitoring.bicep b/Environments/App-Base-WebApp-ACA/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/App-Base-WebApp-ACA/core/networking/cdn-endpoint.bicep b/Environments/App-Base-WebApp-ACA/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/App-Base-WebApp-ACA/core/networking/cdn-profile.bicep b/Environments/App-Base-WebApp-ACA/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/App-Base-WebApp-ACA/core/networking/cdn.bicep b/Environments/App-Base-WebApp-ACA/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/App-Base-WebApp-ACA/core/search/search-services.bicep b/Environments/App-Base-WebApp-ACA/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/App-Base-WebApp-ACA/core/security/aks-managed-cluster-access.bicep b/Environments/App-Base-WebApp-ACA/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/App-Base-WebApp-ACA/core/security/keyvault-access.bicep b/Environments/App-Base-WebApp-ACA/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/App-Base-WebApp-ACA/core/security/keyvault-secret.bicep b/Environments/App-Base-WebApp-ACA/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/App-Base-WebApp-ACA/core/security/keyvault.bicep b/Environments/App-Base-WebApp-ACA/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/App-Base-WebApp-ACA/core/security/registry-access.bicep b/Environments/App-Base-WebApp-ACA/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/App-Base-WebApp-ACA/core/security/role.bicep b/Environments/App-Base-WebApp-ACA/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/App-Base-WebApp-ACA/core/storage/storage-account.bicep b/Environments/App-Base-WebApp-ACA/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/App-Base-WebApp-ACA/core/testing/loadtesting.bicep b/Environments/App-Base-WebApp-ACA/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/App-Base-WebApp-ACA/main.bicep b/Environments/App-Base-WebApp-ACA/main.bicep new file mode 100644 index 00000000..25088009 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/main.bicep @@ -0,0 +1,178 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param apiContainerAppName string = '' +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param containerAppsEnvironmentName string = '' +param containerRegistryName string = '' +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param logAnalyticsName string = '' +param webContainerAppName string = '' +param apimServiceName string = '' +param apiAppExists bool = false +param webAppExists bool = false + +@description('Flag to use Azure API Management to mediate the calls between the Web frontend and the backend API') +param useAPIM bool = false + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('The base URL used by the web service for sending API requests') +param webApiBaseUrl string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } +var apiContainerAppNameOrDefault = '${abbrs.appContainerApps}web-${resourceToken}' +var corsAcaUrl = 'https://${apiContainerAppNameOrDefault}.${containerApps.outputs.defaultDomain}' + +// Container apps host (including container registry) +module containerApps './core/host/container-apps.bicep' = { + name: 'container-apps' + params: { + name: 'app' + location: location + tags: tags + containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + // Work around Azure/azure-dev#3157 (the root cause of which is Azure/acr#723) by explicitly enabling the admin user to allow users which + // don't have the `Owner` role granted (and instead are classic administrators) to access the registry to push even if AAD authentication fails. + // + // This addresses the following error during deploy: + // + // failed getting ACR token: POST https://.azurecr.io/oauth2/exchange 401 Unauthorized + containerRegistryAdminUserEnabled: true + logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Web frontend +module web './app/web.bicep' = { + name: 'web' + params: { + name: !empty(webContainerAppName) ? webContainerAppName : '${abbrs.appContainerApps}web-${resourceToken}' + location: location + tags: tags + identityName: '${abbrs.managedIdentityUserAssignedIdentities}web-${resourceToken}' + apiBaseUrl: !empty(webApiBaseUrl) ? webApiBaseUrl : api.outputs.SERVICE_API_URI + applicationInsightsName: monitoring.outputs.applicationInsightsName + containerAppsEnvironmentName: containerApps.outputs.environmentName + containerRegistryName: containerApps.outputs.registryName + exists: webAppExists + } +} + +// Api backend +module api './app/api.bicep' = { + name: 'api' + params: { + name: !empty(apiContainerAppName) ? apiContainerAppName : '${abbrs.appContainerApps}api-${resourceToken}' + location: location + tags: tags + identityName: '${abbrs.managedIdentityUserAssignedIdentities}api-${resourceToken}' + applicationInsightsName: monitoring.outputs.applicationInsightsName + containerAppsEnvironmentName: containerApps.outputs.environmentName + containerRegistryName: containerApps.outputs.registryName + keyVaultName: keyVault.outputs.name + corsAcaUrl: corsAcaUrl + exists: apiAppExists + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Creates Azure API Management (APIM) service to mediate the requests between the frontend and the backend API +module apim './core/gateway/apim.bicep' = if (useAPIM) { + name: 'apim-deployment' + params: { + name: !empty(apimServiceName) ? apimServiceName : '${abbrs.apiManagementService}${resourceToken}' + location: location + tags: tags + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Configures the API in the Azure API Management (APIM) service +module apimApi './app/apim-api.bicep' = if (useAPIM) { + name: 'apim-api-deployment' + params: { + name: useAPIM ? apim.outputs.apimServiceName : '' + apiName: 'todo-api' + apiDisplayName: 'Simple Todo API' + apiDescription: 'This is a simple Todo API' + apiPath: 'todo' + webFrontendUrl: web.outputs.SERVICE_WEB_URI + apiBackendUrl: api.outputs.SERVICE_API_URI + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output API_CORS_ACA_URL string = corsAcaUrl +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName +output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerApps.outputs.registryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = containerApps.outputs.registryName +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output REACT_APP_API_BASE_URL string = useAPIM ? apimApi.outputs.SERVICE_API_URI : api.outputs.SERVICE_API_URI +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output REACT_APP_WEB_BASE_URL string = web.outputs.SERVICE_WEB_URI +output SERVICE_API_NAME string = api.outputs.SERVICE_API_NAME +output SERVICE_WEB_NAME string = web.outputs.SERVICE_WEB_NAME +output USE_APIM bool = useAPIM +output SERVICE_API_ENDPOINTS array = useAPIM ? [ apimApi.outputs.SERVICE_API_URI, api.outputs.SERVICE_API_URI ]: [] diff --git a/Environments/App-Base-WebApp-ACA/main.parameters.json b/Environments/App-Base-WebApp-ACA/main.parameters.json new file mode 100644 index 00000000..8ddb71e6 --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/main.parameters.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + }, + "apiAppExists": { + "value": "${SERVICE_API_RESOURCE_EXISTS=false}" + }, + "webAppExists": { + "value": "${SERVICE_WEB_RESOURCE_EXISTS=false}" + }, + "webApiBaseUrl": { + "value": "${REACT_APP_API_BASE_URL}" + }, + "useAPIM": { + "value": "${USE_APIM=false}" + } + } +} \ No newline at end of file diff --git a/Environments/App-Base-WebApp-ACA/manifest.yaml b/Environments/App-Base-WebApp-ACA/manifest.yaml new file mode 100644 index 00000000..277374ae --- /dev/null +++ b/Environments/App-Base-WebApp-ACA/manifest.yaml @@ -0,0 +1,19 @@ +name: App-Base-WebApp-ACA +version: 1.0.0 +summary: App-Base-Nodejs Mongo ACA +description: Deploys a base infra for ACA app dev with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true diff --git a/Environments/App-Base-WebApp-AKS/abbreviations.json b/Environments/App-Base-WebApp-AKS/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/App-Base-WebApp-AKS/app/db.bicep b/Environments/App-Base-WebApp-AKS/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json new file mode 100644 index 00000000..9626594f --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -0,0 +1,3384 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13440997233145271475" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the AKS cluster" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the Container Registry (ACR)" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4109929557657560885" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name for the Azure container registry (ACR)" + } + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the connected log analytics workspace" + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the keyvault to grant access" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "addOns": { + "type": "object", + "defaultValue": { + "azurePolicy": { + "enabled": true, + "config": { + "version": "v2" + } + }, + "keyVault": { + "enabled": true, + "config": { + "enableSecretRotation": "true", + "rotationPollInterval": "2m" + } + }, + "openServiceMesh": { + "enabled": false, + "config": {} + }, + "omsAgent": { + "enabled": true, + "config": {} + }, + "applicationGateway": { + "enabled": false, + "config": {} + } + }, + "metadata": { + "description": "AKS add-ons configuration" + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "systemPoolType": { + "type": "string", + "defaultValue": "CostOptimised", + "allowedValues": [ + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The System Pool Preset sizing" + } + }, + "agentPoolType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The User Pool Preset sizing" + } + }, + "systemPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of system node pool" + } + }, + "agentPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of user node pool" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + } + }, + "variables": { + "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", + "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", + "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", + "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", + "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", + "nodePoolBase": { + "osType": "Linux", + "maxPods": 30, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "33%" + } + }, + "nodePoolPresets": { + "CostOptimised": { + "vmSize": "Standard_B4ms", + "count": 1, + "minCount": 1, + "maxCount": 3, + "enableAutoScaling": true, + "availabilityZones": [] + }, + "Standard": { + "vmSize": "Standard_DS2_v2", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + }, + "HighSpec": { + "vmSize": "Standard_D4s_v3", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "managed-cluster", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" + }, + "nodeResourceGroupName": { + "value": "[parameters('nodeResourceGroupName')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "dnsPrefix": { + "value": "[parameters('dnsPrefix')]" + }, + "kubernetesVersion": { + "value": "[parameters('kubernetesVersion')]" + }, + "addOns": { + "value": "[variables('addOnsConfig')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]", + "enableAad": { + "value": "[and(parameters('enableAzureRbac'), not(equals(parameters('aadTenantId'), '')))]" + }, + "disableLocalAccounts": { + "value": "[parameters('disableLocalAccounts')]" + }, + "aadTenantId": { + "value": "[parameters('aadTenantId')]" + }, + "enableRbac": { + "value": "[parameters('enableRbac')]" + }, + "enableAzureRbac": { + "value": "[parameters('enableAzureRbac')]" + }, + "webAppRoutingAddon": { + "value": "[parameters('webAppRoutingAddon')]" + }, + "loadBalancerSku": { + "value": "[parameters('loadBalancerSku')]" + }, + "networkPlugin": { + "value": "[parameters('networkPlugin')]" + }, + "networkPolicy": { + "value": "[parameters('networkPolicy')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8184151159222198677" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-10-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPolicy": "[parameters('networkPolicy')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webAppRoutingAddon')]" + } + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.resourceId]" + } + } + } + } + } + }, + { + "condition": "[variables('hasAgentPool')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks-node-pool", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "name": { + "value": "npuserpool" + }, + "config": { + "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6072270897511874144" + }, + "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "name": { + "type": "string", + "metadata": { + "description": "The agent pool name" + } + }, + "config": { + "type": "object", + "metadata": { + "description": "The agent pool configuration" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2023-10-02-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": "[parameters('config')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-container-registry-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "condition": "[or(parameters('enableAzureRbac'), parameters('disableLocalAccounts'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8205013527918052324" + }, + "description": "Assigns RBAC role to the specified AKS cluster and principal." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "aksClusterAdminRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('aksClusterAdminRole'))]", + "properties": { + "roleDefinitionId": "[variables('aksClusterAdminRole')]", + "principalType": "User", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The resource name of the ACR" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The login server for the container registry" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "5730728686647632614" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "14549161001187918251" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8317058180807592714" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_AKS_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep b/Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep b/Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep b/Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep b/Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep b/Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep b/Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/App-Base-WebApp-AKS/core/host/aks.bicep b/Environments/App-Base-WebApp-AKS/core/host/aks.bicep new file mode 100644 index 00000000..536a534b --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/aks.bicep @@ -0,0 +1,280 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep b/Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/App-Base-WebApp-AKS/core/host/appservice.bicep b/Environments/App-Base-WebApp-AKS/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep b/Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-app.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/functions.bicep b/Environments/App-Base-WebApp-AKS/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep b/Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep b/Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep b/Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep b/Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/App-Base-WebApp-AKS/core/search/search-services.bicep b/Environments/App-Base-WebApp-AKS/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/App-Base-WebApp-AKS/core/security/aks-managed-cluster-access.bicep b/Environments/App-Base-WebApp-AKS/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/App-Base-WebApp-AKS/core/security/keyvault-access.bicep b/Environments/App-Base-WebApp-AKS/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/App-Base-WebApp-AKS/core/security/keyvault-secret.bicep b/Environments/App-Base-WebApp-AKS/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/App-Base-WebApp-AKS/core/security/keyvault.bicep b/Environments/App-Base-WebApp-AKS/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/App-Base-WebApp-AKS/core/security/registry-access.bicep b/Environments/App-Base-WebApp-AKS/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/App-Base-WebApp-AKS/core/security/role.bicep b/Environments/App-Base-WebApp-AKS/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep b/Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep b/Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/App-Base-WebApp-AKS/main.bicep b/Environments/App-Base-WebApp-AKS/main.bicep new file mode 100644 index 00000000..a382fedb --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/main.bicep @@ -0,0 +1,91 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string + +@description('The resource name of the AKS cluster') +param clusterName string = '' + +@description('The resource name of the Container Registry (ACR)') +param containerRegistryName string = '' + +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param logAnalyticsName string = '' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// The AKS cluster to host applications +module aks './core/host/aks.bicep' = { + name: 'aks' + params: { + location: location + name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName + keyVaultName: keyVault.outputs.name + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName +output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/App-Base-WebApp-AKS/main.parameters.json b/Environments/App-Base-WebApp-AKS/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/App-Base-WebApp-AKS/manifest.yaml b/Environments/App-Base-WebApp-AKS/manifest.yaml new file mode 100644 index 00000000..7e08cb3e --- /dev/null +++ b/Environments/App-Base-WebApp-AKS/manifest.yaml @@ -0,0 +1,28 @@ +name: App-Base-WebApp-AKS +version: 1.0.0 +summary: App Base Nodejs Mongo AKS +description: Deploys a base infra for AKS app dev with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + +- id: "principalId" + name: "principalId (e.g. )" + description: "principal id that have the permission (get list) to access key vault" + type: string + required: false + default: '' + + diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/abbreviations.json b/Environments/Contoso-Base-Shared-ACA-Dev/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/app/api.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/app/api.bicep new file mode 100644 index 00000000..1df68145 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/app/api.bicep @@ -0,0 +1,75 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param keyVaultName string +param serviceName string = 'api' +param corsAcaUrl string +param exists bool + +resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +// Give the API access to KeyVault +module apiKeyVaultAccess '../core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: apiIdentity.properties.principalId + } +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + dependsOn: [ apiKeyVaultAccess ] + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: apiIdentity.name + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: '1.0' + containerMemory: '2.0Gi' + env: [ + { + name: 'AZURE_CLIENT_ID' + value: apiIdentity.properties.clientId + } + { + name: 'AZURE_KEY_VAULT_ENDPOINT' + value: keyVault.properties.vaultUri + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'API_ALLOW_ORIGINS' + value: corsAcaUrl + } + ] + targetPort: 3100 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api-policy.xml b/Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
    *
    +
    + +
    *
    +
    +
    + + + + + + + Call to the @(context.Api.Name) + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
    diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api.bicep new file mode 100644 index 00000000..b6008404 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('./openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/app/db.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/app/openapi.yaml b/Environments/Contoso-Base-Shared-ACA-Dev/app/openapi.yaml new file mode 100644 index 00000000..c42e99ca --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/app/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/app/web.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/app/web.bicep new file mode 100644 index 00000000..2c692278 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/app/web.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param apiBaseUrl string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param serviceName string = 'web' +param exists bool + +resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: identityName + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + env: [ + { + name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'REACT_APP_API_BASE_URL' + value: apiBaseUrl + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + ] + targetPort: 80 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId +output SERVICE_WEB_NAME string = app.outputs.name +output SERVICE_WEB_URI string = app.outputs.uri +output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json new file mode 100644 index 00000000..94f1cb0a --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json @@ -0,0 +1,1917 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "7010794115259852587" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10266908178814002202" + }, + "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "logAnalyticsWorkspaceName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4766903245227392386" + }, + "description": "Creates an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Application Insights resource" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "API_CORS_ACA_URL": { + "type": "string", + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "APPLICATIONINSIGHTS_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps-environment.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps.bicep new file mode 100644 index 00000000..b180d264 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/core/host/container-apps.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights-dashboard.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/loganalytics.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/monitoring.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/main.bicep b/Environments/Contoso-Base-Shared-ACA-Dev/main.bicep new file mode 100644 index 00000000..8864f5ca --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/main.bicep @@ -0,0 +1,59 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param containerAppsEnvironmentName string = '' +param logAnalyticsName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } +var apiContainerAppNameOrDefault = '${abbrs.appContainerApps}web-${resourceToken}' +var corsAcaUrl = 'https://${apiContainerAppNameOrDefault}.${containerApps.outputs.defaultDomain}' + +// Container apps host (including container registry) +module containerApps './core/host/container-apps.bicep' = { + name: 'container-apps' + params: { + name: 'app' + location: location + tags: tags + containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output API_CORS_ACA_URL string = corsAcaUrl +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName +output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName + +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId + +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/main.parameters.json b/Environments/Contoso-Base-Shared-ACA-Dev/main.parameters.json new file mode 100644 index 00000000..8f7787be --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/main.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/manifest.yaml b/Environments/Contoso-Base-Shared-ACA-Dev/manifest.yaml new file mode 100644 index 00000000..f95fd3b4 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Dev/manifest.yaml @@ -0,0 +1,19 @@ +name: Contoso-Base-Shared-ACA-Dev +version: 1.0.0 +summary: Contoso-Base-Shared-ACA-Dev +description: Deploys base shared env (Container environment and monitoring) for app development in ACA +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/abbreviations.json b/Environments/Contoso-Base-Shared-ACA-Prod/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/app/api.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/app/api.bicep new file mode 100644 index 00000000..1df68145 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/app/api.bicep @@ -0,0 +1,75 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param keyVaultName string +param serviceName string = 'api' +param corsAcaUrl string +param exists bool + +resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +// Give the API access to KeyVault +module apiKeyVaultAccess '../core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: apiIdentity.properties.principalId + } +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + dependsOn: [ apiKeyVaultAccess ] + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: apiIdentity.name + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: '1.0' + containerMemory: '2.0Gi' + env: [ + { + name: 'AZURE_CLIENT_ID' + value: apiIdentity.properties.clientId + } + { + name: 'AZURE_KEY_VAULT_ENDPOINT' + value: keyVault.properties.vaultUri + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'API_ALLOW_ORIGINS' + value: corsAcaUrl + } + ] + targetPort: 3100 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api-policy.xml b/Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
    *
    +
    + +
    *
    +
    +
    + + + + + + + Call to the @(context.Api.Name) + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
    diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api.bicep new file mode 100644 index 00000000..b6008404 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('./openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/app/db.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/app/openapi.yaml b/Environments/Contoso-Base-Shared-ACA-Prod/app/openapi.yaml new file mode 100644 index 00000000..c42e99ca --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/app/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/app/web.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/app/web.bicep new file mode 100644 index 00000000..2c692278 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/app/web.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param apiBaseUrl string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param serviceName string = 'web' +param exists bool + +resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: identityName + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + env: [ + { + name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'REACT_APP_API_BASE_URL' + value: apiBaseUrl + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + ] + targetPort: 80 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId +output SERVICE_WEB_NAME string = app.outputs.name +output SERVICE_WEB_URI string = app.outputs.uri +output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json new file mode 100644 index 00000000..94f1cb0a --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json @@ -0,0 +1,1917 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "7010794115259852587" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10266908178814002202" + }, + "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "logAnalyticsWorkspaceName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4766903245227392386" + }, + "description": "Creates an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Application Insights resource" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "API_CORS_ACA_URL": { + "type": "string", + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "APPLICATIONINSIGHTS_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps-environment.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps.bicep new file mode 100644 index 00000000..b180d264 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/core/host/container-apps.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights-dashboard.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/loganalytics.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/monitoring.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/main.bicep b/Environments/Contoso-Base-Shared-ACA-Prod/main.bicep new file mode 100644 index 00000000..8864f5ca --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/main.bicep @@ -0,0 +1,59 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param containerAppsEnvironmentName string = '' +param logAnalyticsName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } +var apiContainerAppNameOrDefault = '${abbrs.appContainerApps}web-${resourceToken}' +var corsAcaUrl = 'https://${apiContainerAppNameOrDefault}.${containerApps.outputs.defaultDomain}' + +// Container apps host (including container registry) +module containerApps './core/host/container-apps.bicep' = { + name: 'container-apps' + params: { + name: 'app' + location: location + tags: tags + containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output API_CORS_ACA_URL string = corsAcaUrl +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName +output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName + +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId + +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/main.parameters.json b/Environments/Contoso-Base-Shared-ACA-Prod/main.parameters.json new file mode 100644 index 00000000..8f7787be --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/main.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/manifest.yaml b/Environments/Contoso-Base-Shared-ACA-Prod/manifest.yaml new file mode 100644 index 00000000..90c5fb6d --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Prod/manifest.yaml @@ -0,0 +1,19 @@ +name: Contoso-Base-Shared-ACA-Prod +version: 1.0.0 +summary: Contoso-Base-Shared-ACA-Prod +description: Deploys base shared env (Container environment and monitoring) for app development in ACA +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true diff --git a/Environments/Contoso-Base-Shared-ACA-Test/abbreviations.json b/Environments/Contoso-Base-Shared-ACA-Test/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Test/app/api.bicep b/Environments/Contoso-Base-Shared-ACA-Test/app/api.bicep new file mode 100644 index 00000000..1df68145 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/app/api.bicep @@ -0,0 +1,75 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param keyVaultName string +param serviceName string = 'api' +param corsAcaUrl string +param exists bool + +resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +// Give the API access to KeyVault +module apiKeyVaultAccess '../core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: apiIdentity.properties.principalId + } +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + dependsOn: [ apiKeyVaultAccess ] + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: apiIdentity.name + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: '1.0' + containerMemory: '2.0Gi' + env: [ + { + name: 'AZURE_CLIENT_ID' + value: apiIdentity.properties.clientId + } + { + name: 'AZURE_KEY_VAULT_ENDPOINT' + value: keyVault.properties.vaultUri + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'API_ALLOW_ORIGINS' + value: corsAcaUrl + } + ] + targetPort: 3100 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId +output SERVICE_API_NAME string = app.outputs.name +output SERVICE_API_URI string = app.outputs.uri +output SERVICE_API_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Contoso-Base-Shared-ACA-Test/app/apim-api-policy.xml b/Environments/Contoso-Base-Shared-ACA-Test/app/apim-api-policy.xml new file mode 100644 index 00000000..d9ac2b2d --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/app/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
    *
    +
    + +
    *
    +
    +
    + + + + + + + Call to the @(context.Api.Name) + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
    diff --git a/Environments/Contoso-Base-Shared-ACA-Test/app/apim-api.bicep b/Environments/Contoso-Base-Shared-ACA-Test/app/apim-api.bicep new file mode 100644 index 00000000..b6008404 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/app/apim-api.bicep @@ -0,0 +1,120 @@ +param name string + +@description('Resource name to uniquely identify this API within the API Management service instance') +@minLength(1) +param apiName string + +@description('The Display Name of the API') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Absolute URL of the web frontend') +param webFrontendUrl string + +@description('Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Resource name for backend Web App or Function App') +param apiAppName string = '' + +var apiPolicyContent = replace(loadTextContent('./apim-api-policy.xml'), '{origin}', webFrontendUrl) + +resource restApi 'Microsoft.ApiManagement/service/apis@2021-12-01-preview' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: [ 'https' ] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('./openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2021-12-01-preview' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: name +} + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-03-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' existing = { + name: 'app-insights-logger' + parent: apimService +} + +output SERVICE_API_URI string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/Environments/Contoso-Base-Shared-ACA-Test/app/db.bicep b/Environments/Contoso-Base-Shared-ACA-Test/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-ACA-Test/app/openapi.yaml b/Environments/Contoso-Base-Shared-ACA-Test/app/openapi.yaml new file mode 100644 index 00000000..c42e99ca --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/app/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid diff --git a/Environments/Contoso-Base-Shared-ACA-Test/app/web.bicep b/Environments/Contoso-Base-Shared-ACA-Test/app/web.bicep new file mode 100644 index 00000000..2c692278 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/app/web.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param identityName string +param apiBaseUrl string +param applicationInsightsName string +param containerAppsEnvironmentName string +param containerRegistryName string +param serviceName string = 'web' +param exists bool + +resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module app '../core/host/container-app-upsert.bicep' = { + name: '${serviceName}-container-app' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identityType: 'UserAssigned' + identityName: identityName + exists: exists + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + env: [ + { + name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + { + name: 'REACT_APP_API_BASE_URL' + value: apiBaseUrl + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: applicationInsights.properties.ConnectionString + } + ] + targetPort: 80 + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId +output SERVICE_WEB_NAME string = app.outputs.name +output SERVICE_WEB_URI string = app.outputs.uri +output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName diff --git a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json new file mode 100644 index 00000000..94f1cb0a --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json @@ -0,0 +1,1917 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "7010794115259852587" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "apiContainerAppNameOrDefault": "[format('{0}web-{1}', variables('abbrs').appContainerApps, variables('resourceToken'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-apps", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "app" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "containerAppsEnvironmentName": "[if(not(empty(parameters('containerAppsEnvironmentName'))), createObject('value', parameters('containerAppsEnvironmentName')), createObject('value', format('{0}{1}', variables('abbrs').appManagedEnvironments, variables('resourceToken'))))]", + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10266908178814002202" + }, + "description": "Creates an Azure Container Registry and an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containerAppsEnvironmentName": { + "type": "string" + }, + "logAnalyticsWorkspaceName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-apps-environment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "value": "[parameters('applicationInsightsName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4766903245227392386" + }, + "description": "Creates an Azure Container Apps environment." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Application Insights resource" + } + }, + "daprEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Specifies if Dapr is enabled" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name of the Log Analytics workspace" + } + } + }, + "resources": [ + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('daprEnabled'), not(empty(parameters('applicationInsightsName')))), reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey, '')]" + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2023-04-01-preview').defaultDomain]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "defaultDomain": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.name.value]" + }, + "environmentId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-container-apps-environment', parameters('name'))), '2022-09-01').outputs.id.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "API_CORS_ACA_URL": { + "type": "string", + "value": "[format('https://{0}.{1}', variables('apiContainerAppNameOrDefault'), reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.defaultDomain.value)]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "APPLICATIONINSIGHTS_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsName.value]" + }, + "AZURE_CONTAINER_ENVIRONMENT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-apps'), '2022-09-01').outputs.environmentName.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps-environment.bicep b/Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps.bicep b/Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps.bicep new file mode 100644 index 00000000..b180d264 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/core/host/container-apps.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id diff --git a/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights-dashboard.bicep b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights.bicep b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/loganalytics.bicep b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/monitoring.bicep b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Contoso-Base-Shared-ACA-Test/main.bicep b/Environments/Contoso-Base-Shared-ACA-Test/main.bicep new file mode 100644 index 00000000..8864f5ca --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/main.bicep @@ -0,0 +1,59 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param containerAppsEnvironmentName string = '' +param logAnalyticsName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } +var apiContainerAppNameOrDefault = '${abbrs.appContainerApps}web-${resourceToken}' +var corsAcaUrl = 'https://${apiContainerAppNameOrDefault}.${containerApps.outputs.defaultDomain}' + +// Container apps host (including container registry) +module containerApps './core/host/container-apps.bicep' = { + name: 'container-apps' + params: { + name: 'app' + location: location + tags: tags + containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output API_CORS_ACA_URL string = corsAcaUrl +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName +output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName + +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId + +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Contoso-Base-Shared-ACA-Test/main.parameters.json b/Environments/Contoso-Base-Shared-ACA-Test/main.parameters.json new file mode 100644 index 00000000..8f7787be --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/main.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-ACA-Test/manifest.yaml b/Environments/Contoso-Base-Shared-ACA-Test/manifest.yaml new file mode 100644 index 00000000..e0e54513 --- /dev/null +++ b/Environments/Contoso-Base-Shared-ACA-Test/manifest.yaml @@ -0,0 +1,19 @@ +name: Contoso-Base-Shared-ACA-Test +version: 1.0.0 +summary: Contoso-Base-Shared-ACA-Test +description: Deploys base shared env (Container environment and monitoring) for app development in ACA +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/abbreviations.json b/Environments/Contoso-Base-Shared-AKS-Dev/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/app/db.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json new file mode 100644 index 00000000..0b774988 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json @@ -0,0 +1,2757 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11426766062419716265" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the AKS cluster" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the Container Registry (ACR)" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13278918148041449332" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name for the Azure container registry (ACR)" + } + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the connected log analytics workspace" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "addOns": { + "type": "object", + "defaultValue": { + "azurePolicy": { + "enabled": true, + "config": { + "version": "v2" + } + }, + "keyVault": { + "enabled": true, + "config": { + "enableSecretRotation": "true", + "rotationPollInterval": "2m" + } + }, + "openServiceMesh": { + "enabled": false, + "config": {} + }, + "omsAgent": { + "enabled": true, + "config": {} + }, + "applicationGateway": { + "enabled": false, + "config": {} + } + }, + "metadata": { + "description": "AKS add-ons configuration" + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "systemPoolType": { + "type": "string", + "defaultValue": "CostOptimised", + "allowedValues": [ + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The System Pool Preset sizing" + } + }, + "agentPoolType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The User Pool Preset sizing" + } + }, + "systemPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of system node pool" + } + }, + "agentPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of user node pool" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + } + }, + "variables": { + "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", + "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", + "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", + "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", + "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", + "nodePoolBase": { + "osType": "Linux", + "maxPods": 30, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "33%" + } + }, + "nodePoolPresets": { + "CostOptimised": { + "vmSize": "Standard_B4ms", + "count": 1, + "minCount": 1, + "maxCount": 3, + "enableAutoScaling": true, + "availabilityZones": [] + }, + "Standard": { + "vmSize": "Standard_DS2_v2", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + }, + "HighSpec": { + "vmSize": "Standard_D4s_v3", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "managed-cluster", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" + }, + "nodeResourceGroupName": { + "value": "[parameters('nodeResourceGroupName')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "dnsPrefix": { + "value": "[parameters('dnsPrefix')]" + }, + "kubernetesVersion": { + "value": "[parameters('kubernetesVersion')]" + }, + "addOns": { + "value": "[variables('addOnsConfig')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]", + "enableAad": { + "value": "[and(parameters('enableAzureRbac'), not(equals(parameters('aadTenantId'), '')))]" + }, + "disableLocalAccounts": { + "value": "[parameters('disableLocalAccounts')]" + }, + "aadTenantId": { + "value": "[parameters('aadTenantId')]" + }, + "enableRbac": { + "value": "[parameters('enableRbac')]" + }, + "enableAzureRbac": { + "value": "[parameters('enableAzureRbac')]" + }, + "webAppRoutingAddon": { + "value": "[parameters('webAppRoutingAddon')]" + }, + "loadBalancerSku": { + "value": "[parameters('loadBalancerSku')]" + }, + "networkPlugin": { + "value": "[parameters('networkPlugin')]" + }, + "networkPolicy": { + "value": "[parameters('networkPolicy')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8184151159222198677" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-10-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPolicy": "[parameters('networkPolicy')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webAppRoutingAddon')]" + } + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.resourceId]" + } + } + } + } + } + }, + { + "condition": "[variables('hasAgentPool')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks-node-pool", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "name": { + "value": "npuserpool" + }, + "config": { + "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6072270897511874144" + }, + "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "name": { + "type": "string", + "metadata": { + "description": "The agent pool name" + } + }, + "config": { + "type": "object", + "metadata": { + "description": "The agent pool configuration" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2023-10-02-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": "[parameters('config')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-container-registry-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "condition": "[or(parameters('enableAzureRbac'), parameters('disableLocalAccounts'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8205013527918052324" + }, + "description": "Assigns RBAC role to the specified AKS cluster and principal." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "aksClusterAdminRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('aksClusterAdminRole'))]", + "properties": { + "roleDefinitionId": "[variables('aksClusterAdminRole')]", + "principalType": "User", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The resource name of the ACR" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The login server for the container registry" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_AKS_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/ai/cognitiveservices.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/postgresql/flexibleserver.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/sqlserver/sqlserver.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/gateway/apim.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-agent-pool.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-managed-cluster.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep new file mode 100644 index 00000000..4a36262c --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep @@ -0,0 +1,268 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice-appsettings.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appserviceplan.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app-upsert.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps-environment.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-registry.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/functions.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/staticwebapp.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights-dashboard.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/loganalytics.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/monitoring.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-endpoint.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-profile.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/search/search-services.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/security/aks-managed-cluster-access.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-access.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-secret.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/security/registry-access.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/security/role.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/storage/storage-account.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/testing/loadtesting.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/main.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/main.bicep new file mode 100644 index 00000000..c283ef31 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/main.bicep @@ -0,0 +1,55 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +@description('The resource name of the AKS cluster') +param clusterName string = '' + +@description('The resource name of the Container Registry (ACR)') +param containerRegistryName string = '' + +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param logAnalyticsName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// The AKS cluster to host applications +module aks './core/host/aks.bicep' = { + name: 'aks' + params: { + location: location + name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName +output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/main.parameters.json b/Environments/Contoso-Base-Shared-AKS-Dev/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/manifest.yaml b/Environments/Contoso-Base-Shared-AKS-Dev/manifest.yaml new file mode 100644 index 00000000..2ea8e09d --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Dev/manifest.yaml @@ -0,0 +1,21 @@ +name: Contoso-Base-Shared-AKS-Dev +version: 1.0.0 +summary: Base shared environment for app development in AKS +description: Deploys base shared env (Container environment and monitoring) for app development in AKS +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + + diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/abbreviations.json b/Environments/Contoso-Base-Shared-AKS-Prod/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/app/db.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json new file mode 100644 index 00000000..0b774988 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json @@ -0,0 +1,2757 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11426766062419716265" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the AKS cluster" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the Container Registry (ACR)" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13278918148041449332" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name for the Azure container registry (ACR)" + } + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the connected log analytics workspace" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "addOns": { + "type": "object", + "defaultValue": { + "azurePolicy": { + "enabled": true, + "config": { + "version": "v2" + } + }, + "keyVault": { + "enabled": true, + "config": { + "enableSecretRotation": "true", + "rotationPollInterval": "2m" + } + }, + "openServiceMesh": { + "enabled": false, + "config": {} + }, + "omsAgent": { + "enabled": true, + "config": {} + }, + "applicationGateway": { + "enabled": false, + "config": {} + } + }, + "metadata": { + "description": "AKS add-ons configuration" + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "systemPoolType": { + "type": "string", + "defaultValue": "CostOptimised", + "allowedValues": [ + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The System Pool Preset sizing" + } + }, + "agentPoolType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The User Pool Preset sizing" + } + }, + "systemPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of system node pool" + } + }, + "agentPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of user node pool" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + } + }, + "variables": { + "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", + "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", + "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", + "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", + "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", + "nodePoolBase": { + "osType": "Linux", + "maxPods": 30, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "33%" + } + }, + "nodePoolPresets": { + "CostOptimised": { + "vmSize": "Standard_B4ms", + "count": 1, + "minCount": 1, + "maxCount": 3, + "enableAutoScaling": true, + "availabilityZones": [] + }, + "Standard": { + "vmSize": "Standard_DS2_v2", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + }, + "HighSpec": { + "vmSize": "Standard_D4s_v3", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "managed-cluster", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" + }, + "nodeResourceGroupName": { + "value": "[parameters('nodeResourceGroupName')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "dnsPrefix": { + "value": "[parameters('dnsPrefix')]" + }, + "kubernetesVersion": { + "value": "[parameters('kubernetesVersion')]" + }, + "addOns": { + "value": "[variables('addOnsConfig')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]", + "enableAad": { + "value": "[and(parameters('enableAzureRbac'), not(equals(parameters('aadTenantId'), '')))]" + }, + "disableLocalAccounts": { + "value": "[parameters('disableLocalAccounts')]" + }, + "aadTenantId": { + "value": "[parameters('aadTenantId')]" + }, + "enableRbac": { + "value": "[parameters('enableRbac')]" + }, + "enableAzureRbac": { + "value": "[parameters('enableAzureRbac')]" + }, + "webAppRoutingAddon": { + "value": "[parameters('webAppRoutingAddon')]" + }, + "loadBalancerSku": { + "value": "[parameters('loadBalancerSku')]" + }, + "networkPlugin": { + "value": "[parameters('networkPlugin')]" + }, + "networkPolicy": { + "value": "[parameters('networkPolicy')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8184151159222198677" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-10-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPolicy": "[parameters('networkPolicy')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webAppRoutingAddon')]" + } + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.resourceId]" + } + } + } + } + } + }, + { + "condition": "[variables('hasAgentPool')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks-node-pool", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "name": { + "value": "npuserpool" + }, + "config": { + "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6072270897511874144" + }, + "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "name": { + "type": "string", + "metadata": { + "description": "The agent pool name" + } + }, + "config": { + "type": "object", + "metadata": { + "description": "The agent pool configuration" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2023-10-02-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": "[parameters('config')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-container-registry-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "condition": "[or(parameters('enableAzureRbac'), parameters('disableLocalAccounts'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8205013527918052324" + }, + "description": "Assigns RBAC role to the specified AKS cluster and principal." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "aksClusterAdminRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('aksClusterAdminRole'))]", + "properties": { + "roleDefinitionId": "[variables('aksClusterAdminRole')]", + "principalType": "User", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The resource name of the ACR" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The login server for the container registry" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_AKS_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/ai/cognitiveservices.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/postgresql/flexibleserver.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/sqlserver/sqlserver.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/gateway/apim.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-agent-pool.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-managed-cluster.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks.bicep new file mode 100644 index 00000000..4a36262c --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/aks.bicep @@ -0,0 +1,268 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice-appsettings.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appserviceplan.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app-upsert.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps-environment.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-registry.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/functions.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/host/staticwebapp.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights-dashboard.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/loganalytics.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/monitoring.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-endpoint.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-profile.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/search/search-services.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/security/aks-managed-cluster-access.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-access.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-secret.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/security/registry-access.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/security/role.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/storage/storage-account.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/testing/loadtesting.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/main.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/main.bicep new file mode 100644 index 00000000..c283ef31 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/main.bicep @@ -0,0 +1,55 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +@description('The resource name of the AKS cluster') +param clusterName string = '' + +@description('The resource name of the Container Registry (ACR)') +param containerRegistryName string = '' + +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param logAnalyticsName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// The AKS cluster to host applications +module aks './core/host/aks.bicep' = { + name: 'aks' + params: { + location: location + name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName +output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/main.parameters.json b/Environments/Contoso-Base-Shared-AKS-Prod/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/manifest.yaml b/Environments/Contoso-Base-Shared-AKS-Prod/manifest.yaml new file mode 100644 index 00000000..a8cb75cd --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Prod/manifest.yaml @@ -0,0 +1,21 @@ +name: Contoso-Base-Shared-AKS-Prod +version: 1.0.0 +summary: Base shared environment for app development in AKS +description: Deploys base shared env (Container environment and monitoring) for app development in AKS +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + + diff --git a/Environments/Contoso-Base-Shared-AKS-Test/abbreviations.json b/Environments/Contoso-Base-Shared-AKS-Test/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Test/app/db.bicep b/Environments/Contoso-Base-Shared-AKS-Test/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json new file mode 100644 index 00000000..0b774988 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json @@ -0,0 +1,2757 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11426766062419716265" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the AKS cluster" + } + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource name of the Container Registry (ACR)" + } + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13278918148041449332" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name for the Azure container registry (ACR)" + } + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the connected log analytics workspace" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "addOns": { + "type": "object", + "defaultValue": { + "azurePolicy": { + "enabled": true, + "config": { + "version": "v2" + } + }, + "keyVault": { + "enabled": true, + "config": { + "enableSecretRotation": "true", + "rotationPollInterval": "2m" + } + }, + "openServiceMesh": { + "enabled": false, + "config": {} + }, + "omsAgent": { + "enabled": true, + "config": {} + }, + "applicationGateway": { + "enabled": false, + "config": {} + } + }, + "metadata": { + "description": "AKS add-ons configuration" + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "systemPoolType": { + "type": "string", + "defaultValue": "CostOptimised", + "allowedValues": [ + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The System Pool Preset sizing" + } + }, + "agentPoolType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CostOptimised", + "Standard", + "HighSpec", + "Custom" + ], + "metadata": { + "description": "The User Pool Preset sizing" + } + }, + "systemPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of system node pool" + } + }, + "agentPoolConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom configuration of user node pool" + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + } + }, + "variables": { + "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", + "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", + "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", + "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", + "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", + "nodePoolBase": { + "osType": "Linux", + "maxPods": 30, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "33%" + } + }, + "nodePoolPresets": { + "CostOptimised": { + "vmSize": "Standard_B4ms", + "count": 1, + "minCount": 1, + "maxCount": 3, + "enableAutoScaling": true, + "availabilityZones": [] + }, + "Standard": { + "vmSize": "Standard_DS2_v2", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + }, + "HighSpec": { + "vmSize": "Standard_D4s_v3", + "count": 3, + "minCount": 3, + "maxCount": 5, + "enableAutoScaling": true, + "availabilityZones": [ + "1", + "2", + "3" + ] + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "managed-cluster", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" + }, + "nodeResourceGroupName": { + "value": "[parameters('nodeResourceGroupName')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "dnsPrefix": { + "value": "[parameters('dnsPrefix')]" + }, + "kubernetesVersion": { + "value": "[parameters('kubernetesVersion')]" + }, + "addOns": { + "value": "[variables('addOnsConfig')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]", + "enableAad": { + "value": "[and(parameters('enableAzureRbac'), not(equals(parameters('aadTenantId'), '')))]" + }, + "disableLocalAccounts": { + "value": "[parameters('disableLocalAccounts')]" + }, + "aadTenantId": { + "value": "[parameters('aadTenantId')]" + }, + "enableRbac": { + "value": "[parameters('enableRbac')]" + }, + "enableAzureRbac": { + "value": "[parameters('enableAzureRbac')]" + }, + "webAppRoutingAddon": { + "value": "[parameters('webAppRoutingAddon')]" + }, + "loadBalancerSku": { + "value": "[parameters('loadBalancerSku')]" + }, + "networkPlugin": { + "value": "[parameters('networkPlugin')]" + }, + "networkPolicy": { + "value": "[parameters('networkPolicy')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8184151159222198677" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "kubernetesVersion": { + "type": "string", + "defaultValue": "1.27.7", + "metadata": { + "description": "Kubernetes Version" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "[tenant().tenantId]", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "networkPolicy": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "calico" + ], + "metadata": { + "description": "Network policy used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-10-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPolicy": "[parameters('networkPolicy')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webAppRoutingAddon')]" + } + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.resourceId]" + } + } + } + } + } + }, + { + "condition": "[variables('hasAgentPool')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks-node-pool", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "name": { + "value": "npuserpool" + }, + "config": { + "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6072270897511874144" + }, + "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "name": { + "type": "string", + "metadata": { + "description": "The agent pool name" + } + }, + "config": { + "type": "object", + "metadata": { + "description": "The agent pool configuration" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2023-10-02-preview", + "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", + "properties": "[parameters('config')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12834334744516280883" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-container-registry-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "15144906240959446537" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + }, + { + "condition": "[or(parameters('enableAzureRbac'), parameters('disableLocalAccounts'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "8205013527918052324" + }, + "description": "Assigns RBAC role to the specified AKS cluster and principal." + }, + "parameters": { + "clusterName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "aksClusterAdminRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('aksClusterAdminRole'))]", + "properties": { + "roleDefinitionId": "[variables('aksClusterAdminRole')]", + "principalType": "User", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The resource name of the ACR" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The login server for the container registry" + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitoring", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", + "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "10041669792322197047" + }, + "description": "Creates an Application Insights instance and a Log Analytics workspace." + }, + "parameters": { + "logAnalyticsName": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "applicationInsightsDashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "loganalytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('logAnalyticsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "9622176141085970536" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "applicationinsights", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationInsightsName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "dashboardName": { + "value": "[parameters('applicationInsightsDashboardName')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "1335628967363670282" + }, + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "dashboardName": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "2145880658446193205" + }, + "description": "Creates a dashboard for an Application Insights instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "applicationInsightsName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + }, + "instrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" + ] + } + ], + "outputs": { + "applicationInsightsConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + }, + "applicationInsightsName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + } + } + } + } + } + ], + "outputs": { + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_AKS_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + }, + "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/ai/cognitiveservices.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/postgresql/flexibleserver.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000..7e26b1a8 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/sqlserver/sqlserver.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 00000000..84f2cc2c --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/gateway/apim.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/gateway/apim.bicep new file mode 100644 index 00000000..be7464f0 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-agent-pool.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-agent-pool.bicep new file mode 100644 index 00000000..9c764358 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-managed-cluster.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-managed-cluster.bicep new file mode 100644 index 00000000..de562a66 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks.bicep new file mode 100644 index 00000000..4a36262c --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/aks.bicep @@ -0,0 +1,268 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +@description('Kubernetes Version') +param kubernetesVersion string = '1.27.7' + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = tenant().tenantId + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + nodeResourceGroupName: nodeResourceGroupName + sku: sku + dnsPrefix: dnsPrefix + kubernetesVersion: kubernetesVersion + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + enableAad: enableAzureRbac && aadTenantId != '' + disableLocalAccounts: disableLocalAccounts + aadTenantId: aadTenantId + enableRbac: enableRbac + enableAzureRbac: enableAzureRbac + webAppRoutingAddon: webAppRoutingAddon + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give AKS cluster access to the specified principal +module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { + name: 'cluster-access' + params: { + clusterName: managedCluster.outputs.clusterName + principalId: principalId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice-appsettings.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000..f4b22f81 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice.bicep new file mode 100644 index 00000000..bef4d2ba --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/appservice.bicep @@ -0,0 +1,123 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/appserviceplan.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/appserviceplan.bicep new file mode 100644 index 00000000..2e37e041 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app-upsert.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app-upsert.bicep new file mode 100644 index 00000000..3eec62f2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app.bicep new file mode 100644 index 00000000..3724086d --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps-environment.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps-environment.bicep new file mode 100644 index 00000000..8633ba48 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps.bicep new file mode 100644 index 00000000..1c656e28 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-apps.bicep @@ -0,0 +1,40 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param containerRegistryAdminUserEnabled bool = false +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + adminUserEnabled: containerRegistryAdminUserEnabled + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-registry.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/functions.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/functions.bicep new file mode 100644 index 00000000..7070a2c6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/host/staticwebapp.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/host/staticwebapp.bicep new file mode 100644 index 00000000..cedaf906 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights-dashboard.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000..d082e668 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000..4b4d01e3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string = '' +param location string = resourceGroup().location +param tags object = {} +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/loganalytics.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/monitoring.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/monitoring.bicep new file mode 100644 index 00000000..6bb05b0b --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/monitor/monitoring.bicep @@ -0,0 +1,32 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string = '' +param location string = resourceGroup().location +param tags object = {} + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-endpoint.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-endpoint.bicep new file mode 100644 index 00000000..5e8ab695 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-profile.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-profile.bicep new file mode 100644 index 00000000..27669ee2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn.bicep new file mode 100644 index 00000000..de98a1f9 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/search/search-services.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/search/search-services.bicep new file mode 100644 index 00000000..d9c619a9 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/search/search-services.bicep @@ -0,0 +1,68 @@ +metadata description = 'Creates an Azure AI Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +var searchIdentityProvider = (sku.name == 'free') ? null : { + type: 'SystemAssigned' +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + // The free tier does not support managed identity + identity: searchIdentityProvider + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name +output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' + diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/security/aks-managed-cluster-access.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-access.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-secret.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/security/registry-access.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/security/role.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/storage/storage-account.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/storage/storage-account.bicep new file mode 100644 index 00000000..4b6febbe --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/testing/loadtesting.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/testing/loadtesting.bicep new file mode 100644 index 00000000..46781086 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/Environments/Contoso-Base-Shared-AKS-Test/main.bicep b/Environments/Contoso-Base-Shared-AKS-Test/main.bicep new file mode 100644 index 00000000..c283ef31 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/main.bicep @@ -0,0 +1,55 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +@description('The resource name of the AKS cluster') +param clusterName string = '' + +@description('The resource name of the Container Registry (ACR)') +param containerRegistryName string = '' + +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param logAnalyticsName string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// The AKS cluster to host applications +module aks './core/host/aks.bicep' = { + name: 'aks' + params: { + location: location + name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName +output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer +output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName +output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString diff --git a/Environments/Contoso-Base-Shared-AKS-Test/main.parameters.json b/Environments/Contoso-Base-Shared-AKS-Test/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/Contoso-Base-Shared-AKS-Test/manifest.yaml b/Environments/Contoso-Base-Shared-AKS-Test/manifest.yaml new file mode 100644 index 00000000..2ceb185f --- /dev/null +++ b/Environments/Contoso-Base-Shared-AKS-Test/manifest.yaml @@ -0,0 +1,21 @@ +name: Contoso-Base-Shared-AKS-Test +version: 1.0.0 +summary: Base shared environment for app development in AKS +description: Deploys base shared env (Container environment and monitoring) for app development in AKS +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + + From 862e86f861bdec472c119ce24f5f2fac8fe54cde Mon Sep 17 00:00:00 2001 From: Mark Weitzel Date: Sun, 3 Mar 2024 23:09:02 -0500 Subject: [PATCH 075/112] updated aks --- .../App-Base-WebApp-AKS/azuredeploy.json | 3422 ++--------------- .../core/ai/cognitiveservices.bicep | 53 - .../database/postgresql/flexibleserver.bicep | 65 - .../core/database/sqlserver/sqlserver.bicep | 130 - .../core/gateway/apim.bicep | 79 - .../core/host/aks-agent-pool.bicep | 18 - .../core/host/aks-managed-cluster.bicep | 140 - .../App-Base-WebApp-AKS/core/host/aks.bicep | 280 -- .../core/host/appservice-appsettings.bicep | 17 - .../core/host/appservice.bicep | 123 - .../core/host/appserviceplan.bicep | 22 - .../core/host/container-app-upsert.bicep | 105 - .../core/host/container-app.bicep | 162 - .../host/container-apps-environment.bicep | 41 - .../core/host/container-apps.bicep | 40 - .../core/host/container-registry.bicep | 83 - .../core/host/functions.bicep | 86 - .../core/host/staticwebapp.bicep | 22 - .../applicationinsights-dashboard.bicep | 1236 ------ .../core/monitor/applicationinsights.bicep | 30 - .../core/monitor/loganalytics.bicep | 22 - .../core/monitor/monitoring.bicep | 32 - .../core/networking/cdn-endpoint.bicep | 52 - .../core/networking/cdn-profile.bicep | 34 - .../core/networking/cdn.bicep | 42 - .../core/search/search-services.bicep | 68 - .../core/storage/storage-account.bicep | 64 - .../core/testing/loadtesting.bicep | 15 - Environments/App-Base-WebApp-AKS/main.bicep | 48 +- .../App-Base-WebApp-AKS/manifest.yaml | 15 +- 30 files changed, 434 insertions(+), 6112 deletions(-) delete mode 100644 Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/aks.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/appservice.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-app.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/functions.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/search/search-services.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep delete mode 100644 Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json index 9626594f..84d2235d 100644 --- a/Environments/App-Base-WebApp-AKS/azuredeploy.json +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "13440997233145271475" + "templateHash": "2845459636497042502" } }, "parameters": { @@ -19,33 +19,12 @@ }, "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "minLength": 1, "metadata": { "description": "Primary location for all resources" } }, - "clusterName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The resource name of the AKS cluster" - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The resource name of the Container Registry (ACR)" - } - }, - "applicationInsightsDashboardName": { - "type": "string", - "defaultValue": "" - }, - "applicationInsightsName": { - "type": "string", - "defaultValue": "" - }, "cosmosAccountName": { "type": "string", "defaultValue": "" @@ -58,16 +37,12 @@ "type": "string", "defaultValue": "" }, - "logAnalyticsName": { + "principalId": { "type": "string", "defaultValue": "" }, - "principalId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Id of the user or app to assign application roles" - } + "aksClusterIdentityObjectId": { + "type": "string" } }, "variables": { @@ -217,20 +192,22 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "aks", + "name": "cosmos", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, "location": { "value": "[parameters('location')]" }, - "name": "[if(not(empty(parameters('clusterName'))), createObject('value', parameters('clusterName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", - "containerRegistryName": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", - "logAnalyticsName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + "tags": { + "value": "[variables('tags')]" }, "keyVaultName": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" @@ -243,348 +220,78 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "4109929557657560885" - }, - "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." + "templateHash": "5730728686647632614" + } }, "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "The name for the AKS managed cluster" - } - }, - "containerRegistryName": { - "type": "string", - "metadata": { - "description": "The name for the Azure container registry (ACR)" - } - }, - "logAnalyticsName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the connected log analytics workspace" - } - }, - "keyVaultName": { - "type": "string", - "metadata": { - "description": "The name of the keyvault to grant access" - } + "accountName": { + "type": "string" }, "location": { "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "The Azure region/location for the AKS resources" - } + "defaultValue": "[resourceGroup().location]" }, "tags": { "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Custom tags to apply to the AKS resources" - } + "defaultValue": {} }, - "addOns": { - "type": "object", - "defaultValue": { - "azurePolicy": { - "enabled": true, - "config": { - "version": "v2" - } - }, - "keyVault": { - "enabled": true, - "config": { - "enableSecretRotation": "true", - "rotationPollInterval": "2m" - } - }, - "openServiceMesh": { - "enabled": false, - "config": {} - }, - "omsAgent": { - "enabled": true, - "config": {} + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" }, - "applicationGateway": { - "enabled": false, - "config": {} + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" } - }, - "metadata": { - "description": "AKS add-ons configuration" - } - }, - "sku": { - "type": "string", - "defaultValue": "Free", - "allowedValues": [ - "Free", - "Paid", - "Standard" - ], - "metadata": { - "description": "The managed cluster SKU." - } - }, - "loadBalancerSku": { - "type": "string", - "defaultValue": "standard", - "allowedValues": [ - "basic", - "standard" - ], - "metadata": { - "description": "The load balancer SKU to use for ingress into the AKS cluster" - } - }, - "networkPlugin": { - "type": "string", - "defaultValue": "azure", - "allowedValues": [ - "azure", - "kubenet", - "none" - ], - "metadata": { - "description": "Network plugin used for building the Kubernetes network." - } - }, - "networkPolicy": { - "type": "string", - "defaultValue": "azure", - "allowedValues": [ - "azure", - "calico" - ], - "metadata": { - "description": "Network policy used for building the Kubernetes network." - } - }, - "dnsPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The DNS prefix to associate with the AKS cluster" - } - }, - "nodeResourceGroupName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the resource group for the managed resources of the AKS cluster" - } - }, - "systemPoolType": { - "type": "string", - "defaultValue": "CostOptimised", - "allowedValues": [ - "CostOptimised", - "Standard", - "HighSpec", - "Custom" - ], - "metadata": { - "description": "The System Pool Preset sizing" - } - }, - "agentPoolType": { - "type": "string", - "defaultValue": "", - "allowedValues": [ - "", - "CostOptimised", - "Standard", - "HighSpec", - "Custom" - ], - "metadata": { - "description": "The User Pool Preset sizing" - } - }, - "systemPoolConfig": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Custom configuration of system node pool" - } - }, - "agentPoolConfig": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Custom configuration of user node pool" - } - }, - "principalId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Id of the user or app to assign application roles" - } - }, - "kubernetesVersion": { - "type": "string", - "defaultValue": "1.27.7", - "metadata": { - "description": "Kubernetes Version" - } + ] }, - "aadTenantId": { + "databaseName": { "type": "string", - "defaultValue": "[tenant().tenantId]", - "metadata": { - "description": "The Tenant ID associated to the Azure Active Directory" - } - }, - "enableRbac": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Whether RBAC is enabled for local accounts" - } - }, - "disableLocalAccounts": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "If set to true, getting static credentials will be disabled for this cluster." - } - }, - "enableAzureRbac": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable RBAC using AAD" - } + "defaultValue": "" }, - "webAppRoutingAddon": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Whether web app routing (preview) add-on is enabled" - } + "keyVaultName": { + "type": "string" } }, "variables": { - "omsAgentConfig": "[if(and(and(not(empty(parameters('logAnalyticsName'))), not(empty(parameters('addOns').omsAgent))), parameters('addOns').omsAgent.enabled), union(parameters('addOns').omsAgent, createObject('config', createObject('logAnalyticsWorkspaceResourceID', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))))), createObject())]", - "addOnsConfig": "[union(if(and(not(empty(parameters('addOns').azurePolicy)), parameters('addOns').azurePolicy.enabled), createObject('azurepolicy', parameters('addOns').azurePolicy), createObject()), if(and(not(empty(parameters('addOns').keyVault)), parameters('addOns').keyVault.enabled), createObject('azureKeyvaultSecretsProvider', parameters('addOns').keyVault), createObject()), if(and(not(empty(parameters('addOns').openServiceMesh)), parameters('addOns').openServiceMesh.enabled), createObject('openServiceMesh', parameters('addOns').openServiceMesh), createObject()), if(and(not(empty(parameters('addOns').omsAgent)), parameters('addOns').omsAgent.enabled), createObject('omsagent', variables('omsAgentConfig')), createObject()), if(and(not(empty(parameters('addOns').applicationGateway)), parameters('addOns').applicationGateway.enabled), createObject('ingressApplicationGateway', parameters('addOns').applicationGateway), createObject()))]", - "systemPoolSpec": "[if(not(empty(parameters('systemPoolConfig'))), parameters('systemPoolConfig'), variables('nodePoolPresets')[parameters('systemPoolType')])]", - "hasAgentPool": "[or(not(empty(parameters('agentPoolConfig'))), not(empty(parameters('agentPoolType'))))]", - "agentPoolSpec": "[if(and(variables('hasAgentPool'), not(empty(parameters('agentPoolConfig')))), parameters('agentPoolConfig'), if(empty(parameters('agentPoolType')), createObject(), variables('nodePoolPresets')[parameters('agentPoolType')]))]", - "nodePoolBase": { - "osType": "Linux", - "maxPods": 30, - "type": "VirtualMachineScaleSets", - "upgradeSettings": { - "maxSurge": "33%" - } - }, - "nodePoolPresets": { - "CostOptimised": { - "vmSize": "Standard_B4ms", - "count": 1, - "minCount": 1, - "maxCount": 3, - "enableAutoScaling": true, - "availabilityZones": [] - }, - "Standard": { - "vmSize": "Standard_DS2_v2", - "count": 3, - "minCount": 3, - "maxCount": 5, - "enableAutoScaling": true, - "availabilityZones": [ - "1", - "2", - "3" - ] - }, - "HighSpec": { - "vmSize": "Standard_D4s_v3", - "count": 3, - "minCount": 3, - "maxCount": 5, - "enableAutoScaling": true, - "availabilityZones": [ - "1", - "2", - "3" - ] - } - } + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" }, "resources": [ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "managed-cluster", + "name": "cosmos-mongo", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[parameters('name')]" + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" }, "location": { "value": "[parameters('location')]" }, - "tags": { - "value": "[parameters('tags')]" - }, - "systemPoolConfig": { - "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'), variables('systemPoolSpec'))]" - }, - "nodeResourceGroupName": { - "value": "[parameters('nodeResourceGroupName')]" - }, - "sku": { - "value": "[parameters('sku')]" - }, - "dnsPrefix": { - "value": "[parameters('dnsPrefix')]" - }, - "kubernetesVersion": { - "value": "[parameters('kubernetesVersion')]" - }, - "addOns": { - "value": "[variables('addOnsConfig')]" - }, - "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]", - "enableAad": { - "value": "[and(parameters('enableAzureRbac'), not(equals(parameters('aadTenantId'), '')))]" - }, - "disableLocalAccounts": { - "value": "[parameters('disableLocalAccounts')]" - }, - "aadTenantId": { - "value": "[parameters('aadTenantId')]" - }, - "enableRbac": { - "value": "[parameters('enableRbac')]" - }, - "enableAzureRbac": { - "value": "[parameters('enableAzureRbac')]" - }, - "webAppRoutingAddon": { - "value": "[parameters('webAppRoutingAddon')]" - }, - "loadBalancerSku": { - "value": "[parameters('loadBalancerSku')]" + "collections": { + "value": "[parameters('collections')]" }, - "networkPlugin": { - "value": "[parameters('networkPlugin')]" + "keyVaultName": { + "value": "[parameters('keyVaultName')]" }, - "networkPolicy": { - "value": "[parameters('networkPolicy')]" + "tags": { + "value": "[parameters('tags')]" } }, "template": { @@ -594,1437 +301,85 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "8184151159222198677" + "templateHash": "14549161001187918251" }, - "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "The name for the AKS managed cluster" - } + "accountName": { + "type": "string" }, - "nodeResourceGroupName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the resource group for the managed resources of the AKS cluster" - } + "databaseName": { + "type": "string" }, "location": { "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "The Azure region/location for the AKS resources" - } + "defaultValue": "[resourceGroup().location]" }, "tags": { "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Custom tags to apply to the AKS resources" - } - }, - "kubernetesVersion": { - "type": "string", - "defaultValue": "1.27.7", - "metadata": { - "description": "Kubernetes Version" - } - }, - "enableRbac": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Whether RBAC is enabled for local accounts" - } - }, - "webAppRoutingAddon": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Whether web app routing (preview) add-on is enabled" - } - }, - "enableAad": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable Azure Active Directory integration" - } - }, - "enableAzureRbac": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable RBAC using AAD" - } - }, - "aadTenantId": { - "type": "string", - "defaultValue": "[tenant().tenantId]", - "metadata": { - "description": "The Tenant ID associated to the Azure Active Directory" - } - }, - "loadBalancerSku": { - "type": "string", - "defaultValue": "standard", - "allowedValues": [ - "basic", - "standard" - ], - "metadata": { - "description": "The load balancer SKU to use for ingress into the AKS cluster" - } - }, - "networkPlugin": { - "type": "string", - "defaultValue": "azure", - "allowedValues": [ - "azure", - "kubenet", - "none" - ], - "metadata": { - "description": "Network plugin used for building the Kubernetes network." - } - }, - "networkPolicy": { - "type": "string", - "defaultValue": "azure", - "allowedValues": [ - "azure", - "calico" - ], - "metadata": { - "description": "Network policy used for building the Kubernetes network." - } - }, - "disableLocalAccounts": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "If set to true, getting static credentials will be disabled for this cluster." - } - }, - "sku": { - "type": "string", - "defaultValue": "Free", - "allowedValues": [ - "Free", - "Paid", - "Standard" - ], - "metadata": { - "description": "The managed cluster SKU." - } + "defaultValue": {} }, - "addOns": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Configuration of AKS add-ons" - } + "collections": { + "type": "array", + "defaultValue": [] }, - "workspaceId": { + "connectionStringKey": { "type": "string", - "defaultValue": "", - "metadata": { - "description": "The log analytics workspace id used for logging & monitoring" - } - }, - "systemPoolConfig": { - "type": "object", - "metadata": { - "description": "The node pool configuration for the System agent pool" - } + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" }, - "dnsPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The DNS prefix to associate with the AKS cluster" - } + "keyVaultName": { + "type": "string" } }, - "variables": { - "aksDiagCategories": [ - "cluster-autoscaler", - "kube-controller-manager", - "kube-audit-admin", - "guard" - ] - }, "resources": [ { - "type": "Microsoft.ContainerService/managedClusters", - "apiVersion": "2023-10-02-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": { - "type": "SystemAssigned" - }, - "sku": { - "name": "Base", - "tier": "[parameters('sku')]" + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", "properties": { - "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", - "kubernetesVersion": "[parameters('kubernetesVersion')]", - "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", - "enableRBAC": "[parameters('enableRbac')]", - "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", - "agentPoolProfiles": [ - "[parameters('systemPoolConfig')]" - ], - "networkProfile": { - "loadBalancerSku": "[parameters('loadBalancerSku')]", - "networkPlugin": "[parameters('networkPlugin')]", - "networkPolicy": "[parameters('networkPolicy')]" - }, - "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", - "addonProfiles": "[parameters('addOns')]", - "ingressProfile": { - "webAppRouting": { - "enabled": "[parameters('webAppRoutingAddon')]" - } + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] }, { - "condition": "[not(empty(parameters('workspaceId')))]", - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", - "name": "aks-diagnostics", + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", "properties": { - "copy": [ - { - "name": "logs", - "count": "[length(variables('aksDiagCategories'))]", - "input": { - "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", - "enabled": true - } - } - ], - "workspaceId": "[parameters('workspaceId')]", - "metrics": [ - { - "category": "AllMetrics", - "enabled": true - } - ] + "resource": { + "id": "[parameters('databaseName')]" + } }, "dependsOn": [ - "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" ] - } - ], - "outputs": { - "clusterName": { - "type": "string", - "metadata": { - "description": "The resource name of the AKS cluster" - }, - "value": "[parameters('name')]" - }, - "clusterIdentity": { - "type": "object", - "metadata": { - "description": "The AKS cluster identity" - }, - "value": { - "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.clientId]", - "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]", - "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-10-02-preview').identityProfile.kubeletidentity.resourceId]" - } - } - } - } - } - }, - { - "condition": "[variables('hasAgentPool')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "aks-node-pool", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "clusterName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" - }, - "name": { - "value": "npuserpool" - }, - "config": { - "value": "[union(createObject('name', 'npuser', 'mode', 'User'), variables('nodePoolBase'), variables('agentPoolSpec'))]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6072270897511874144" - }, - "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." - }, - "parameters": { - "clusterName": { - "type": "string" - }, - "name": { - "type": "string", - "metadata": { - "description": "The agent pool name" - } - }, - "config": { - "type": "object", - "metadata": { - "description": "The agent pool configuration" - } - } - }, - "resources": [ - { - "type": "Microsoft.ContainerService/managedClusters/agentPools", - "apiVersion": "2023-10-02-preview", - "name": "[format('{0}/{1}', parameters('clusterName'), parameters('name'))]", - "properties": "[parameters('config')]" - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "container-registry", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('containerRegistryName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "workspaceId": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsName'))), createObject('value', ''))]" - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" - }, - "description": "Creates an Azure Container Registry." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "adminUserEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates whether admin user is enabled" - } - }, - "anonymousPullEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates whether anonymous pull is enabled" - } - }, - "dataEndpointEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates whether data endpoint is enabled" - } - }, - "encryption": { - "type": "object", - "defaultValue": { - "status": "disabled" - }, - "metadata": { - "description": "Encryption settings" - } - }, - "networkRuleBypassOptions": { - "type": "string", - "defaultValue": "AzureServices", - "metadata": { - "description": "Options for bypassing network rules" - } - }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "Enabled", - "metadata": { - "description": "Public network access setting" - } - }, - "sku": { - "type": "object", - "defaultValue": { - "name": "Basic" - }, - "metadata": { - "description": "SKU settings" - } - }, - "zoneRedundancy": { - "type": "string", - "defaultValue": "Disabled", - "metadata": { - "description": "Zone redundancy setting" - } - }, - "workspaceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The log analytics workspace ID used for logging and monitoring" - } - } - }, - "resources": [ - { - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2022-02-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", - "properties": { - "adminUserEnabled": "[parameters('adminUserEnabled')]", - "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", - "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", - "encryption": "[parameters('encryption')]", - "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]", - "zoneRedundancy": "[parameters('zoneRedundancy')]" - } - }, - { - "condition": "[not(empty(parameters('workspaceId')))]", - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", - "name": "registry-diagnostics", - "properties": { - "workspaceId": "[parameters('workspaceId')]", - "logs": [ - { - "category": "ContainerRegistryRepositoryEvents", - "enabled": true - }, - { - "category": "ContainerRegistryLoginEvents", - "enabled": true - } - ], - "metrics": [ - { - "category": "AllMetrics", - "enabled": true, - "timeGrain": "PT1M" - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" - ] - } - ], - "outputs": { - "loginServer": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cluster-container-registry-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "containerRegistryName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" - }, - "principalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" - }, - "description": "Assigns ACR Pull permissions to access an Azure Container Registry." - }, - "parameters": { - "containerRegistryName": { - "type": "string" - }, - "principalId": { - "type": "string" - } - }, - "variables": { - "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", - "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", - "properties": { - "roleDefinitionId": "[variables('acrPullRole')]", - "principalType": "ServicePrincipal", - "principalId": "[parameters('principalId')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", - "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" - ] - }, - { - "condition": "[or(parameters('enableAzureRbac'), parameters('disableLocalAccounts'))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cluster-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "clusterName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" - }, - "principalId": { - "value": "[parameters('principalId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8205013527918052324" - }, - "description": "Assigns RBAC role to the specified AKS cluster and principal." - }, - "parameters": { - "clusterName": { - "type": "string" - }, - "principalId": { - "type": "string" - } - }, - "variables": { - "aksClusterAdminRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", - "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('aksClusterAdminRole'))]", - "properties": { - "roleDefinitionId": "[variables('aksClusterAdminRole')]", - "principalType": "User", - "principalId": "[parameters('principalId')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cluster-keyvault-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "principalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value.objectId]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" - }, - "description": "Assigns an Azure Key Vault access policy." - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "add" - }, - "keyVaultName": { - "type": "string" - }, - "permissions": { - "type": "object", - "defaultValue": { - "secrets": [ - "get", - "list" - ] - } - }, - "principalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "properties": { - "accessPolicies": [ - { - "objectId": "[parameters('principalId')]", - "tenantId": "[subscription().tenantId]", - "permissions": "[parameters('permissions')]" - } - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'managed-cluster')]" - ] - } - ], - "outputs": { - "clusterName": { - "type": "string", - "metadata": { - "description": "The resource name of the AKS cluster" - }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterName.value]" - }, - "clusterIdentity": { - "type": "object", - "metadata": { - "description": "The AKS cluster identity" - }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'managed-cluster'), '2022-09-01').outputs.clusterIdentity.value]" - }, - "containerRegistryName": { - "type": "string", - "metadata": { - "description": "The resource name of the ACR" - }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value]" - }, - "containerRegistryLoginServer": { - "type": "string", - "metadata": { - "description": "The login server for the container registry" - }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", - "[resourceId('Microsoft.Resources/deployments', 'monitoring')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", - "databaseName": { - "value": "[parameters('cosmosDatabaseName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" - } - }, - "parameters": { - "accountName": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "collections": { - "type": "array", - "defaultValue": [ - { - "name": "TodoList", - "id": "TodoList", - "shardKey": "Hash", - "indexKey": "_id" - }, - { - "name": "TodoItem", - "id": "TodoItem", - "shardKey": "Hash", - "indexKey": "_id" - } - ] - }, - "databaseName": { - "type": "string", - "defaultValue": "" - }, - "keyVaultName": { - "type": "string" - } - }, - "variables": { - "defaultDatabaseName": "Todo", - "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-mongo", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": { - "value": "[parameters('accountName')]" - }, - "databaseName": { - "value": "[variables('actualDatabaseName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "collections": { - "value": "[parameters('collections')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" - }, - "description": "Creates an Azure Cosmos DB for MongoDB account with a database." - }, - "parameters": { - "accountName": { - "type": "string" - }, - "databaseName": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "collections": { - "type": "array", - "defaultValue": [] - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - }, - "keyVaultName": { - "type": "string" - } - }, - "resources": [ - { - "copy": { - "name": "list", - "count": "[length(parameters('collections'))]" - }, - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", - "properties": { - "resource": { - "id": "[parameters('collections')[copyIndex()].id]", - "shardKey": { - "_id": "[parameters('collections')[copyIndex()].shardKey]" - }, - "indexes": [ - { - "key": { - "keys": [ - "[parameters('collections')[copyIndex()].indexKey]" - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" - ] - }, - { - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": { - "id": "[parameters('databaseName')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-mongo-account", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('accountName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" - }, - "description": "Creates an Azure Cosmos DB for MongoDB account." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "keyVaultName": { - "type": "string" - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-account", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "kind": { - "value": "MongoDB" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" - }, - "description": "Creates an Azure Cosmos DB account." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - }, - "keyVaultName": { - "type": "string" - }, - "kind": { - "type": "string", - "allowedValues": [ - "GlobalDocumentDB", - "MongoDB", - "Parse" - ] - } - }, - "resources": [ - { - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2022-08-15", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "consistencyPolicy": { - "defaultConsistencyLevel": "Session" - }, - "locations": [ - { - "locationName": "[parameters('location')]", - "failoverPriority": 0, - "isZoneRedundant": false - } - ], - "databaseAccountOfferType": "Standard", - "enableAutomaticFailover": false, - "enableMultipleWriteLocations": false, - "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", - "capabilities": [ - { - "name": "EnableServerless" - } - ] - } - }, - { - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", - "properties": { - "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" - }, - "dependsOn": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" - ] - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[parameters('connectionStringKey')]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" - }, - "id": { - "type": "string", - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" - }, - "id": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" - } - } - } - } - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[parameters('connectionStringKey')]" - }, - "databaseName": { - "type": "string", - "value": "[parameters('databaseName')]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" - } - } - } - } - } - ], - "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" - }, - "databaseName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" - }, - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "keyvault", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "principalId": { - "value": "[parameters('principalId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" - }, - "description": "Creates an Azure Key Vault." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "principalId": { - "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" - } - } - ], - "outputs": { - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "monitoring", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "logAnalyticsName": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", - "applicationInsightsName": "[if(not(empty(parameters('applicationInsightsName'))), createObject('value', parameters('applicationInsightsName')), createObject('value', format('{0}{1}', variables('abbrs').insightsComponents, variables('resourceToken'))))]", - "applicationInsightsDashboardName": "[if(not(empty(parameters('applicationInsightsDashboardName'))), createObject('value', parameters('applicationInsightsDashboardName')), createObject('value', format('{0}{1}', variables('abbrs').portalDashboards, variables('resourceToken'))))]" - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" - }, - "description": "Creates an Application Insights instance and a Log Analytics workspace." - }, - "parameters": { - "logAnalyticsName": { - "type": "string" - }, - "applicationInsightsName": { - "type": "string" - }, - "applicationInsightsDashboardName": { - "type": "string", - "defaultValue": "" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "loganalytics", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('logAnalyticsName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" - }, - "description": "Creates a Log Analytics workspace." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - } - }, - "resources": [ - { - "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-12-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "retentionInDays": 30, - "features": { - "searchVersion": 1 - }, - "sku": { - "name": "PerGB2018" - } - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "applicationinsights", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('applicationInsightsName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "dashboardName": { - "value": "[parameters('applicationInsightsDashboardName')]" - }, - "logAnalyticsWorkspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" - }, - "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." - }, - "parameters": { - "name": { - "type": "string" - }, - "dashboardName": { - "type": "string", - "defaultValue": "" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "logAnalyticsWorkspaceId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Insights/components", - "apiVersion": "2020-02-02", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "kind": "web", - "properties": { - "Application_Type": "web", - "WorkspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" - } }, { - "condition": "[not(empty(parameters('dashboardName')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "application-insights-dashboard", + "name": "cosmos-mongo-account", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2032,13 +387,19 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('dashboardName')]" + "value": "[parameters('accountName')]" }, "location": { "value": "[parameters('location')]" }, - "applicationInsightsName": { - "value": "[parameters('name')]" + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" } }, "template": { @@ -2048,17 +409,14 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "templateHash": "8317058180807592714" }, - "description": "Creates a dashboard for an Application Insights instance." + "description": "Creates an Azure Cosmos DB for MongoDB account." }, "parameters": { "name": { "type": "string" }, - "applicationInsightsName": { - "type": "string" - }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]" @@ -2066,1269 +424,353 @@ "tags": { "type": "object", "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" } }, "resources": [ { - "type": "Microsoft.Portal/dashboards", - "apiVersion": "2020-09-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", "properties": { - "lenses": [ - { - "order": 0, - "parts": [ - { - "position": { - "x": 0, - "y": 0, - "colSpan": 2, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "id", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", - "asset": { - "idInputName": "id", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "overview" - } - }, - { - "position": { - "x": 2, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "ProactiveDetection" - } - }, - { - "position": { - "x": 3, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - } - ], - "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 4, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-04T01:20:33.345Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 5, - "y": 0, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-08T18:47:35.237Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "ConfigurationId", - "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" - } - ], - "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 0, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Usage", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 3, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "endTime": null, - "createdTime": "2018-05-04T01:22:35.782Z", - "isInitialTime": true, - "grain": 1, - "useDashboardTimeRange": false - } - } - ], - "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - } - } - }, - { - "position": { - "x": 4, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Reliability", - "title": "", - "subtitle": "" - } - } - } - } - }, - { - "position": { - "x": 7, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "DataModel", - "value": { - "version": "1.0.0", - "timeContext": { - "durationMs": 86400000, - "createdTime": "2018-05-04T23:42:40.072Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - "isOptional": true - }, - { - "name": "ConfigurationId", - "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", - "isOptional": true - } - ], - "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", - "isAdapter": true, - "asset": { - "idInputName": "ResourceId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "failures" - } - }, - { - "position": { - "x": 8, - "y": 1, - "colSpan": 3, - "rowSpan": 1 + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "13614361263700788271" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Responsiveness\r\n", - "title": "", - "subtitle": "" - } - } + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false } - } - }, - { - "position": { - "x": 11, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ResourceId", - "value": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - { - "name": "DataModel", - "value": { - "version": "1.0.0", - "timeContext": { - "durationMs": 86400000, - "createdTime": "2018-05-04T23:43:37.804Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - "isOptional": true - }, - { - "name": "ConfigurationId", - "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", - "isOptional": true - } - ], - "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", - "isAdapter": true, - "asset": { - "idInputName": "ResourceId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "performance" - } - }, - { - "position": { - "x": 12, - "y": 1, - "colSpan": 3, - "rowSpan": 1 - }, - "metadata": { - "inputs": [], - "type": "Extension/HubsExtension/PartType/MarkdownPart", - "settings": { - "content": { - "settings": { - "content": "# Browser", - "title": "", - "subtitle": "" - } - } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" } - } - }, - { - "position": { - "x": 15, - "y": 1, - "colSpan": 1, - "rowSpan": 1 - }, - "metadata": { - "inputs": [ - { - "name": "ComponentId", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "MetricsExplorerJsonDefinitionId", - "value": "BrowserPerformanceTimelineMetrics" - }, - { - "name": "TimeContext", - "value": { - "durationMs": 86400000, - "createdTime": "2018-05-08T12:16:27.534Z", - "isInitialTime": false, - "grain": 1, - "useDashboardTimeRange": false - } - }, - { - "name": "CurrentFilter", - "value": { - "eventTypes": [ - 4, - 1, - 3, - 5, - 2, - 6, - 13 - ], - "typeFacets": {}, - "isPermissive": false - } - }, - { - "name": "id", - "value": { - "Name": "[parameters('applicationInsightsName')]", - "SubscriptionId": "[subscription().subscriptionId]", - "ResourceGroup": "[resourceGroup().name]" - } - }, - { - "name": "Version", - "value": "1.0" - } - ], - "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", - "asset": { - "idInputName": "ComponentId", - "type": "ApplicationInsights" - }, - "defaultMenuItemId": "browser" - } - }, - { - "position": { - "x": 0, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "sessions/count", - "aggregationType": 5, - "namespace": "microsoft.insights/components/kusto", - "metricVisualization": { - "displayName": "Sessions", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "users/count", - "aggregationType": 5, - "namespace": "microsoft.insights/components/kusto", - "metricVisualization": { - "displayName": "Users", - "color": "#7E58FF" - } - } - ], - "title": "Unique sessions and users", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "segmentationUsers" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "requests/failed", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Failed requests", - "color": "#EC008C" - } - } - ], - "title": "Failed requests", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "failures" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "requests/duration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Server response time", - "color": "#00BCF2" - } - } - ], - "title": "Server response time", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "performance" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 12, - "y": 2, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/networkDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Page load network connect time", - "color": "#7E58FF" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/processingDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Client processing time", - "color": "#44F1C8" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/sendDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Send request time", - "color": "#EB9371" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "browserTimings/receiveDuration", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Receiving response time", - "color": "#0672F1" - } - } - ], - "title": "Average page load time breakdown", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 0, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "availabilityResults/availabilityPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Availability", - "color": "#47BDF5" - } - } - ], - "title": "Average availability", - "visualization": { - "chartType": 3, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - }, - "openBladeOnClick": { - "openBlade": true, - "destinationBlade": { - "extensionName": "HubsExtension", - "bladeName": "ResourceMenuBlade", - "parameters": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]", - "menuid": "availability" - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "exceptions/server", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Server exceptions", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "dependencies/failed", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Dependency failures", - "color": "#7E58FF" - } - } - ], - "title": "Server exceptions and Dependency failures", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processorCpuPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Processor time", - "color": "#47BDF5" - } - }, - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processCpuPercentage", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Process CPU", - "color": "#7E58FF" - } - } - ], - "title": "Average processor and process CPU utilization", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 12, - "y": 5, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "exceptions/browser", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Browser exceptions", - "color": "#47BDF5" - } - } - ], - "title": "Browser exceptions", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 0, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "availabilityResults/count", - "aggregationType": 7, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Availability test results count", - "color": "#47BDF5" - } - } - ], - "title": "Availability test results count", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 4, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/processIOBytesPerSecond", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Process IO rate", - "color": "#47BDF5" - } - } - ], - "title": "Average process I/O rate", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } - }, - { - "position": { - "x": 8, - "y": 8, - "colSpan": 4, - "rowSpan": 3 - }, - "metadata": { - "inputs": [ - { - "name": "options", - "value": { - "chart": { - "metrics": [ - { - "resourceMetadata": { - "id": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/components/{2}', subscription().subscriptionId, resourceGroup().name, parameters('applicationInsightsName'))]" - }, - "name": "performanceCounters/memoryAvailableBytes", - "aggregationType": 4, - "namespace": "microsoft.insights/components", - "metricVisualization": { - "displayName": "Available memory", - "color": "#47BDF5" - } - } - ], - "title": "Average available memory", - "visualization": { - "chartType": 2, - "legendVisualization": { - "isVisible": true, - "position": 2, - "hideSubtitle": false - }, - "axisVisualization": { - "x": { - "isVisible": true, - "axisType": 2 - }, - "y": { - "isVisible": true, - "axisType": 1 - } - } - } - } - } - }, - { - "name": "sharedTimeRange", - "isOptional": true - } - ], - "type": "Extension/HubsExtension/PartType/MonitorChartPart", - "settings": {} - } + ] } - ] + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } } - ] + } } } - ] + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } } - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/components', parameters('name'))]" - ] + } } ], "outputs": { - "connectionString": { + "connectionStringKey": { "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').ConnectionString]" + "value": "[parameters('connectionStringKey')]" }, - "instrumentationKey": { + "databaseName": { "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('name')), '2020-02-02').InstrumentationKey]" + "value": "[parameters('databaseName')]" }, - "name": { + "endpoint": { "type": "string", - "value": "[parameters('name')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" } } } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'loganalytics')]" - ] + } } ], "outputs": { - "applicationInsightsConnectionString": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.connectionString.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" }, - "applicationInsightsInstrumentationKey": { + "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.instrumentationKey.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "17948623451174129396" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" }, - "applicationInsightsName": { + "location": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'applicationinsights'), '2022-09-01').outputs.name.value]" + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} }, - "logAnalyticsWorkspaceId": { + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.id.value]" + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" }, - "logAnalyticsWorkspaceName": { + "name": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'loganalytics'), '2022-09-01').outputs.name.value]" + "value": "[parameters('name')]" } } } } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[parameters('aksClusterIdentityObjectId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "815983560956742247" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] } ], "outputs": { @@ -3340,18 +782,6 @@ "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" }, - "APPLICATIONINSIGHTS_CONNECTION_STRING": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" - }, - "AZURE_KEY_VAULT_ENDPOINT": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" - }, - "AZURE_KEY_VAULT_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, "AZURE_LOCATION": { "type": "string", "value": "[parameters('location')]" @@ -3360,25 +790,13 @@ "type": "string", "value": "[tenant().tenantId]" }, - "AZURE_AKS_CLUSTER_NAME": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterName.value]" - }, - "AZURE_AKS_IDENTITY_CLIENT_ID": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.clusterIdentity.value.clientId]" - }, - "AZURE_CONTAINER_REGISTRY_ENDPOINT": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryLoginServer.value]" - }, - "AZURE_CONTAINER_REGISTRY_NAME": { + "AZURE_KEY_VAULT_ENDPOINT": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.containerRegistryName.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" }, - "REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING": { + "AZURE_KEY_VAULT_NAME": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitoring'), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" } } } \ No newline at end of file diff --git a/Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep b/Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep deleted file mode 100644 index 1bf5666b..00000000 --- a/Environments/App-Base-WebApp-AKS/core/ai/cognitiveservices.bicep +++ /dev/null @@ -1,53 +0,0 @@ -metadata description = 'Creates an Azure Cognitive Services instance.' -param name string -param location string = resourceGroup().location -param tags object = {} -@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') -param customSubDomainName string = name -param deployments array = [] -param kind string = 'OpenAI' - -@allowed([ 'Enabled', 'Disabled' ]) -param publicNetworkAccess string = 'Enabled' -param sku object = { - name: 'S0' -} - -param allowedIpRules array = [] -param networkAcls object = empty(allowedIpRules) ? { - defaultAction: 'Allow' -} : { - ipRules: allowedIpRules - defaultAction: 'Deny' -} - -resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { - name: name - location: location - tags: tags - kind: kind - properties: { - customSubDomainName: customSubDomainName - publicNetworkAccess: publicNetworkAccess - networkAcls: networkAcls - } - sku: sku -} - -@batchSize(1) -resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { - parent: account - name: deployment.name - properties: { - model: deployment.model - raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null - } - sku: contains(deployment, 'sku') ? deployment.sku : { - name: 'Standard' - capacity: 20 - } -}] - -output endpoint string = account.properties.endpoint -output id string = account.id -output name string = account.name diff --git a/Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep b/Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep deleted file mode 100644 index 7e26b1a8..00000000 --- a/Environments/App-Base-WebApp-AKS/core/database/postgresql/flexibleserver.bicep +++ /dev/null @@ -1,65 +0,0 @@ -metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param sku object -param storage object -param administratorLogin string -@secure() -param administratorLoginPassword string -param databaseNames array = [] -param allowAzureIPsFirewall bool = false -param allowAllIPsFirewall bool = false -param allowedSingleIPs array = [] - -// PostgreSQL version -param version string - -// Latest official version 2022-12-01 does not have Bicep types available -resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { - location: location - tags: tags - name: name - sku: sku - properties: { - version: version - administratorLogin: administratorLogin - administratorLoginPassword: administratorLoginPassword - storage: storage - highAvailability: { - mode: 'Disabled' - } - } - - resource database 'databases' = [for name in databaseNames: { - name: name - }] - - resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { - name: 'allow-all-IPs' - properties: { - startIpAddress: '0.0.0.0' - endIpAddress: '255.255.255.255' - } - } - - resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { - name: 'allow-all-azure-internal-IPs' - properties: { - startIpAddress: '0.0.0.0' - endIpAddress: '0.0.0.0' - } - } - - resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { - name: 'allow-single-${replace(ip, '.', '')}' - properties: { - startIpAddress: ip - endIpAddress: ip - } - }] - -} - -output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep b/Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep deleted file mode 100644 index 84f2cc2c..00000000 --- a/Environments/App-Base-WebApp-AKS/core/database/sqlserver/sqlserver.bicep +++ /dev/null @@ -1,130 +0,0 @@ -metadata description = 'Creates an Azure SQL Server instance.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param appUser string = 'appUser' -param databaseName string -param keyVaultName string -param sqlAdmin string = 'sqlAdmin' -param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' - -@secure() -param sqlAdminPassword string -@secure() -param appUserPassword string - -resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { - name: name - location: location - tags: tags - properties: { - version: '12.0' - minimalTlsVersion: '1.2' - publicNetworkAccess: 'Enabled' - administratorLogin: sqlAdmin - administratorLoginPassword: sqlAdminPassword - } - - resource database 'databases' = { - name: databaseName - location: location - } - - resource firewall 'firewallRules' = { - name: 'Azure Services' - properties: { - // Allow all clients - // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". - // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. - startIpAddress: '0.0.0.1' - endIpAddress: '255.255.255.254' - } - } -} - -resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { - name: '${name}-deployment-script' - location: location - kind: 'AzureCLI' - properties: { - azCliVersion: '2.37.0' - retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running - timeout: 'PT5M' // Five minutes - cleanupPreference: 'OnSuccess' - environmentVariables: [ - { - name: 'APPUSERNAME' - value: appUser - } - { - name: 'APPUSERPASSWORD' - secureValue: appUserPassword - } - { - name: 'DBNAME' - value: databaseName - } - { - name: 'DBSERVER' - value: sqlServer.properties.fullyQualifiedDomainName - } - { - name: 'SQLCMDPASSWORD' - secureValue: sqlAdminPassword - } - { - name: 'SQLADMIN' - value: sqlAdmin - } - ] - - scriptContent: ''' -wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 -tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . - -cat < ./initDb.sql -drop user if exists ${APPUSERNAME} -go -create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' -go -alter role db_owner add member ${APPUSERNAME} -go -SCRIPT_END - -./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql - ''' - } -} - -resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: 'sqlAdminPassword' - properties: { - value: sqlAdminPassword - } -} - -resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: 'appUserPassword' - properties: { - value: appUserPassword - } -} - -resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: connectionStringKey - properties: { - value: '${connectionString}; Password=${appUserPassword}' - } -} - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - -var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' -output connectionStringKey string = connectionStringKey -output databaseName string = sqlServer::database.name diff --git a/Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep b/Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep deleted file mode 100644 index be7464f0..00000000 --- a/Environments/App-Base-WebApp-AKS/core/gateway/apim.bicep +++ /dev/null @@ -1,79 +0,0 @@ -metadata description = 'Creates an Azure API Management instance.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The email address of the owner of the service') -@minLength(1) -param publisherEmail string = 'noreply@microsoft.com' - -@description('The name of the owner of the service') -@minLength(1) -param publisherName string = 'n/a' - -@description('The pricing tier of this API Management service') -@allowed([ - 'Consumption' - 'Developer' - 'Standard' - 'Premium' -]) -param sku string = 'Consumption' - -@description('The instance size of this API Management service.') -@allowed([ 0, 1, 2 ]) -param skuCount int = 0 - -@description('Azure Application Insights Name') -param applicationInsightsName string - -resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { - name: name - location: location - tags: union(tags, { 'azd-service-name': name }) - sku: { - name: sku - capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) - } - properties: { - publisherEmail: publisherEmail - publisherName: publisherName - // Custom properties are not supported for Consumption SKU - customProperties: sku == 'Consumption' ? {} : { - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' - } - } -} - -resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { - name: 'app-insights-logger' - parent: apimService - properties: { - credentials: { - instrumentationKey: applicationInsights.properties.InstrumentationKey - } - description: 'Logger to Azure Application Insights' - isBuffered: false - loggerType: 'applicationInsights' - resourceId: applicationInsights.id - } -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { - name: applicationInsightsName -} - -output apimServiceName string = apimService.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep b/Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep deleted file mode 100644 index 9c764358..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/aks-agent-pool.bicep +++ /dev/null @@ -1,18 +0,0 @@ -metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' -param clusterName string - -@description('The agent pool name') -param name string - -@description('The agent pool configuration') -param config object - -resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { - name: clusterName -} - -resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { - parent: aksCluster - name: name - properties: config -} diff --git a/Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep b/Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep deleted file mode 100644 index de562a66..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/aks-managed-cluster.bicep +++ /dev/null @@ -1,140 +0,0 @@ -metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' -@description('The name for the AKS managed cluster') -param name string - -@description('The name of the resource group for the managed resources of the AKS cluster') -param nodeResourceGroupName string = '' - -@description('The Azure region/location for the AKS resources') -param location string = resourceGroup().location - -@description('Custom tags to apply to the AKS resources') -param tags object = {} - -@description('Kubernetes Version') -param kubernetesVersion string = '1.27.7' - -@description('Whether RBAC is enabled for local accounts') -param enableRbac bool = true - -// Add-ons -@description('Whether web app routing (preview) add-on is enabled') -param webAppRoutingAddon bool = true - -// AAD Integration -@description('Enable Azure Active Directory integration') -param enableAad bool = false - -@description('Enable RBAC using AAD') -param enableAzureRbac bool = false - -@description('The Tenant ID associated to the Azure Active Directory') -param aadTenantId string = tenant().tenantId - -@description('The load balancer SKU to use for ingress into the AKS cluster') -@allowed([ 'basic', 'standard' ]) -param loadBalancerSku string = 'standard' - -@description('Network plugin used for building the Kubernetes network.') -@allowed([ 'azure', 'kubenet', 'none' ]) -param networkPlugin string = 'azure' - -@description('Network policy used for building the Kubernetes network.') -@allowed([ 'azure', 'calico' ]) -param networkPolicy string = 'azure' - -@description('If set to true, getting static credentials will be disabled for this cluster.') -param disableLocalAccounts bool = false - -@description('The managed cluster SKU.') -@allowed([ 'Free', 'Paid', 'Standard' ]) -param sku string = 'Free' - -@description('Configuration of AKS add-ons') -param addOns object = {} - -@description('The log analytics workspace id used for logging & monitoring') -param workspaceId string = '' - -@description('The node pool configuration for the System agent pool') -param systemPoolConfig object - -@description('The DNS prefix to associate with the AKS cluster') -param dnsPrefix string = '' - -resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' = { - name: name - location: location - tags: tags - identity: { - type: 'SystemAssigned' - } - sku: { - name: 'Base' - tier: sku - } - properties: { - nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' - kubernetesVersion: kubernetesVersion - dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix - enableRBAC: enableRbac - aadProfile: enableAad ? { - managed: true - enableAzureRBAC: enableAzureRbac - tenantID: aadTenantId - } : null - agentPoolProfiles: [ - systemPoolConfig - ] - networkProfile: { - loadBalancerSku: loadBalancerSku - networkPlugin: networkPlugin - networkPolicy: networkPolicy - } - disableLocalAccounts: disableLocalAccounts && enableAad - addonProfiles: addOns - ingressProfile: { - webAppRouting: { - enabled: webAppRoutingAddon - } - } - } -} - -var aksDiagCategories = [ - 'cluster-autoscaler' - 'kube-controller-manager' - 'kube-audit-admin' - 'guard' -] - -// TODO: Update diagnostics to be its own module -// Blocking issue: https://github.com/Azure/bicep/issues/622 -// Unable to pass in a `resource` scope or unable to use string interpolation in resource types -resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { - name: 'aks-diagnostics' - scope: aks - properties: { - workspaceId: workspaceId - logs: [for category in aksDiagCategories: { - category: category - enabled: true - }] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -@description('The resource name of the AKS cluster') -output clusterName string = aks.name - -@description('The AKS cluster identity') -output clusterIdentity object = { - clientId: aks.properties.identityProfile.kubeletidentity.clientId - objectId: aks.properties.identityProfile.kubeletidentity.objectId - resourceId: aks.properties.identityProfile.kubeletidentity.resourceId -} diff --git a/Environments/App-Base-WebApp-AKS/core/host/aks.bicep b/Environments/App-Base-WebApp-AKS/core/host/aks.bicep deleted file mode 100644 index 536a534b..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/aks.bicep +++ /dev/null @@ -1,280 +0,0 @@ -metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' -@description('The name for the AKS managed cluster') -param name string - -@description('The name for the Azure container registry (ACR)') -param containerRegistryName string - -@description('The name of the connected log analytics workspace') -param logAnalyticsName string = '' - -@description('The name of the keyvault to grant access') -param keyVaultName string - -@description('The Azure region/location for the AKS resources') -param location string = resourceGroup().location - -@description('Custom tags to apply to the AKS resources') -param tags object = {} - -@description('AKS add-ons configuration') -param addOns object = { - azurePolicy: { - enabled: true - config: { - version: 'v2' - } - } - keyVault: { - enabled: true - config: { - enableSecretRotation: 'true' - rotationPollInterval: '2m' - } - } - openServiceMesh: { - enabled: false - config: {} - } - omsAgent: { - enabled: true - config: {} - } - applicationGateway: { - enabled: false - config: {} - } -} - -@description('The managed cluster SKU.') -@allowed([ 'Free', 'Paid', 'Standard' ]) -param sku string = 'Free' - -@description('The load balancer SKU to use for ingress into the AKS cluster') -@allowed([ 'basic', 'standard' ]) -param loadBalancerSku string = 'standard' - -@description('Network plugin used for building the Kubernetes network.') -@allowed([ 'azure', 'kubenet', 'none' ]) -param networkPlugin string = 'azure' - -@description('Network policy used for building the Kubernetes network.') -@allowed([ 'azure', 'calico' ]) -param networkPolicy string = 'azure' - -@description('The DNS prefix to associate with the AKS cluster') -param dnsPrefix string = '' - -@description('The name of the resource group for the managed resources of the AKS cluster') -param nodeResourceGroupName string = '' - -@allowed([ - 'CostOptimised' - 'Standard' - 'HighSpec' - 'Custom' -]) -@description('The System Pool Preset sizing') -param systemPoolType string = 'CostOptimised' - -@allowed([ - '' - 'CostOptimised' - 'Standard' - 'HighSpec' - 'Custom' -]) -@description('The User Pool Preset sizing') -param agentPoolType string = '' - -// Configure system / user agent pools -@description('Custom configuration of system node pool') -param systemPoolConfig object = {} -@description('Custom configuration of user node pool') -param agentPoolConfig object = {} - -@description('Id of the user or app to assign application roles') -param principalId string = '' - -@description('Kubernetes Version') -param kubernetesVersion string = '1.27.7' - -@description('The Tenant ID associated to the Azure Active Directory') -param aadTenantId string = tenant().tenantId - -@description('Whether RBAC is enabled for local accounts') -param enableRbac bool = true - -@description('If set to true, getting static credentials will be disabled for this cluster.') -param disableLocalAccounts bool = false - -@description('Enable RBAC using AAD') -param enableAzureRbac bool = false - -// Add-ons -@description('Whether web app routing (preview) add-on is enabled') -param webAppRoutingAddon bool = true - -// Configure AKS add-ons -var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( - addOns.omsAgent, - { - config: { - logAnalyticsWorkspaceResourceID: logAnalytics.id - } - } -) : {} - -var addOnsConfig = union( - (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, - (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, - (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, - (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, - (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} -) - -// Link to existing log analytics workspace when available -resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { - name: logAnalyticsName -} - -var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] - -// Create the primary AKS cluster resources and system node pool -module managedCluster 'aks-managed-cluster.bicep' = { - name: 'managed-cluster' - params: { - name: name - location: location - tags: tags - systemPoolConfig: union( - { name: 'npsystem', mode: 'System' }, - nodePoolBase, - systemPoolSpec - ) - nodeResourceGroupName: nodeResourceGroupName - sku: sku - dnsPrefix: dnsPrefix - kubernetesVersion: kubernetesVersion - addOns: addOnsConfig - workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' - enableAad: enableAzureRbac && aadTenantId != '' - disableLocalAccounts: disableLocalAccounts - aadTenantId: aadTenantId - enableRbac: enableRbac - enableAzureRbac: enableAzureRbac - webAppRoutingAddon: webAppRoutingAddon - loadBalancerSku: loadBalancerSku - networkPlugin: networkPlugin - networkPolicy: networkPolicy - } -} - -var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) -var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] - -// Create additional user agent pool when specified -module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { - name: 'aks-node-pool' - params: { - clusterName: managedCluster.outputs.clusterName - name: 'npuserpool' - config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) - } -} - -// Creates container registry (ACR) -module containerRegistry 'container-registry.bicep' = { - name: 'container-registry' - params: { - name: containerRegistryName - location: location - tags: tags - workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' - } -} - -// Grant ACR Pull access from cluster managed identity to container registry -module containerRegistryAccess '../security/registry-access.bicep' = { - name: 'cluster-container-registry-access' - params: { - containerRegistryName: containerRegistry.outputs.name - principalId: managedCluster.outputs.clusterIdentity.objectId - } -} - -// Give AKS cluster access to the specified principal -module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (enableAzureRbac || disableLocalAccounts) { - name: 'cluster-access' - params: { - clusterName: managedCluster.outputs.clusterName - principalId: principalId - } -} - -// Give the AKS Cluster access to KeyVault -module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { - name: 'cluster-keyvault-access' - params: { - keyVaultName: keyVaultName - principalId: managedCluster.outputs.clusterIdentity.objectId - } -} - -// Helpers for node pool configuration -var nodePoolBase = { - osType: 'Linux' - maxPods: 30 - type: 'VirtualMachineScaleSets' - upgradeSettings: { - maxSurge: '33%' - } -} - -var nodePoolPresets = { - CostOptimised: { - vmSize: 'Standard_B4ms' - count: 1 - minCount: 1 - maxCount: 3 - enableAutoScaling: true - availabilityZones: [] - } - Standard: { - vmSize: 'Standard_DS2_v2' - count: 3 - minCount: 3 - maxCount: 5 - enableAutoScaling: true - availabilityZones: [ - '1' - '2' - '3' - ] - } - HighSpec: { - vmSize: 'Standard_D4s_v3' - count: 3 - minCount: 3 - maxCount: 5 - enableAutoScaling: true - availabilityZones: [ - '1' - '2' - '3' - ] - } -} - -// Module outputs -@description('The resource name of the AKS cluster') -output clusterName string = managedCluster.outputs.clusterName - -@description('The AKS cluster identity') -output clusterIdentity object = managedCluster.outputs.clusterIdentity - -@description('The resource name of the ACR') -output containerRegistryName string = containerRegistry.outputs.name - -@description('The login server for the container registry') -output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep b/Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep deleted file mode 100644 index f4b22f81..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/appservice-appsettings.bicep +++ /dev/null @@ -1,17 +0,0 @@ -metadata description = 'Updates app settings for an Azure App Service.' -@description('The name of the app service resource within the current resource group scope') -param name string - -@description('The app settings to be applied to the app service') -@secure() -param appSettings object - -resource appService 'Microsoft.Web/sites@2022-03-01' existing = { - name: name -} - -resource settings 'Microsoft.Web/sites/config@2022-03-01' = { - name: 'appsettings' - parent: appService - properties: appSettings -} diff --git a/Environments/App-Base-WebApp-AKS/core/host/appservice.bicep b/Environments/App-Base-WebApp-AKS/core/host/appservice.bicep deleted file mode 100644 index bef4d2ba..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/appservice.bicep +++ /dev/null @@ -1,123 +0,0 @@ -metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' -param name string -param location string = resourceGroup().location -param tags object = {} - -// Reference Properties -param applicationInsightsName string = '' -param appServicePlanId string -param keyVaultName string = '' -param managedIdentity bool = !empty(keyVaultName) - -// Runtime Properties -@allowed([ - 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' -]) -param runtimeName string -param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' -param runtimeVersion string - -// Microsoft.Web/sites Properties -param kind string = 'app,linux' - -// Microsoft.Web/sites/config -param allowedOrigins array = [] -param alwaysOn bool = true -param appCommandLine string = '' -@secure() -param appSettings object = {} -param clientAffinityEnabled bool = false -param enableOryxBuild bool = contains(kind, 'linux') -param functionAppScaleLimit int = -1 -param linuxFxVersion string = runtimeNameAndVersion -param minimumElasticInstanceCount int = -1 -param numberOfWorkers int = -1 -param scmDoBuildDuringDeployment bool = false -param use32BitWorkerProcess bool = false -param ftpsState string = 'FtpsOnly' -param healthCheckPath string = '' - -resource appService 'Microsoft.Web/sites@2022-03-01' = { - name: name - location: location - tags: tags - kind: kind - properties: { - serverFarmId: appServicePlanId - siteConfig: { - linuxFxVersion: linuxFxVersion - alwaysOn: alwaysOn - ftpsState: ftpsState - minTlsVersion: '1.2' - appCommandLine: appCommandLine - numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null - minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null - use32BitWorkerProcess: use32BitWorkerProcess - functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null - healthCheckPath: healthCheckPath - cors: { - allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) - } - } - clientAffinityEnabled: clientAffinityEnabled - httpsOnly: true - } - - identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } - - resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { - name: 'ftp' - properties: { - allow: false - } - } - - resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { - name: 'scm' - properties: { - allow: false - } - } -} - -// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially -// sites/web/config 'appsettings' -module configAppSettings 'appservice-appsettings.bicep' = { - name: '${name}-appSettings' - params: { - name: appService.name - appSettings: union(appSettings, - { - SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) - ENABLE_ORYX_BUILD: string(enableOryxBuild) - }, - runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, - !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, - !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) - } -} - -// sites/web/config 'logs' -resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { - name: 'logs' - parent: appService - properties: { - applicationLogs: { fileSystem: { level: 'Verbose' } } - detailedErrorMessages: { enabled: true } - failedRequestsTracing: { enabled: true } - httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } - } - dependsOn: [configAppSettings] -} - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { - name: keyVaultName -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { - name: applicationInsightsName -} - -output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' -output name string = appService.name -output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep b/Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep deleted file mode 100644 index 2e37e041..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/appserviceplan.bicep +++ /dev/null @@ -1,22 +0,0 @@ -metadata description = 'Creates an Azure App Service plan.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param kind string = '' -param reserved bool = true -param sku object - -resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { - name: name - location: location - tags: tags - sku: sku - kind: kind - properties: { - reserved: reserved - } -} - -output id string = appServicePlan.id -output name string = appServicePlan.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep deleted file mode 100644 index 3eec62f2..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/container-app-upsert.bicep +++ /dev/null @@ -1,105 +0,0 @@ -metadata description = 'Creates or updates an existing Azure Container App.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The environment name for the container apps') -param containerAppsEnvironmentName string - -@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') -param containerCpuCoreCount string = '0.5' - -@description('The maximum number of replicas to run. Must be at least 1.') -@minValue(1) -param containerMaxReplicas int = 10 - -@description('The amount of memory allocated to a single container instance, e.g., 1Gi') -param containerMemory string = '1.0Gi' - -@description('The minimum number of replicas to run. Must be at least 1.') -@minValue(1) -param containerMinReplicas int = 1 - -@description('The name of the container') -param containerName string = 'main' - -@description('The name of the container registry') -param containerRegistryName string = '' - -@allowed([ 'http', 'grpc' ]) -@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') -param daprAppProtocol string = 'http' - -@description('Enable or disable Dapr for the container app') -param daprEnabled bool = false - -@description('The Dapr app ID') -param daprAppId string = containerName - -@description('Specifies if the resource already exists') -param exists bool = false - -@description('Specifies if Ingress is enabled for the container app') -param ingressEnabled bool = true - -@description('The type of identity for the resource') -@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) -param identityType string = 'None' - -@description('The name of the user-assigned identity') -param identityName string = '' - -@description('The name of the container image') -param imageName string = '' - -@description('The secrets required for the container') -param secrets array = [] - -@description('The environment variables for the container') -param env array = [] - -@description('Specifies if the resource ingress is exposed externally') -param external bool = true - -@description('The service binds associated with the container') -param serviceBinds array = [] - -@description('The target port for the container') -param targetPort int = 80 - -resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { - name: name -} - -module app 'container-app.bicep' = { - name: '${deployment().name}-update' - params: { - name: name - location: location - tags: tags - identityType: identityType - identityName: identityName - ingressEnabled: ingressEnabled - containerName: containerName - containerAppsEnvironmentName: containerAppsEnvironmentName - containerRegistryName: containerRegistryName - containerCpuCoreCount: containerCpuCoreCount - containerMemory: containerMemory - containerMinReplicas: containerMinReplicas - containerMaxReplicas: containerMaxReplicas - daprEnabled: daprEnabled - daprAppId: daprAppId - daprAppProtocol: daprAppProtocol - secrets: secrets - external: external - env: env - imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' - targetPort: targetPort - serviceBinds: serviceBinds - } -} - -output defaultDomain string = app.outputs.defaultDomain -output imageName string = app.outputs.imageName -output name string = app.outputs.name -output uri string = app.outputs.uri diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-app.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-app.bicep deleted file mode 100644 index 3724086d..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/container-app.bicep +++ /dev/null @@ -1,162 +0,0 @@ -metadata description = 'Creates a container app in an Azure Container App environment.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('Allowed origins') -param allowedOrigins array = [] - -@description('Name of the environment for container apps') -param containerAppsEnvironmentName string - -@description('CPU cores allocated to a single container instance, e.g., 0.5') -param containerCpuCoreCount string = '0.5' - -@description('The maximum number of replicas to run. Must be at least 1.') -@minValue(1) -param containerMaxReplicas int = 10 - -@description('Memory allocated to a single container instance, e.g., 1Gi') -param containerMemory string = '1.0Gi' - -@description('The minimum number of replicas to run. Must be at least 1.') -param containerMinReplicas int = 1 - -@description('The name of the container') -param containerName string = 'main' - -@description('The name of the container registry') -param containerRegistryName string = '' - -@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') -@allowed([ 'http', 'grpc' ]) -param daprAppProtocol string = 'http' - -@description('The Dapr app ID') -param daprAppId string = containerName - -@description('Enable Dapr') -param daprEnabled bool = false - -@description('The environment variables for the container') -param env array = [] - -@description('Specifies if the resource ingress is exposed externally') -param external bool = true - -@description('The name of the user-assigned identity') -param identityName string = '' - -@description('The type of identity for the resource') -@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) -param identityType string = 'None' - -@description('The name of the container image') -param imageName string = '' - -@description('Specifies if Ingress is enabled for the container app') -param ingressEnabled bool = true - -param revisionMode string = 'Single' - -@description('The secrets required for the container') -param secrets array = [] - -@description('The service binds associated with the container') -param serviceBinds array = [] - -@description('The name of the container apps add-on to use. e.g. redis') -param serviceType string = '' - -@description('The target port for the container') -param targetPort int = 80 - -resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { - name: identityName -} - -// Private registry support requires both an ACR name and a User Assigned managed identity -var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) - -// Automatically set to `UserAssigned` when an `identityName` has been set -var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType - -module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { - name: '${deployment().name}-registry-access' - params: { - containerRegistryName: containerRegistryName - principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' - } -} - -resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { - name: name - location: location - tags: tags - // It is critical that the identity is granted ACR pull access before the app is created - // otherwise the container app will throw a provision error - // This also forces us to use an user assigned managed identity since there would no way to - // provide the system assigned identity with the ACR pull access before the app is created - dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] - identity: { - type: normalizedIdentityType - userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null - } - properties: { - managedEnvironmentId: containerAppsEnvironment.id - configuration: { - activeRevisionsMode: revisionMode - ingress: ingressEnabled ? { - external: external - targetPort: targetPort - transport: 'auto' - corsPolicy: { - allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) - } - } : null - dapr: daprEnabled ? { - enabled: true - appId: daprAppId - appProtocol: daprAppProtocol - appPort: ingressEnabled ? targetPort : 0 - } : { enabled: false } - secrets: secrets - service: !empty(serviceType) ? { type: serviceType } : null - registries: usePrivateRegistry ? [ - { - server: '${containerRegistryName}.azurecr.io' - identity: userIdentity.id - } - ] : [] - } - template: { - serviceBinds: !empty(serviceBinds) ? serviceBinds : null - containers: [ - { - image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' - name: containerName - env: env - resources: { - cpu: json(containerCpuCoreCount) - memory: containerMemory - } - } - ] - scale: { - minReplicas: containerMinReplicas - maxReplicas: containerMaxReplicas - } - } - } -} - -resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { - name: containerAppsEnvironmentName -} - -output defaultDomain string = containerAppsEnvironment.properties.defaultDomain -output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) -output imageName string = imageName -output name string = app.name -output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} -output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep deleted file mode 100644 index 8633ba48..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/container-apps-environment.bicep +++ /dev/null @@ -1,41 +0,0 @@ -metadata description = 'Creates an Azure Container Apps environment.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('Name of the Application Insights resource') -param applicationInsightsName string = '' - -@description('Specifies if Dapr is enabled') -param daprEnabled bool = false - -@description('Name of the Log Analytics workspace') -param logAnalyticsWorkspaceName string - -resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { - name: name - location: location - tags: tags - properties: { - appLogsConfiguration: { - destination: 'log-analytics' - logAnalyticsConfiguration: { - customerId: logAnalyticsWorkspace.properties.customerId - sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey - } - } - daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' - } -} - -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { - name: logAnalyticsWorkspaceName -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { - name: applicationInsightsName -} - -output defaultDomain string = containerAppsEnvironment.properties.defaultDomain -output id string = containerAppsEnvironment.id -output name string = containerAppsEnvironment.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep deleted file mode 100644 index 1c656e28..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/container-apps.bicep +++ /dev/null @@ -1,40 +0,0 @@ -metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param containerAppsEnvironmentName string -param containerRegistryName string -param containerRegistryResourceGroupName string = '' -param containerRegistryAdminUserEnabled bool = false -param logAnalyticsWorkspaceName string -param applicationInsightsName string = '' - -module containerAppsEnvironment 'container-apps-environment.bicep' = { - name: '${name}-container-apps-environment' - params: { - name: containerAppsEnvironmentName - location: location - tags: tags - logAnalyticsWorkspaceName: logAnalyticsWorkspaceName - applicationInsightsName: applicationInsightsName - } -} - -module containerRegistry 'container-registry.bicep' = { - name: '${name}-container-registry' - scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() - params: { - name: containerRegistryName - location: location - adminUserEnabled: containerRegistryAdminUserEnabled - tags: tags - } -} - -output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain -output environmentName string = containerAppsEnvironment.outputs.name -output environmentId string = containerAppsEnvironment.outputs.id - -output registryLoginServer string = containerRegistry.outputs.loginServer -output registryName string = containerRegistry.outputs.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep b/Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep deleted file mode 100644 index 9c64531b..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/container-registry.bicep +++ /dev/null @@ -1,83 +0,0 @@ -metadata description = 'Creates an Azure Container Registry.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('Indicates whether admin user is enabled') -param adminUserEnabled bool = false - -@description('Indicates whether anonymous pull is enabled') -param anonymousPullEnabled bool = false - -@description('Indicates whether data endpoint is enabled') -param dataEndpointEnabled bool = false - -@description('Encryption settings') -param encryption object = { - status: 'disabled' -} - -@description('Options for bypassing network rules') -param networkRuleBypassOptions string = 'AzureServices' - -@description('Public network access setting') -param publicNetworkAccess string = 'Enabled' - -@description('SKU settings') -param sku object = { - name: 'Basic' -} - -@description('Zone redundancy setting') -param zoneRedundancy string = 'Disabled' - -@description('The log analytics workspace ID used for logging and monitoring') -param workspaceId string = '' - -// 2022-02-01-preview needed for anonymousPullEnabled -resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { - name: name - location: location - tags: tags - sku: sku - properties: { - adminUserEnabled: adminUserEnabled - anonymousPullEnabled: anonymousPullEnabled - dataEndpointEnabled: dataEndpointEnabled - encryption: encryption - networkRuleBypassOptions: networkRuleBypassOptions - publicNetworkAccess: publicNetworkAccess - zoneRedundancy: zoneRedundancy - } -} - -// TODO: Update diagnostics to be its own module -// Blocking issue: https://github.com/Azure/bicep/issues/622 -// Unable to pass in a `resource` scope or unable to use string interpolation in resource types -resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { - name: 'registry-diagnostics' - scope: containerRegistry - properties: { - workspaceId: workspaceId - logs: [ - { - category: 'ContainerRegistryRepositoryEvents' - enabled: true - } - { - category: 'ContainerRegistryLoginEvents' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - timeGrain: 'PT1M' - } - ] - } -} - -output loginServer string = containerRegistry.properties.loginServer -output name string = containerRegistry.name diff --git a/Environments/App-Base-WebApp-AKS/core/host/functions.bicep b/Environments/App-Base-WebApp-AKS/core/host/functions.bicep deleted file mode 100644 index 7070a2c6..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/functions.bicep +++ /dev/null @@ -1,86 +0,0 @@ -metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' -param name string -param location string = resourceGroup().location -param tags object = {} - -// Reference Properties -param applicationInsightsName string = '' -param appServicePlanId string -param keyVaultName string = '' -param managedIdentity bool = !empty(keyVaultName) -param storageAccountName string - -// Runtime Properties -@allowed([ - 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' -]) -param runtimeName string -param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' -param runtimeVersion string - -// Function Settings -@allowed([ - '~4', '~3', '~2', '~1' -]) -param extensionVersion string = '~4' - -// Microsoft.Web/sites Properties -param kind string = 'functionapp,linux' - -// Microsoft.Web/sites/config -param allowedOrigins array = [] -param alwaysOn bool = true -param appCommandLine string = '' -@secure() -param appSettings object = {} -param clientAffinityEnabled bool = false -param enableOryxBuild bool = contains(kind, 'linux') -param functionAppScaleLimit int = -1 -param linuxFxVersion string = runtimeNameAndVersion -param minimumElasticInstanceCount int = -1 -param numberOfWorkers int = -1 -param scmDoBuildDuringDeployment bool = true -param use32BitWorkerProcess bool = false -param healthCheckPath string = '' - -module functions 'appservice.bicep' = { - name: '${name}-functions' - params: { - name: name - location: location - tags: tags - allowedOrigins: allowedOrigins - alwaysOn: alwaysOn - appCommandLine: appCommandLine - applicationInsightsName: applicationInsightsName - appServicePlanId: appServicePlanId - appSettings: union(appSettings, { - AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' - FUNCTIONS_EXTENSION_VERSION: extensionVersion - FUNCTIONS_WORKER_RUNTIME: runtimeName - }) - clientAffinityEnabled: clientAffinityEnabled - enableOryxBuild: enableOryxBuild - functionAppScaleLimit: functionAppScaleLimit - healthCheckPath: healthCheckPath - keyVaultName: keyVaultName - kind: kind - linuxFxVersion: linuxFxVersion - managedIdentity: managedIdentity - minimumElasticInstanceCount: minimumElasticInstanceCount - numberOfWorkers: numberOfWorkers - runtimeName: runtimeName - runtimeVersion: runtimeVersion - runtimeNameAndVersion: runtimeNameAndVersion - scmDoBuildDuringDeployment: scmDoBuildDuringDeployment - use32BitWorkerProcess: use32BitWorkerProcess - } -} - -resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { - name: storageAccountName -} - -output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' -output name string = functions.outputs.name -output uri string = functions.outputs.uri diff --git a/Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep b/Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep deleted file mode 100644 index cedaf906..00000000 --- a/Environments/App-Base-WebApp-AKS/core/host/staticwebapp.bicep +++ /dev/null @@ -1,22 +0,0 @@ -metadata description = 'Creates an Azure Static Web Apps instance.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param sku object = { - name: 'Free' - tier: 'Free' -} - -resource web 'Microsoft.Web/staticSites@2022-03-01' = { - name: name - location: location - tags: tags - sku: sku - properties: { - provider: 'Custom' - } -} - -output name string = web.name -output uri string = 'https://${web.properties.defaultHostname}' diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep deleted file mode 100644 index d082e668..00000000 --- a/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights-dashboard.bicep +++ /dev/null @@ -1,1236 +0,0 @@ -metadata description = 'Creates a dashboard for an Application Insights instance.' -param name string -param applicationInsightsName string -param location string = resourceGroup().location -param tags object = {} - -// 2020-09-01-preview because that is the latest valid version -resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { - name: name - location: location - tags: tags - properties: { - lenses: [ - { - order: 0 - parts: [ - { - position: { - x: 0 - y: 0 - colSpan: 2 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'id' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' - asset: { - idInputName: 'id' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'overview' - } - } - { - position: { - x: 2 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'ProactiveDetection' - } - } - { - position: { - x: 3 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:20:33.345Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 5 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-08T18:47:35.237Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'ConfigurationId' - value: '78ce933e-e864-4b05-a27b-71fd55a6afad' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 0 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Usage' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 3 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:22:35.782Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Reliability' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 7 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:42:40.072Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'failures' - } - } - { - position: { - x: 8 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Responsiveness\r\n' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 11 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:43:37.804Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'performance' - } - } - { - position: { - x: 12 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Browser' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 15 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'MetricsExplorerJsonDefinitionId' - value: 'BrowserPerformanceTimelineMetrics' - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - createdTime: '2018-05-08T12:16:27.534Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'CurrentFilter' - value: { - eventTypes: [ - 4 - 1 - 3 - 5 - 2 - 6 - 13 - ] - typeFacets: {} - isPermissive: false - } - } - { - name: 'id' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'browser' - } - } - { - position: { - x: 0 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'sessions/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Sessions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'users/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Users' - color: '#7E58FF' - } - } - ] - title: 'Unique sessions and users' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'segmentationUsers' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Failed requests' - color: '#EC008C' - } - } - ] - title: 'Failed requests' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'failures' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/duration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server response time' - color: '#00BCF2' - } - } - ] - title: 'Server response time' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'performance' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/networkDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Page load network connect time' - color: '#7E58FF' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/processingDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Client processing time' - color: '#44F1C8' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/sendDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Send request time' - color: '#EB9371' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/receiveDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Receiving response time' - color: '#0672F1' - } - } - ] - title: 'Average page load time breakdown' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/availabilityPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability' - color: '#47BDF5' - } - } - ] - title: 'Average availability' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'availability' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/server' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server exceptions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'dependencies/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Dependency failures' - color: '#7E58FF' - } - } - ] - title: 'Server exceptions and Dependency failures' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processorCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Processor time' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process CPU' - color: '#7E58FF' - } - } - ] - title: 'Average processor and process CPU utilization' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/browser' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Browser exceptions' - color: '#47BDF5' - } - } - ] - title: 'Browser exceptions' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/count' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability test results count' - color: '#47BDF5' - } - } - ] - title: 'Availability test results count' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processIOBytesPerSecond' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process IO rate' - color: '#47BDF5' - } - } - ] - title: 'Average process I/O rate' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/memoryAvailableBytes' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Available memory' - color: '#47BDF5' - } - } - ] - title: 'Average available memory' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - ] - } - ] - } -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { - name: applicationInsightsName -} diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep deleted file mode 100644 index 4b4d01e3..00000000 --- a/Environments/App-Base-WebApp-AKS/core/monitor/applicationinsights.bicep +++ /dev/null @@ -1,30 +0,0 @@ -metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' -param name string -param dashboardName string = '' -param location string = resourceGroup().location -param tags object = {} -param logAnalyticsWorkspaceId string - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { - name: name - location: location - tags: tags - kind: 'web' - properties: { - Application_Type: 'web' - WorkspaceResourceId: logAnalyticsWorkspaceId - } -} - -module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { - name: 'application-insights-dashboard' - params: { - name: dashboardName - location: location - applicationInsightsName: applicationInsights.name - } -} - -output connectionString string = applicationInsights.properties.ConnectionString -output instrumentationKey string = applicationInsights.properties.InstrumentationKey -output name string = applicationInsights.name diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep deleted file mode 100644 index 33f9dc29..00000000 --- a/Environments/App-Base-WebApp-AKS/core/monitor/loganalytics.bicep +++ /dev/null @@ -1,22 +0,0 @@ -metadata description = 'Creates a Log Analytics workspace.' -param name string -param location string = resourceGroup().location -param tags object = {} - -resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { - name: name - location: location - tags: tags - properties: any({ - retentionInDays: 30 - features: { - searchVersion: 1 - } - sku: { - name: 'PerGB2018' - } - }) -} - -output id string = logAnalytics.id -output name string = logAnalytics.name diff --git a/Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep b/Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep deleted file mode 100644 index 6bb05b0b..00000000 --- a/Environments/App-Base-WebApp-AKS/core/monitor/monitoring.bicep +++ /dev/null @@ -1,32 +0,0 @@ -metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' -param logAnalyticsName string -param applicationInsightsName string -param applicationInsightsDashboardName string = '' -param location string = resourceGroup().location -param tags object = {} - -module logAnalytics 'loganalytics.bicep' = { - name: 'loganalytics' - params: { - name: logAnalyticsName - location: location - tags: tags - } -} - -module applicationInsights 'applicationinsights.bicep' = { - name: 'applicationinsights' - params: { - name: applicationInsightsName - location: location - tags: tags - dashboardName: applicationInsightsDashboardName - logAnalyticsWorkspaceId: logAnalytics.outputs.id - } -} - -output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString -output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey -output applicationInsightsName string = applicationInsights.outputs.name -output logAnalyticsWorkspaceId string = logAnalytics.outputs.id -output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep b/Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep deleted file mode 100644 index 5e8ab695..00000000 --- a/Environments/App-Base-WebApp-AKS/core/networking/cdn-endpoint.bicep +++ /dev/null @@ -1,52 +0,0 @@ -metadata description = 'Adds an endpoint to an Azure CDN profile.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The name of the CDN profile resource') -@minLength(1) -param cdnProfileName string - -@description('Delivery policy rules') -param deliveryPolicyRules array = [] - -@description('The origin URL for the endpoint') -@minLength(1) -param originUrl string - -resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { - parent: cdnProfile - name: name - location: location - tags: tags - properties: { - originHostHeader: originUrl - isHttpAllowed: false - isHttpsAllowed: true - queryStringCachingBehavior: 'UseQueryString' - optimizationType: 'GeneralWebDelivery' - origins: [ - { - name: replace(originUrl, '.', '-') - properties: { - hostName: originUrl - originHostHeader: originUrl - priority: 1 - weight: 1000 - enabled: true - } - } - ] - deliveryPolicy: { - rules: deliveryPolicyRules - } - } -} - -resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { - name: cdnProfileName -} - -output id string = endpoint.id -output name string = endpoint.name -output uri string = 'https://${endpoint.properties.hostName}' diff --git a/Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep b/Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep deleted file mode 100644 index 27669ee2..00000000 --- a/Environments/App-Base-WebApp-AKS/core/networking/cdn-profile.bicep +++ /dev/null @@ -1,34 +0,0 @@ -metadata description = 'Creates an Azure CDN profile.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The pricing tier of this CDN profile') -@allowed([ - 'Custom_Verizon' - 'Premium_AzureFrontDoor' - 'Premium_Verizon' - 'StandardPlus_955BandWidth_ChinaCdn' - 'StandardPlus_AvgBandWidth_ChinaCdn' - 'StandardPlus_ChinaCdn' - 'Standard_955BandWidth_ChinaCdn' - 'Standard_Akamai' - 'Standard_AvgBandWidth_ChinaCdn' - 'Standard_AzureFrontDoor' - 'Standard_ChinaCdn' - 'Standard_Microsoft' - 'Standard_Verizon' -]) -param sku string = 'Standard_Microsoft' - -resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { - name: name - location: location - tags: tags - sku: { - name: sku - } -} - -output id string = profile.id -output name string = profile.name diff --git a/Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep b/Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep deleted file mode 100644 index de98a1f9..00000000 --- a/Environments/App-Base-WebApp-AKS/core/networking/cdn.bicep +++ /dev/null @@ -1,42 +0,0 @@ -metadata description = 'Creates an Azure CDN profile with a single endpoint.' -param location string = resourceGroup().location -param tags object = {} - -@description('Name of the CDN endpoint resource') -param cdnEndpointName string - -@description('Name of the CDN profile resource') -param cdnProfileName string - -@description('Delivery policy rules') -param deliveryPolicyRules array = [] - -@description('Origin URL for the CDN endpoint') -param originUrl string - -module cdnProfile 'cdn-profile.bicep' = { - name: 'cdn-profile' - params: { - name: cdnProfileName - location: location - tags: tags - } -} - -module cdnEndpoint 'cdn-endpoint.bicep' = { - name: 'cdn-endpoint' - params: { - name: cdnEndpointName - location: location - tags: tags - cdnProfileName: cdnProfile.outputs.name - originUrl: originUrl - deliveryPolicyRules: deliveryPolicyRules - } -} - -output endpointName string = cdnEndpoint.outputs.name -output endpointId string = cdnEndpoint.outputs.id -output profileName string = cdnProfile.outputs.name -output profileId string = cdnProfile.outputs.id -output uri string = cdnEndpoint.outputs.uri diff --git a/Environments/App-Base-WebApp-AKS/core/search/search-services.bicep b/Environments/App-Base-WebApp-AKS/core/search/search-services.bicep deleted file mode 100644 index d9c619a9..00000000 --- a/Environments/App-Base-WebApp-AKS/core/search/search-services.bicep +++ /dev/null @@ -1,68 +0,0 @@ -metadata description = 'Creates an Azure AI Search instance.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param sku object = { - name: 'standard' -} - -param authOptions object = {} -param disableLocalAuth bool = false -param disabledDataExfiltrationOptions array = [] -param encryptionWithCmk object = { - enforcement: 'Unspecified' -} -@allowed([ - 'default' - 'highDensity' -]) -param hostingMode string = 'default' -param networkRuleSet object = { - bypass: 'None' - ipRules: [] -} -param partitionCount int = 1 -@allowed([ - 'enabled' - 'disabled' -]) -param publicNetworkAccess string = 'enabled' -param replicaCount int = 1 -@allowed([ - 'disabled' - 'free' - 'standard' -]) -param semanticSearch string = 'disabled' - -var searchIdentityProvider = (sku.name == 'free') ? null : { - type: 'SystemAssigned' -} - -resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { - name: name - location: location - tags: tags - // The free tier does not support managed identity - identity: searchIdentityProvider - properties: { - authOptions: authOptions - disableLocalAuth: disableLocalAuth - disabledDataExfiltrationOptions: disabledDataExfiltrationOptions - encryptionWithCmk: encryptionWithCmk - hostingMode: hostingMode - networkRuleSet: networkRuleSet - partitionCount: partitionCount - publicNetworkAccess: publicNetworkAccess - replicaCount: replicaCount - semanticSearch: semanticSearch - } - sku: sku -} - -output id string = search.id -output endpoint string = 'https://${name}.search.windows.net/' -output name string = search.name -output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' - diff --git a/Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep b/Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep deleted file mode 100644 index 4b6febbe..00000000 --- a/Environments/App-Base-WebApp-AKS/core/storage/storage-account.bicep +++ /dev/null @@ -1,64 +0,0 @@ -metadata description = 'Creates an Azure storage account.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@allowed([ - 'Cool' - 'Hot' - 'Premium' ]) -param accessTier string = 'Hot' -param allowBlobPublicAccess bool = true -param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true -param containers array = [] -param defaultToOAuthAuthentication bool = false -param deleteRetentionPolicy object = {} -@allowed([ 'AzureDnsZone', 'Standard' ]) -param dnsEndpointType string = 'Standard' -param kind string = 'StorageV2' -param minimumTlsVersion string = 'TLS1_2' -param supportsHttpsTrafficOnly bool = true -param networkAcls object = { - bypass: 'AzureServices' - defaultAction: 'Allow' -} -@allowed([ 'Enabled', 'Disabled' ]) -param publicNetworkAccess string = 'Enabled' -param sku object = { name: 'Standard_LRS' } - -resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { - name: name - location: location - tags: tags - kind: kind - sku: sku - properties: { - accessTier: accessTier - allowBlobPublicAccess: allowBlobPublicAccess - allowCrossTenantReplication: allowCrossTenantReplication - allowSharedKeyAccess: allowSharedKeyAccess - defaultToOAuthAuthentication: defaultToOAuthAuthentication - dnsEndpointType: dnsEndpointType - minimumTlsVersion: minimumTlsVersion - networkAcls: networkAcls - publicNetworkAccess: publicNetworkAccess - supportsHttpsTrafficOnly: supportsHttpsTrafficOnly - } - - resource blobServices 'blobServices' = if (!empty(containers)) { - name: 'default' - properties: { - deleteRetentionPolicy: deleteRetentionPolicy - } - resource container 'containers' = [for container in containers: { - name: container.name - properties: { - publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' - } - }] - } -} - -output name string = storage.name -output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep b/Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep deleted file mode 100644 index 46781086..00000000 --- a/Environments/App-Base-WebApp-AKS/core/testing/loadtesting.bicep +++ /dev/null @@ -1,15 +0,0 @@ -param name string -param location string = resourceGroup().location -param managedIdentity bool = false -param tags object = {} - -resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { - name: name - location: location - tags: tags - identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } - properties: { - } -} - -output loadTestingName string = loadTest.name diff --git a/Environments/App-Base-WebApp-AKS/main.bicep b/Environments/App-Base-WebApp-AKS/main.bicep index a382fedb..378c06a8 100644 --- a/Environments/App-Base-WebApp-AKS/main.bicep +++ b/Environments/App-Base-WebApp-AKS/main.bicep @@ -5,40 +5,18 @@ param environmentName string @minLength(1) @description('Primary location for all resources') -param location string +param location string = resourceGroup().location -@description('The resource name of the AKS cluster') -param clusterName string = '' - -@description('The resource name of the Container Registry (ACR)') -param containerRegistryName string = '' - -param applicationInsightsDashboardName string = '' -param applicationInsightsName string = '' param cosmosAccountName string = '' param cosmosDatabaseName string = '' param keyVaultName string = '' -param logAnalyticsName string = '' - -@description('Id of the user or app to assign application roles') param principalId string = '' +param aksClusterIdentityObjectId string var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } -// The AKS cluster to host applications -module aks './core/host/aks.bicep' = { - name: 'aks' - params: { - location: location - name: !empty(clusterName) ? clusterName : '${abbrs.containerServiceManagedClusters}${resourceToken}' - containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' - logAnalyticsName: monitoring.outputs.logAnalyticsWorkspaceName - keyVaultName: keyVault.outputs.name - } -} - // The application database module cosmos './app/db.bicep' = { name: 'cosmos' @@ -62,15 +40,11 @@ module keyVault './core/security/keyvault.bicep' = { } } -// Monitor application with Azure Monitor -module monitoring './core/monitor/monitoring.bicep' = { - name: 'monitoring' +module clusterKeyVaultAccess './core/security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' params: { - location: location - tags: tags - logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' - applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' - applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + keyVaultName: keyVault.outputs.name + principalId: aksClusterIdentityObjectId } } @@ -79,13 +53,7 @@ output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStri output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName // App outputs -output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString -output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint -output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId -output AZURE_AKS_CLUSTER_NAME string = aks.outputs.clusterName -output AZURE_AKS_IDENTITY_CLIENT_ID string = aks.outputs.clusterIdentity.clientId -output AZURE_CONTAINER_REGISTRY_ENDPOINT string = aks.outputs.containerRegistryLoginServer -output AZURE_CONTAINER_REGISTRY_NAME string = aks.outputs.containerRegistryName -output REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name diff --git a/Environments/App-Base-WebApp-AKS/manifest.yaml b/Environments/App-Base-WebApp-AKS/manifest.yaml index 7e08cb3e..cbc7191c 100644 --- a/Environments/App-Base-WebApp-AKS/manifest.yaml +++ b/Environments/App-Base-WebApp-AKS/manifest.yaml @@ -1,7 +1,7 @@ name: App-Base-WebApp-AKS version: 1.0.0 -summary: App Base Nodejs Mongo AKS -description: Deploys a base infra for AKS app dev with Nodejs and Mongo +summary: Base infra for AKS web app dev +description: Deploys a todo app with Nodejs and Mongo runner: ARM templatePath: azuredeploy.json @@ -18,11 +18,8 @@ parameters: type: string required: true -- id: "principalId" - name: "principalId (e.g. )" - description: "principal id that have the permission (get list) to access key vault" +- id: "aksClusterIdentityObjectId" + name: "AKS Cluster Identity Object Id" + description: "Object Id of the identity used by the AKS cluster to access the KeyVault" type: string - required: false - default: '' - - + required: true From 9bb888651b8b2fb836767d3a2996c9ba931cc1e0 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 15 Mar 2024 15:00:58 +0800 Subject: [PATCH 076/112] storage account: allow shared key false, cognitive service account: diable local auth true --- Environments/AKS/core/ai/cognitiveservices.bicep | 1 + Environments/AKS/core/storage/storage-account.bicep | 2 +- Environments/APIM/core/storage/storage-account.bicep | 1 + Environments/ContainerApp/core/storage/storage-account.bicep | 1 + Environments/FunctionApp/main.bicep | 5 +++-- Environments/OpenAISearch/core/ai/cognitiveservices.bicep | 1 + Environments/OpenAISearch/core/storage/storage-account.bicep | 2 +- Environments/OpenAISearch/main.bicep | 1 + .../OpenAISummarization/core/ai/openai-account.bicep | 1 + .../OpenAISummarization/core/storage/storage-account.bicep | 2 +- Environments/StaticWeb/core/ai/cognitiveservices.bicep | 1 + Environments/StaticWeb/core/storage/storage-account.bicep | 2 +- Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep | 1 + .../Todo-Mongo-ACA/core/storage/storage-account.bicep | 2 +- .../Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep | 1 + .../Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep | 2 +- .../Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep | 1 + .../Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep | 2 +- Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep | 1 + .../Todo-Shared-AKS/core/storage/storage-account.bicep | 2 +- 20 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Environments/AKS/core/ai/cognitiveservices.bicep b/Environments/AKS/core/ai/cognitiveservices.bicep index e0afb877..7336c9f6 100644 --- a/Environments/AKS/core/ai/cognitiveservices.bicep +++ b/Environments/AKS/core/ai/cognitiveservices.bicep @@ -18,6 +18,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { properties: { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess + disableLocalAuth: true } sku: sku } diff --git a/Environments/AKS/core/storage/storage-account.bicep b/Environments/AKS/core/storage/storage-account.bicep index 53d449ba..aac54563 100644 --- a/Environments/AKS/core/storage/storage-account.bicep +++ b/Environments/AKS/core/storage/storage-account.bicep @@ -9,7 +9,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = true param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param containers array = [] param defaultToOAuthAuthentication bool = false param deleteRetentionPolicy object = {} diff --git a/Environments/APIM/core/storage/storage-account.bicep b/Environments/APIM/core/storage/storage-account.bicep index a41972ce..f3cabaa1 100644 --- a/Environments/APIM/core/storage/storage-account.bicep +++ b/Environments/APIM/core/storage/storage-account.bicep @@ -17,6 +17,7 @@ resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { properties: { minimumTlsVersion: minimumTlsVersion allowBlobPublicAccess: allowBlobPublicAccess + allowSharedKeyAccess: false networkAcls: { bypass: 'AzureServices' defaultAction: 'Allow' diff --git a/Environments/ContainerApp/core/storage/storage-account.bicep b/Environments/ContainerApp/core/storage/storage-account.bicep index a41972ce..f3cabaa1 100644 --- a/Environments/ContainerApp/core/storage/storage-account.bicep +++ b/Environments/ContainerApp/core/storage/storage-account.bicep @@ -17,6 +17,7 @@ resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { properties: { minimumTlsVersion: minimumTlsVersion allowBlobPublicAccess: allowBlobPublicAccess + allowSharedKeyAccess: false networkAcls: { bypass: 'AzureServices' defaultAction: 'Allow' diff --git a/Environments/FunctionApp/main.bicep b/Environments/FunctionApp/main.bicep index 607a75de..b1f2b05c 100644 --- a/Environments/FunctionApp/main.bicep +++ b/Environments/FunctionApp/main.bicep @@ -71,6 +71,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { kind: 'StorageV2' properties: { supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + allowSharedKeyAccess: false } tags: tags } @@ -89,8 +90,8 @@ resource functionApp 'Microsoft.Web/sites@2022-03-01' = { linuxFxVersion: linexFxVersions[runtime] appSettings: [ { - name: 'AzureWebJobsStorage' - value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' + name: 'AzureWebJobsStorage__accountName' + value: storageAccount.name } { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' diff --git a/Environments/OpenAISearch/core/ai/cognitiveservices.bicep b/Environments/OpenAISearch/core/ai/cognitiveservices.bicep index 39108a39..8594e16f 100644 --- a/Environments/OpenAISearch/core/ai/cognitiveservices.bicep +++ b/Environments/OpenAISearch/core/ai/cognitiveservices.bicep @@ -18,6 +18,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { properties: { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess + disableLocalAuth: true } sku: sku } diff --git a/Environments/OpenAISearch/core/storage/storage-account.bicep b/Environments/OpenAISearch/core/storage/storage-account.bicep index b6dd9891..aa7d5873 100644 --- a/Environments/OpenAISearch/core/storage/storage-account.bicep +++ b/Environments/OpenAISearch/core/storage/storage-account.bicep @@ -6,7 +6,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = false param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param defaultToOAuthAuthentication bool = false param deleteRetentionPolicy object = {} @allowed([ 'AzureDnsZone', 'Standard' ]) diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep index 9d79a7fa..8a73859c 100644 --- a/Environments/OpenAISearch/main.bicep +++ b/Environments/OpenAISearch/main.bicep @@ -154,6 +154,7 @@ module storage 'core/storage/storage-account.bicep' = { location: storageResourceGroupLocation tags: tags publicNetworkAccess: 'Enabled' + allowSharedKeyAccess: false sku: { name: 'Standard_ZRS' } diff --git a/Environments/OpenAISummarization/core/ai/openai-account.bicep b/Environments/OpenAISummarization/core/ai/openai-account.bicep index 45cba57b..28141d55 100644 --- a/Environments/OpenAISummarization/core/ai/openai-account.bicep +++ b/Environments/OpenAISummarization/core/ai/openai-account.bicep @@ -12,6 +12,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { properties: { customSubDomainName: name publicNetworkAccess: 'Enabled' + disableLocalAuth: true } sku: { name: 'S0' diff --git a/Environments/OpenAISummarization/core/storage/storage-account.bicep b/Environments/OpenAISummarization/core/storage/storage-account.bicep index 5f74102b..e86514ff 100644 --- a/Environments/OpenAISummarization/core/storage/storage-account.bicep +++ b/Environments/OpenAISummarization/core/storage/storage-account.bicep @@ -6,7 +6,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = false param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param defaultToOAuthAuthentication bool = false @allowed([ 'AzureDnsZone', 'Standard' ]) param dnsEndpointType string = 'Standard' diff --git a/Environments/StaticWeb/core/ai/cognitiveservices.bicep b/Environments/StaticWeb/core/ai/cognitiveservices.bicep index e0afb877..7336c9f6 100644 --- a/Environments/StaticWeb/core/ai/cognitiveservices.bicep +++ b/Environments/StaticWeb/core/ai/cognitiveservices.bicep @@ -18,6 +18,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { properties: { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess + disableLocalAuth: true } sku: sku } diff --git a/Environments/StaticWeb/core/storage/storage-account.bicep b/Environments/StaticWeb/core/storage/storage-account.bicep index 53d449ba..aac54563 100644 --- a/Environments/StaticWeb/core/storage/storage-account.bicep +++ b/Environments/StaticWeb/core/storage/storage-account.bicep @@ -9,7 +9,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = true param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param containers array = [] param defaultToOAuthAuthentication bool = false param deleteRetentionPolicy object = {} diff --git a/Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep b/Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep index 1bf5666b..2d5b8e4b 100644 --- a/Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep +++ b/Environments/Todo-Mongo-ACA/core/ai/cognitiveservices.bicep @@ -30,6 +30,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess networkAcls: networkAcls + disableLocalAuth: true } sku: sku } diff --git a/Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep b/Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep index 4b6febbe..7ce6aec8 100644 --- a/Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep +++ b/Environments/Todo-Mongo-ACA/core/storage/storage-account.bicep @@ -10,7 +10,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = true param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param containers array = [] param defaultToOAuthAuthentication bool = false param deleteRetentionPolicy object = {} diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep index 1bf5666b..2d5b8e4b 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/ai/cognitiveservices.bicep @@ -30,6 +30,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess networkAcls: networkAcls + disableLocalAuth: true } sku: sku } diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep index 4b6febbe..7ce6aec8 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/storage/storage-account.bicep @@ -10,7 +10,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = true param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param containers array = [] param defaultToOAuthAuthentication bool = false param deleteRetentionPolicy object = {} diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep index 1bf5666b..2d5b8e4b 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/ai/cognitiveservices.bicep @@ -30,6 +30,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess networkAcls: networkAcls + disableLocalAuth: true } sku: sku } diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep index 4b6febbe..7ce6aec8 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/storage/storage-account.bicep @@ -10,7 +10,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = true param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param containers array = [] param defaultToOAuthAuthentication bool = false param deleteRetentionPolicy object = {} diff --git a/Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep b/Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep index 1bf5666b..2d5b8e4b 100644 --- a/Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep +++ b/Environments/Todo-Shared-AKS/core/ai/cognitiveservices.bicep @@ -30,6 +30,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess networkAcls: networkAcls + disableLocalAuth: true } sku: sku } diff --git a/Environments/Todo-Shared-AKS/core/storage/storage-account.bicep b/Environments/Todo-Shared-AKS/core/storage/storage-account.bicep index 4b6febbe..7ce6aec8 100644 --- a/Environments/Todo-Shared-AKS/core/storage/storage-account.bicep +++ b/Environments/Todo-Shared-AKS/core/storage/storage-account.bicep @@ -10,7 +10,7 @@ param tags object = {} param accessTier string = 'Hot' param allowBlobPublicAccess bool = true param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true +param allowSharedKeyAccess bool = false param containers array = [] param defaultToOAuthAuthentication bool = false param deleteRetentionPolicy object = {} From 42cc361cbb88c0093390f80875205798852f7a9a Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 15 Mar 2024 07:03:05 +0000 Subject: [PATCH 077/112] Rebuild ARM templates --- Environments/APIM/azuredeploy.json | 5 +++-- Environments/FunctionApp/azuredeploy.json | 9 +++++---- Environments/OpenAISearch/azuredeploy.json | 19 ++++++++++++------- .../OpenAISummarization/azuredeploy.json | 11 ++++++----- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json index 95c5c42e..03d38820 100644 --- a/Environments/APIM/azuredeploy.json +++ b/Environments/APIM/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "11168061523890493302" + "templateHash": "4278591048699575854" } }, "parameters": { @@ -1583,7 +1583,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "3566538323657511454" + "templateHash": "16069210643566716899" } }, "parameters": { @@ -1658,6 +1658,7 @@ "properties": { "minimumTlsVersion": "[parameters('minimumTlsVersion')]", "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "allowSharedKeyAccess": false, "networkAcls": { "bypass": "AzureServices", "defaultAction": "Allow" diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index 21dc8212..25fa1405 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "16024393491195140895" + "templateHash": "5309824171399817062" } }, "parameters": { @@ -102,7 +102,8 @@ }, "kind": "StorageV2", "properties": { - "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]" + "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", + "allowSharedKeyAccess": false }, "tags": "[parameters('tags')]" }, @@ -122,8 +123,8 @@ "linuxFxVersion": "[variables('linexFxVersions')[parameters('runtime')]]", "appSettings": [ { - "name": "AzureWebJobsStorage", - "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}', variables('storageAcctName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAcctName')), '2022-05-01').keys[0].value)]" + "name": "AzureWebJobsStorage__accountName", + "value": "[variables('storageAcctName')]" }, { "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index a0da7559..d7c4b504 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "3847265105826312822" + "templateHash": "8618545896355817640" } }, "parameters": { @@ -650,7 +650,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "9784023372961823844" + "templateHash": "558853153341605687" } }, "parameters": { @@ -698,7 +698,8 @@ "kind": "[parameters('kind')]", "properties": { "customSubDomainName": "[parameters('customSubDomainName')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]" + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "disableLocalAuth": true }, "sku": "[parameters('sku')]" }, @@ -772,7 +773,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "9784023372961823844" + "templateHash": "558853153341605687" } }, "parameters": { @@ -820,7 +821,8 @@ "kind": "[parameters('kind')]", "properties": { "customSubDomainName": "[parameters('customSubDomainName')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]" + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "disableLocalAuth": true }, "sku": "[parameters('sku')]" }, @@ -998,6 +1000,9 @@ "publicNetworkAccess": { "value": "Enabled" }, + "allowSharedKeyAccess": { + "value": false + }, "sku": { "value": { "name": "Standard_ZRS" @@ -1025,7 +1030,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "546351219266755734" + "templateHash": "10058363838811997106" } }, "parameters": { @@ -1059,7 +1064,7 @@ }, "allowSharedKeyAccess": { "type": "bool", - "defaultValue": true + "defaultValue": false }, "defaultToOAuthAuthentication": { "type": "bool", diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index 42ab9fa9..0878a692 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "4415727950855254716" + "templateHash": "6718158956593452240" } }, "parameters": { @@ -250,7 +250,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "7233160254411631600" + "templateHash": "9150824237650559673" } }, "parameters": { @@ -280,7 +280,8 @@ "kind": "OpenAI", "properties": { "customSubDomainName": "[parameters('name')]", - "publicNetworkAccess": "Enabled" + "publicNetworkAccess": "Enabled", + "disableLocalAuth": true }, "sku": { "name": "S0" @@ -438,7 +439,7 @@ "_generator": { "name": "bicep", "version": "0.25.53.49325", - "templateHash": "11821310300065265050" + "templateHash": "1905516507240704714" } }, "parameters": { @@ -472,7 +473,7 @@ }, "allowSharedKeyAccess": { "type": "bool", - "defaultValue": true + "defaultValue": false }, "defaultToOAuthAuthentication": { "type": "bool", From 73102a12a08fc79e3d931ac71465d3e2306bb4b6 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 10 Apr 2024 14:17:23 +0800 Subject: [PATCH 078/112] add eShop template --- Environments/eShop/main.bicep | 43 +++++++ Environments/eShop/manifest.yaml | 21 ++++ Environments/eShop/resources.bicep | 194 +++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 Environments/eShop/main.bicep create mode 100644 Environments/eShop/manifest.yaml create mode 100644 Environments/eShop/resources.bicep diff --git a/Environments/eShop/main.bicep b/Environments/eShop/main.bicep new file mode 100644 index 00000000..68454945 --- /dev/null +++ b/Environments/eShop/main.bicep @@ -0,0 +1,43 @@ +@minLength(1) +@maxLength(64) +@description('Name of the environment that can be used as part of naming resource convention, the name of the resource group for your application will use this name, prefixed with rg-') +param environmentName string + +@minLength(1) +@description('The location used for all deployed resources') +param location string + +@secure() +@metadata({azd: { + type: 'inputs' + autoGenerate: { + eventbus: { + password: { len: 10 } + } + postgres: { + password: { len: 10 } + } + }} +}) +param inputs object + +var tags = { + 'azd-env-name': environmentName +} + +module resources 'resources.bicep' = { + name: 'resources' + params: { + location: location + tags: tags + inputs: inputs + } +} + +output MANAGED_IDENTITY_CLIENT_ID string = resources.outputs.MANAGED_IDENTITY_CLIENT_ID +output MANAGED_IDENTITY_NAME string = resources.outputs.MANAGED_IDENTITY_NAME +output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = resources.outputs.AZURE_LOG_ANALYTICS_WORKSPACE_NAME +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = resources.outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = resources.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID +output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_ID +output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN diff --git a/Environments/eShop/manifest.yaml b/Environments/eShop/manifest.yaml new file mode 100644 index 00000000..b3fa119a --- /dev/null +++ b/Environments/eShop/manifest.yaml @@ -0,0 +1,21 @@ +name: eShop +version: 1.0.0 +summary: eShop Reference Application +description: eShop Reference Application - "Northern Mountains" +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. test)" + description: "Name of the Environment" + type: "string" + required: false + default: "test" + +- id: "location" + name: "Region (e.g. eastus)" + description: "Region" + type: "string" + required: false + default: "eastus" diff --git a/Environments/eShop/resources.bicep b/Environments/eShop/resources.bicep new file mode 100644 index 00000000..7730fc8b --- /dev/null +++ b/Environments/eShop/resources.bicep @@ -0,0 +1,194 @@ +@description('The location used for all deployed resources') +param location string = resourceGroup().location + +@description('Tags that will be applied to all resources') +param tags object = {} +@secure() +param inputs object + + +var resourceToken = uniqueString(resourceGroup().id) + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: 'mi-${resourceToken}' + location: location + tags: tags +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-07-01' = { + name: replace('acr-${resourceToken}', '-', '') + location: location + sku: { + name: 'Basic' + } + properties: { + adminUserEnabled: true + } + tags: tags +} + +resource caeMiRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(containerRegistry.id, managedIdentity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')) + scope: containerRegistry + properties: { + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: 'law-${resourceToken}' + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } + tags: tags +} + +resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { + name: 'cae-${resourceToken}' + location: location + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } + tags: tags +} + +resource eventbus 'Microsoft.App/containerApps@2023-05-02-preview' = { + name: 'eventbus' + location: location + properties: { + environmentId: containerAppEnvironment.id + configuration: { + activeRevisionsMode: 'Single' + ingress: { + external: false + targetPort: 5672 + transport: 'tcp' + } + secrets: [ + { + name: 'rabbitmq-default-pass' + value: inputs.eventbus.password + } + ] + } + template: { + containers: [ + { + image: 'rabbitmq:3' + name: 'eventbus' + env: [ + { + name: 'RABBITMQ_DEFAULT_USER' + value: 'guest' + } + { + name: 'RABBITMQ_DEFAULT_PASS' + secretRef: 'rabbitmq-default-pass' + } + ] + } + ] + scale: { + minReplicas: 1 + } + } + } + tags: union(tags, {'aspire-resource-name': 'eventbus'}) +} + +resource postgres 'Microsoft.App/containerApps@2023-05-02-preview' = { + name: 'postgres' + location: location + properties: { + environmentId: containerAppEnvironment.id + configuration: { + activeRevisionsMode: 'Single' + ingress: { + external: false + targetPort: 5432 + transport: 'tcp' + } + secrets: [ + { + name: 'postgres-password' + value: inputs.postgres.password + } + ] + } + template: { + containers: [ + { + image: 'ankane/pgvector:latest' + name: 'postgres' + env: [ + { + name: 'POSTGRES_HOST_AUTH_METHOD' + value: 'scram-sha-256' + } + { + name: 'POSTGRES_INITDB_ARGS' + value: '--auth-host=scram-sha-256 --auth-local=scram-sha-256' + } + { + name: 'POSTGRES_PASSWORD' + secretRef: 'postgres-password' + } + ] + } + ] + scale: { + minReplicas: 1 + } + } + } + tags: union(tags, {'aspire-resource-name': 'postgres'}) +} + +resource redis 'Microsoft.App/containerApps@2023-05-02-preview' = { + name: 'redis' + location: location + properties: { + environmentId: containerAppEnvironment.id + configuration: { + activeRevisionsMode: 'Single' + ingress: { + external: false + targetPort: 6379 + transport: 'tcp' + } + } + template: { + containers: [ + { + image: 'redis:7.2.4' + name: 'redis' + } + ] + scale: { + minReplicas: 1 + } + } + } + tags: union(tags, {'aspire-resource-name': 'redis'}) +} + +output MANAGED_IDENTITY_CLIENT_ID string = managedIdentity.properties.clientId +output MANAGED_IDENTITY_NAME string = managedIdentity.name +output MANAGED_IDENTITY_PRINCIPAL_ID string = managedIdentity.properties.principalId +output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = logAnalyticsWorkspace.name +output AZURE_LOG_ANALYTICS_WORKSPACE_ID string = logAnalyticsWorkspace.id +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.properties.loginServer +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = managedIdentity.id +output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = containerAppEnvironment.id +output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = containerAppEnvironment.properties.defaultDomain From a5cf976953a62effc00f7ea7ae230a7079219823 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Wed, 10 Apr 2024 06:19:43 +0000 Subject: [PATCH 079/112] Rebuild ARM templates --- Environments/AKS/azuredeploy.json | 64 +-- Environments/APIM/azuredeploy.json | 44 +- .../App-Base-WebApp-ACA/azuredeploy.json | 96 ++--- .../App-Base-WebApp-AKS/azuredeploy.json | 28 +- Environments/AppVNet/azuredeploy.json | 12 +- Environments/ContainerApp/azuredeploy.json | 32 +- .../azuredeploy.json | 28 +- .../azuredeploy.json | 28 +- .../azuredeploy.json | 28 +- .../azuredeploy.json | 44 +- .../azuredeploy.json | 44 +- .../azuredeploy.json | 44 +- Environments/FunctionApp/azuredeploy.json | 4 +- Environments/OpenAISearch/azuredeploy.json | 64 +-- .../OpenAISummarization/azuredeploy.json | 36 +- Environments/Sandbox/azuredeploy.json | 4 +- Environments/Spring/azuredeploy.json | 12 +- Environments/Todo-Mongo-ACA/azuredeploy.json | 72 ++-- Environments/Todo-Mongo-AKS/azuredeploy.json | 28 +- .../Todo-Nodejs-Mongo-ACA/azuredeploy.json | 96 ++--- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 68 ++-- Environments/Todo-Shared-ACA/azuredeploy.json | 28 +- Environments/Todo-Shared-AKS/azuredeploy.json | 44 +- Environments/WebApp/azuredeploy.json | 4 +- Environments/eShop/azuredeploy.json | 378 ++++++++++++++++++ 25 files changed, 854 insertions(+), 476 deletions(-) create mode 100644 Environments/eShop/azuredeploy.json diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index 3f544eba..b7567a4f 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10018920614151267096" + "version": "0.26.54.24096", + "templateHash": "14705962401270339344" } }, "parameters": { @@ -243,8 +243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16060944584185565434" + "version": "0.26.54.24096", + "templateHash": "4834662464872905634" } }, "parameters": { @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10614293808878569427" + "version": "0.26.54.24096", + "templateHash": "17255267993693195617" } }, "parameters": { @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12339762446846171191" + "version": "0.26.54.24096", + "templateHash": "398383251887108056" } }, "parameters": { @@ -787,8 +787,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "203201205747518522" + "version": "0.26.54.24096", + "templateHash": "5711878933290893029" } }, "parameters": { @@ -932,8 +932,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7101107860953907100" + "version": "0.26.54.24096", + "templateHash": "17324306373551752378" } }, "parameters": { @@ -990,8 +990,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10350383361040663332" + "version": "0.26.54.24096", + "templateHash": "4546930119972086224" } }, "parameters": { @@ -1105,8 +1105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10326446962893102982" + "version": "0.26.54.24096", + "templateHash": "6446084179936994854" } }, "parameters": { @@ -1186,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12895087016061856845" + "version": "0.26.54.24096", + "templateHash": "10221031687472873373" } }, "parameters": { @@ -1293,8 +1293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "74163935805296023" + "version": "0.26.54.24096", + "templateHash": "6004410298470182087" } }, "parameters": { @@ -1353,8 +1353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10930228915954428963" + "version": "0.26.54.24096", + "templateHash": "2712602865217069438" } }, "parameters": { @@ -1532,8 +1532,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5173484044918305307" + "version": "0.26.54.24096", + "templateHash": "15989205648669899326" } }, "parameters": { @@ -1609,8 +1609,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5828371091540255080" + "version": "0.26.54.24096", + "templateHash": "8004480784637054546" } }, "parameters": { @@ -1659,8 +1659,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16989282822094195299" + "version": "0.26.54.24096", + "templateHash": "11537399683787126617" } }, "parameters": { @@ -1739,8 +1739,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12454312259379619990" + "version": "0.26.54.24096", + "templateHash": "10878265115034450635" } }, "parameters": { @@ -1801,8 +1801,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9513575600833160668" + "version": "0.26.54.24096", + "templateHash": "1426875865049868025" } }, "parameters": { diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json index 03d38820..ae091996 100644 --- a/Environments/APIM/azuredeploy.json +++ b/Environments/APIM/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4278591048699575854" + "version": "0.26.54.24096", + "templateHash": "6279479381596719762" } }, "parameters": { @@ -82,8 +82,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5828371091540255080" + "version": "0.26.54.24096", + "templateHash": "8004480784637054546" } }, "parameters": { @@ -132,8 +132,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16989282822094195299" + "version": "0.26.54.24096", + "templateHash": "11537399683787126617" } }, "parameters": { @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12454312259379619990" + "version": "0.26.54.24096", + "templateHash": "10878265115034450635" } }, "parameters": { @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9513575600833160668" + "version": "0.26.54.24096", + "templateHash": "1426875865049868025" } }, "parameters": { @@ -1582,8 +1582,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16069210643566716899" + "version": "0.26.54.24096", + "templateHash": "3986547581209693951" } }, "parameters": { @@ -1711,8 +1711,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5745686453184402361" + "version": "0.26.54.24096", + "templateHash": "5977381490546492812" } }, "parameters": { @@ -1811,8 +1811,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1361641401814185517" + "version": "0.26.54.24096", + "templateHash": "13689214219732288912" } }, "parameters": { @@ -2015,8 +2015,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1045760257801402220" + "version": "0.26.54.24096", + "templateHash": "13412871015897770137" } }, "parameters": { @@ -2263,8 +2263,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1795228908000942816" + "version": "0.26.54.24096", + "templateHash": "7053410133338541789" } }, "parameters": { @@ -2397,8 +2397,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8794806107235716835" + "version": "0.26.54.24096", + "templateHash": "5630097546441939340" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-ACA/azuredeploy.json b/Environments/App-Base-WebApp-ACA/azuredeploy.json index 65c1d9f7..0fb8ad41 100644 --- a/Environments/App-Base-WebApp-ACA/azuredeploy.json +++ b/Environments/App-Base-WebApp-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5915759848307494038" + "version": "0.26.54.24096", + "templateHash": "5906921002916319723" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14116108111976192358" + "version": "0.26.54.24096", + "templateHash": "18420764662990746250" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4766903245227392386" + "version": "0.26.54.24096", + "templateHash": "18309103684351395002" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5244656399300381833" + "version": "0.26.54.24096", + "templateHash": "8837302822615064546" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" + "version": "0.26.54.24096", + "templateHash": "15518016269231653446" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1912096201798605494" + "version": "0.26.54.24096", + "templateHash": "7330114957522770158" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11092891629527222377" + "version": "0.26.54.24096", + "templateHash": "15133572285528423972" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" + "version": "0.26.54.24096", + "templateHash": "4480412712998156633" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" + "version": "0.26.54.24096", + "templateHash": "15518016269231653446" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1912096201798605494" + "version": "0.26.54.24096", + "templateHash": "7330114957522770158" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "version": "0.26.54.24096", + "templateHash": "3191030148362316528" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "version": "0.26.54.24096", + "templateHash": "13527588968236853243" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "version": "0.26.54.24096", + "templateHash": "3220231753590578091" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2695,8 +2695,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" + "version": "0.26.54.24096", + "templateHash": "4552321833419182500" }, "description": "Creates an Azure Key Vault." }, @@ -2773,8 +2773,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2825,8 +2825,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -2906,8 +2906,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2971,8 +2971,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4282,8 +4282,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3036576769636454145" + "version": "0.26.54.24096", + "templateHash": "3385554986928067105" }, "description": "Creates an Azure API Management instance." }, @@ -4432,8 +4432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6615097664318461925" + "version": "0.26.54.24096", + "templateHash": "5801551854136161809" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json index 84d2235d..d1ee4b7c 100644 --- a/Environments/App-Base-WebApp-AKS/azuredeploy.json +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2845459636497042502" + "version": "0.26.54.24096", + "templateHash": "1751087585348510436" } }, "parameters": { @@ -219,8 +219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "version": "0.26.54.24096", + "templateHash": "3191030148362316528" } }, "parameters": { @@ -300,8 +300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "version": "0.26.54.24096", + "templateHash": "13527588968236853243" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -408,8 +408,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "version": "0.26.54.24096", + "templateHash": "3220231753590578091" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -469,8 +469,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" }, "description": "Creates an Azure Cosmos DB account." }, @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" + "version": "0.26.54.24096", + "templateHash": "4552321833419182500" }, "description": "Creates an Azure Key Vault." }, @@ -724,8 +724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" + "version": "0.26.54.24096", + "templateHash": "4480412712998156633" }, "description": "Assigns an Azure Key Vault access policy." }, diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json index 98c6d48b..ee5d8f75 100644 --- a/Environments/AppVNet/azuredeploy.json +++ b/Environments/AppVNet/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1536878747358182609" + "version": "0.26.54.24096", + "templateHash": "5876871532578709430" } }, "parameters": { @@ -81,8 +81,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "847261945363262167" + "version": "0.26.54.24096", + "templateHash": "800240006004044154" } }, "parameters": { @@ -369,8 +369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8155104801331874358" + "version": "0.26.54.24096", + "templateHash": "2391425913980408554" } }, "parameters": { diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json index 6363eb80..e7d3e340 100644 --- a/Environments/ContainerApp/azuredeploy.json +++ b/Environments/ContainerApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14152034777764657013" + "version": "0.26.54.24096", + "templateHash": "15939569857305320444" } }, "parameters": { @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5173484044918305307" + "version": "0.26.54.24096", + "templateHash": "15989205648669899326" } }, "parameters": { @@ -161,8 +161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2232453168194463623" + "version": "0.26.54.24096", + "templateHash": "17990678596458258198" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1835581463787778494" + "version": "0.26.54.24096", + "templateHash": "6228823906425620387" } }, "parameters": { @@ -293,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5616502198215845633" + "version": "0.26.54.24096", + "templateHash": "6416926961886581978" } }, "parameters": { @@ -434,8 +434,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10619128240462649164" + "version": "0.26.54.24096", + "templateHash": "8313026216379119506" } }, "parameters": { @@ -508,8 +508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17857818200713698273" + "version": "0.26.54.24096", + "templateHash": "5397344132030627297" } }, "parameters": { @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16989282822094195299" + "version": "0.26.54.24096", + "templateHash": "11537399683787126617" } }, "parameters": { diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json index 94f1cb0a..51b71b5a 100644 --- a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7010794115259852587" + "version": "0.26.54.24096", + "templateHash": "15561992108809927454" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10266908178814002202" + "version": "0.26.54.24096", + "templateHash": "1868577110995523309" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4766903245227392386" + "version": "0.26.54.24096", + "templateHash": "18309103684351395002" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json index 94f1cb0a..51b71b5a 100644 --- a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7010794115259852587" + "version": "0.26.54.24096", + "templateHash": "15561992108809927454" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10266908178814002202" + "version": "0.26.54.24096", + "templateHash": "1868577110995523309" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4766903245227392386" + "version": "0.26.54.24096", + "templateHash": "18309103684351395002" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json index 94f1cb0a..51b71b5a 100644 --- a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7010794115259852587" + "version": "0.26.54.24096", + "templateHash": "15561992108809927454" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10266908178814002202" + "version": "0.26.54.24096", + "templateHash": "1868577110995523309" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4766903245227392386" + "version": "0.26.54.24096", + "templateHash": "18309103684351395002" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json index 0b774988..63f70f55 100644 --- a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11426766062419716265" + "version": "0.26.54.24096", + "templateHash": "1499570709905675063" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13278918148041449332" + "version": "0.26.54.24096", + "templateHash": "5116950368212491210" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8184151159222198677" + "version": "0.26.54.24096", + "templateHash": "1485594887423027715" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6072270897511874144" + "version": "0.26.54.24096", + "templateHash": "6919390155052012096" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8205013527918052324" + "version": "0.26.54.24096", + "templateHash": "16891164712798672456" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json index 0b774988..63f70f55 100644 --- a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11426766062419716265" + "version": "0.26.54.24096", + "templateHash": "1499570709905675063" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13278918148041449332" + "version": "0.26.54.24096", + "templateHash": "5116950368212491210" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8184151159222198677" + "version": "0.26.54.24096", + "templateHash": "1485594887423027715" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6072270897511874144" + "version": "0.26.54.24096", + "templateHash": "6919390155052012096" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8205013527918052324" + "version": "0.26.54.24096", + "templateHash": "16891164712798672456" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json index 0b774988..63f70f55 100644 --- a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11426766062419716265" + "version": "0.26.54.24096", + "templateHash": "1499570709905675063" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13278918148041449332" + "version": "0.26.54.24096", + "templateHash": "5116950368212491210" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8184151159222198677" + "version": "0.26.54.24096", + "templateHash": "1485594887423027715" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6072270897511874144" + "version": "0.26.54.24096", + "templateHash": "6919390155052012096" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8205013527918052324" + "version": "0.26.54.24096", + "templateHash": "16891164712798672456" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index 25fa1405..f18e17bf 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5309824171399817062" + "version": "0.26.54.24096", + "templateHash": "17337791585652966371" } }, "parameters": { diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index d7c4b504..53a31f4d 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8618545896355817640" + "version": "0.26.54.24096", + "templateHash": "7270598933171374278" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5484207409505496262" + "version": "0.26.54.24096", + "templateHash": "11596624744744955282" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13872150863523986003" + "version": "0.26.54.24096", + "templateHash": "13746138269004593570" } }, "parameters": { @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "558853153341605687" + "version": "0.26.54.24096", + "templateHash": "15261049305082890883" } }, "parameters": { @@ -772,8 +772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "558853153341605687" + "version": "0.26.54.24096", + "templateHash": "15261049305082890883" } }, "parameters": { @@ -902,8 +902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4262027139711048800" + "version": "0.26.54.24096", + "templateHash": "15486167099316024236" } }, "parameters": { @@ -1029,8 +1029,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10058363838811997106" + "version": "0.26.54.24096", + "templateHash": "15302819503019861766" } }, "parameters": { @@ -1201,8 +1201,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1265,8 +1265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1329,8 +1329,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1393,8 +1393,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1457,8 +1457,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1521,8 +1521,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1585,8 +1585,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1652,8 +1652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { @@ -1719,8 +1719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.26.54.24096", + "templateHash": "2682974829435550284" } }, "parameters": { diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index 0878a692..8d750669 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6718158956593452240" + "version": "0.26.54.24096", + "templateHash": "2872422258645152072" } }, "parameters": { @@ -249,8 +249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9150824237650559673" + "version": "0.26.54.24096", + "templateHash": "16884556765387010262" } }, "parameters": { @@ -343,8 +343,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7073695551235566262" + "version": "0.26.54.24096", + "templateHash": "14111957338638220978" } }, "parameters": { @@ -438,8 +438,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1905516507240704714" + "version": "0.26.54.24096", + "templateHash": "9798428962718279486" } }, "parameters": { @@ -603,8 +603,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10674317264156662708" + "version": "0.26.54.24096", + "templateHash": "14238317958653849476" } }, "parameters": { @@ -660,8 +660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10674317264156662708" + "version": "0.26.54.24096", + "templateHash": "14238317958653849476" } }, "parameters": { @@ -717,8 +717,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10674317264156662708" + "version": "0.26.54.24096", + "templateHash": "14238317958653849476" } }, "parameters": { @@ -774,8 +774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10674317264156662708" + "version": "0.26.54.24096", + "templateHash": "14238317958653849476" } }, "parameters": { @@ -831,8 +831,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10674317264156662708" + "version": "0.26.54.24096", + "templateHash": "14238317958653849476" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index 657dc9c5..0a957344 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11408007220382628824" + "version": "0.26.54.24096", + "templateHash": "6609161197123188811" } }, "resources": [] diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index 37e08254..48d05374 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "16870146603885581990" + "version": "0.26.54.24096", + "templateHash": "392445969946894024" } }, "parameters": { @@ -143,8 +143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12452574487687496324" + "version": "0.26.54.24096", + "templateHash": "3493762751568630334" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1175982527140700654" + "version": "0.26.54.24096", + "templateHash": "9105768340151577374" } }, "parameters": { diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index c7957e74..166348eb 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14453627926966060746" + "version": "0.26.54.24096", + "templateHash": "15721463991948645518" } }, "parameters": { @@ -295,8 +295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12761856845851580026" + "version": "0.26.54.24096", + "templateHash": "15275434175670248923" } }, "parameters": { @@ -410,8 +410,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12494944888544926830" + "version": "0.26.54.24096", + "templateHash": "12646603870028624690" }, "description": "Creates or updates an existing Azure Container App." }, @@ -669,8 +669,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10348832124348087726" + "version": "0.26.54.24096", + "templateHash": "17904700932623043957" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -923,8 +923,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12086032597939785" + "version": "0.26.54.24096", + "templateHash": "10402974300023168185" } }, "parameters": { @@ -1162,8 +1162,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" + "version": "0.26.54.24096", + "templateHash": "4480412712998156633" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1283,8 +1283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12494944888544926830" + "version": "0.26.54.24096", + "templateHash": "12646603870028624690" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1542,8 +1542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10348832124348087726" + "version": "0.26.54.24096", + "templateHash": "17904700932623043957" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1796,8 +1796,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1937,8 +1937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -2114,8 +2114,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "version": "0.26.54.24096", + "templateHash": "3191030148362316528" } }, "parameters": { @@ -2195,8 +2195,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "version": "0.26.54.24096", + "templateHash": "13527588968236853243" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2303,8 +2303,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "version": "0.26.54.24096", + "templateHash": "3220231753590578091" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2364,8 +2364,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2544,8 +2544,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" + "version": "0.26.54.24096", + "templateHash": "4552321833419182500" }, "description": "Creates an Azure Key Vault." }, @@ -2624,8 +2624,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3036576769636454145" + "version": "0.26.54.24096", + "templateHash": "3385554986928067105" }, "description": "Creates an Azure API Management instance." }, @@ -2771,8 +2771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6615097664318461925" + "version": "0.26.54.24096", + "templateHash": "5801551854136161809" } }, "parameters": { diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index 84d2235d..d1ee4b7c 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2845459636497042502" + "version": "0.26.54.24096", + "templateHash": "1751087585348510436" } }, "parameters": { @@ -219,8 +219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "version": "0.26.54.24096", + "templateHash": "3191030148362316528" } }, "parameters": { @@ -300,8 +300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "version": "0.26.54.24096", + "templateHash": "13527588968236853243" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -408,8 +408,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "version": "0.26.54.24096", + "templateHash": "3220231753590578091" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -469,8 +469,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" }, "description": "Creates an Azure Cosmos DB account." }, @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" + "version": "0.26.54.24096", + "templateHash": "4552321833419182500" }, "description": "Creates an Azure Key Vault." }, @@ -724,8 +724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" + "version": "0.26.54.24096", + "templateHash": "4480412712998156633" }, "description": "Assigns an Azure Key Vault access policy." }, diff --git a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json index 65c1d9f7..0fb8ad41 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5915759848307494038" + "version": "0.26.54.24096", + "templateHash": "5906921002916319723" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14116108111976192358" + "version": "0.26.54.24096", + "templateHash": "18420764662990746250" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4766903245227392386" + "version": "0.26.54.24096", + "templateHash": "18309103684351395002" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5244656399300381833" + "version": "0.26.54.24096", + "templateHash": "8837302822615064546" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" + "version": "0.26.54.24096", + "templateHash": "15518016269231653446" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1912096201798605494" + "version": "0.26.54.24096", + "templateHash": "7330114957522770158" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11092891629527222377" + "version": "0.26.54.24096", + "templateHash": "15133572285528423972" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" + "version": "0.26.54.24096", + "templateHash": "4480412712998156633" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17242409915151931414" + "version": "0.26.54.24096", + "templateHash": "15518016269231653446" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1912096201798605494" + "version": "0.26.54.24096", + "templateHash": "7330114957522770158" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "version": "0.26.54.24096", + "templateHash": "3191030148362316528" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "version": "0.26.54.24096", + "templateHash": "13527588968236853243" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "version": "0.26.54.24096", + "templateHash": "3220231753590578091" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2695,8 +2695,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" + "version": "0.26.54.24096", + "templateHash": "4552321833419182500" }, "description": "Creates an Azure Key Vault." }, @@ -2773,8 +2773,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2825,8 +2825,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -2906,8 +2906,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2971,8 +2971,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4282,8 +4282,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3036576769636454145" + "version": "0.26.54.24096", + "templateHash": "3385554986928067105" }, "description": "Creates an Azure API Management instance." }, @@ -4432,8 +4432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6615097664318461925" + "version": "0.26.54.24096", + "templateHash": "5801551854136161809" } }, "parameters": { diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json index 9626594f..1cd7f8ca 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13440997233145271475" + "version": "0.26.54.24096", + "templateHash": "16064494889423816665" } }, "parameters": { @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4109929557657560885" + "version": "0.26.54.24096", + "templateHash": "5221068689828227975" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -593,8 +593,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8184151159222198677" + "version": "0.26.54.24096", + "templateHash": "1485594887423027715" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -874,8 +874,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6072270897511874144" + "version": "0.26.54.24096", + "templateHash": "6919390155052012096" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -937,8 +937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -1107,8 +1107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1167,8 +1167,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8205013527918052324" + "version": "0.26.54.24096", + "templateHash": "16891164712798672456" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1225,8 +1225,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "815983560956742247" + "version": "0.26.54.24096", + "templateHash": "4480412712998156633" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1341,8 +1341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5730728686647632614" + "version": "0.26.54.24096", + "templateHash": "3191030148362316528" } }, "parameters": { @@ -1422,8 +1422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "14549161001187918251" + "version": "0.26.54.24096", + "templateHash": "13527588968236853243" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1530,8 +1530,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8317058180807592714" + "version": "0.26.54.24096", + "templateHash": "3220231753590578091" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1591,8 +1591,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13614361263700788271" + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1771,8 +1771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17948623451174129396" + "version": "0.26.54.24096", + "templateHash": "4552321833419182500" }, "description": "Creates an Azure Key Vault." }, @@ -1849,8 +1849,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1901,8 +1901,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -1982,8 +1982,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2047,8 +2047,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-ACA/azuredeploy.json b/Environments/Todo-Shared-ACA/azuredeploy.json index 94f1cb0a..51b71b5a 100644 --- a/Environments/Todo-Shared-ACA/azuredeploy.json +++ b/Environments/Todo-Shared-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7010794115259852587" + "version": "0.26.54.24096", + "templateHash": "15561992108809927454" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10266908178814002202" + "version": "0.26.54.24096", + "templateHash": "1868577110995523309" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4766903245227392386" + "version": "0.26.54.24096", + "templateHash": "18309103684351395002" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-AKS/azuredeploy.json b/Environments/Todo-Shared-AKS/azuredeploy.json index 0b774988..63f70f55 100644 --- a/Environments/Todo-Shared-AKS/azuredeploy.json +++ b/Environments/Todo-Shared-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11426766062419716265" + "version": "0.26.54.24096", + "templateHash": "1499570709905675063" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13278918148041449332" + "version": "0.26.54.24096", + "templateHash": "5116950368212491210" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8184151159222198677" + "version": "0.26.54.24096", + "templateHash": "1485594887423027715" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "6072270897511874144" + "version": "0.26.54.24096", + "templateHash": "6919390155052012096" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12834334744516280883" + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15144906240959446537" + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "8205013527918052324" + "version": "0.26.54.24096", + "templateHash": "16891164712798672456" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10041669792322197047" + "version": "0.26.54.24096", + "templateHash": "54270050405814058" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "9622176141085970536" + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "1335628967363670282" + "version": "0.26.54.24096", + "templateHash": "1697740909761260680" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "2145880658446193205" + "version": "0.26.54.24096", + "templateHash": "623850250427461329" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 23e50e38..036bbe64 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13878161974033062355" + "version": "0.26.54.24096", + "templateHash": "5321697744522399711" } }, "parameters": { diff --git a/Environments/eShop/azuredeploy.json b/Environments/eShop/azuredeploy.json new file mode 100644 index 00000000..5f0cd525 --- /dev/null +++ b/Environments/eShop/azuredeploy.json @@ -0,0 +1,378 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "9798675544251830912" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the environment that can be used as part of naming resource convention, the name of the resource group for your application will use this name, prefixed with rg-" + } + }, + "location": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "The location used for all deployed resources" + } + }, + "inputs": { + "type": "secureObject", + "metadata": { + "azd": { + "type": "inputs", + "autoGenerate": { + "eventbus": { + "password": { + "len": 10 + } + }, + "postgres": { + "password": { + "len": 10 + } + } + } + } + } + } + }, + "variables": { + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "resources", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "inputs": { + "value": "[parameters('inputs')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "13501501416131613708" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location used for all deployed resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags that will be applied to all resources" + } + }, + "inputs": { + "type": "secureObject" + } + }, + "variables": { + "resourceToken": "[uniqueString(resourceGroup().id)]" + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[format('mi-{0}', variables('resourceToken'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-07-01", + "name": "[replace(format('acr-{0}', variables('resourceToken')), '-', '')]", + "location": "[parameters('location')]", + "sku": { + "name": "Basic" + }, + "properties": { + "adminUserEnabled": true + }, + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', replace(format('acr-{0}', variables('resourceToken')), '-', ''))]", + "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', replace(format('acr-{0}', variables('resourceToken')), '-', '')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('mi-{0}', variables('resourceToken'))), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d'))]", + "properties": { + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('mi-{0}', variables('resourceToken'))), '2023-01-31').principalId]", + "principalType": "ServicePrincipal", + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', replace(format('acr-{0}', variables('resourceToken')), '-', ''))]", + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('mi-{0}', variables('resourceToken')))]" + ] + }, + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[format('law-{0}', variables('resourceToken'))]", + "location": "[parameters('location')]", + "properties": { + "sku": { + "name": "PerGB2018" + } + }, + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-05-01", + "name": "[format('cae-{0}', variables('resourceToken'))]", + "location": "[parameters('location')]", + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', format('law-{0}', variables('resourceToken'))), '2022-10-01').customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', format('law-{0}', variables('resourceToken'))), '2022-10-01').primarySharedKey]" + } + } + }, + "tags": "[parameters('tags')]", + "dependsOn": [ + "[resourceId('Microsoft.OperationalInsights/workspaces', format('law-{0}', variables('resourceToken')))]" + ] + }, + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-05-02-preview", + "name": "eventbus", + "location": "[parameters('location')]", + "properties": { + "environmentId": "[resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken')))]", + "configuration": { + "activeRevisionsMode": "Single", + "ingress": { + "external": false, + "targetPort": 5672, + "transport": "tcp" + }, + "secrets": [ + { + "name": "rabbitmq-default-pass", + "value": "[parameters('inputs').eventbus.password]" + } + ] + }, + "template": { + "containers": [ + { + "image": "rabbitmq:3", + "name": "eventbus", + "env": [ + { + "name": "RABBITMQ_DEFAULT_USER", + "value": "guest" + }, + { + "name": "RABBITMQ_DEFAULT_PASS", + "secretRef": "rabbitmq-default-pass" + } + ] + } + ], + "scale": { + "minReplicas": 1 + } + } + }, + "tags": "[union(parameters('tags'), createObject('aspire-resource-name', 'eventbus'))]", + "dependsOn": [ + "[resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken')))]" + ] + }, + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-05-02-preview", + "name": "postgres", + "location": "[parameters('location')]", + "properties": { + "environmentId": "[resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken')))]", + "configuration": { + "activeRevisionsMode": "Single", + "ingress": { + "external": false, + "targetPort": 5432, + "transport": "tcp" + }, + "secrets": [ + { + "name": "postgres-password", + "value": "[parameters('inputs').postgres.password]" + } + ] + }, + "template": { + "containers": [ + { + "image": "ankane/pgvector:latest", + "name": "postgres", + "env": [ + { + "name": "POSTGRES_HOST_AUTH_METHOD", + "value": "scram-sha-256" + }, + { + "name": "POSTGRES_INITDB_ARGS", + "value": "--auth-host=scram-sha-256 --auth-local=scram-sha-256" + }, + { + "name": "POSTGRES_PASSWORD", + "secretRef": "postgres-password" + } + ] + } + ], + "scale": { + "minReplicas": 1 + } + } + }, + "tags": "[union(parameters('tags'), createObject('aspire-resource-name', 'postgres'))]", + "dependsOn": [ + "[resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken')))]" + ] + }, + { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-05-02-preview", + "name": "redis", + "location": "[parameters('location')]", + "properties": { + "environmentId": "[resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken')))]", + "configuration": { + "activeRevisionsMode": "Single", + "ingress": { + "external": false, + "targetPort": 6379, + "transport": "tcp" + } + }, + "template": { + "containers": [ + { + "image": "redis:7.2.4", + "name": "redis" + } + ], + "scale": { + "minReplicas": 1 + } + } + }, + "tags": "[union(parameters('tags'), createObject('aspire-resource-name', 'redis'))]", + "dependsOn": [ + "[resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken')))]" + ] + } + ], + "outputs": { + "MANAGED_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('mi-{0}', variables('resourceToken'))), '2023-01-31').clientId]" + }, + "MANAGED_IDENTITY_NAME": { + "type": "string", + "value": "[format('mi-{0}', variables('resourceToken'))]" + }, + "MANAGED_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('mi-{0}', variables('resourceToken'))), '2023-01-31').principalId]" + }, + "AZURE_LOG_ANALYTICS_WORKSPACE_NAME": { + "type": "string", + "value": "[format('law-{0}', variables('resourceToken'))]" + }, + "AZURE_LOG_ANALYTICS_WORKSPACE_ID": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', format('law-{0}', variables('resourceToken')))]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', replace(format('acr-{0}', variables('resourceToken')), '-', '')), '2023-07-01').loginServer]" + }, + "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID": { + "type": "string", + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('mi-{0}', variables('resourceToken')))]" + }, + "AZURE_CONTAINER_APPS_ENVIRONMENT_ID": { + "type": "string", + "value": "[resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken')))]" + }, + "AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN": { + "type": "string", + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', format('cae-{0}', variables('resourceToken'))), '2023-05-01').defaultDomain]" + } + } + } + } + } + ], + "outputs": { + "MANAGED_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.MANAGED_IDENTITY_CLIENT_ID.value]" + }, + "MANAGED_IDENTITY_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.MANAGED_IDENTITY_NAME.value]" + }, + "AZURE_LOG_ANALYTICS_WORKSPACE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.AZURE_LOG_ANALYTICS_WORKSPACE_NAME.value]" + }, + "AZURE_CONTAINER_REGISTRY_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT.value]" + }, + "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID.value]" + }, + "AZURE_CONTAINER_APPS_ENVIRONMENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_ID.value]" + }, + "AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'resources'), '2022-09-01').outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN.value]" + } + } +} \ No newline at end of file From 3fb52780f30467fb79d7bb42d254ab2bef9c9094 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 10 Apr 2024 14:39:47 +0800 Subject: [PATCH 080/112] location give the default value --- Environments/eShop/main.bicep | 2 +- Environments/eShop/manifest.yaml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Environments/eShop/main.bicep b/Environments/eShop/main.bicep index 68454945..7cf6026e 100644 --- a/Environments/eShop/main.bicep +++ b/Environments/eShop/main.bicep @@ -5,7 +5,7 @@ param environmentName string @minLength(1) @description('The location used for all deployed resources') -param location string +param location string = resourceGroup().location @secure() @metadata({azd: { diff --git a/Environments/eShop/manifest.yaml b/Environments/eShop/manifest.yaml index b3fa119a..d577f2e2 100644 --- a/Environments/eShop/manifest.yaml +++ b/Environments/eShop/manifest.yaml @@ -10,8 +10,7 @@ parameters: name: "Environment Name (e.g. test)" description: "Name of the Environment" type: "string" - required: false - default: "test" + required: true - id: "location" name: "Region (e.g. eastus)" From a023cdc14cf280d175270277028aa80c76b4287a Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Wed, 10 Apr 2024 06:42:05 +0000 Subject: [PATCH 081/112] Rebuild ARM templates --- Environments/eShop/azuredeploy.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Environments/eShop/azuredeploy.json b/Environments/eShop/azuredeploy.json index 5f0cd525..b20d8565 100644 --- a/Environments/eShop/azuredeploy.json +++ b/Environments/eShop/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "9798675544251830912" + "templateHash": "5335287364340536322" } }, "parameters": { @@ -19,6 +19,7 @@ }, "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "minLength": 1, "metadata": { "description": "The location used for all deployed resources" From d64c893d6b3861430af9e42fb8f208b4b8fec6c1 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 10 Apr 2024 15:02:21 +0800 Subject: [PATCH 082/112] split password for eventbus and postgre --- Environments/eShop/main.bicep | 21 ++++++++------------- Environments/eShop/manifest.yaml | 14 ++++++++++++++ Environments/eShop/resources.bicep | 11 ++++++++--- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Environments/eShop/main.bicep b/Environments/eShop/main.bicep index 7cf6026e..5dcd9d62 100644 --- a/Environments/eShop/main.bicep +++ b/Environments/eShop/main.bicep @@ -8,18 +8,12 @@ param environmentName string param location string = resourceGroup().location @secure() -@metadata({azd: { - type: 'inputs' - autoGenerate: { - eventbus: { - password: { len: 10 } - } - postgres: { - password: { len: 10 } - } - }} -}) -param inputs object +@minLength(10) +param eventbusPassword string + +@secure() +@minLength(10) +param postgresPassword string var tags = { 'azd-env-name': environmentName @@ -28,9 +22,10 @@ var tags = { module resources 'resources.bicep' = { name: 'resources' params: { + eventbusPassword: eventbusPassword + postgresPassword: postgresPassword location: location tags: tags - inputs: inputs } } diff --git a/Environments/eShop/manifest.yaml b/Environments/eShop/manifest.yaml index d577f2e2..727737e6 100644 --- a/Environments/eShop/manifest.yaml +++ b/Environments/eShop/manifest.yaml @@ -12,9 +12,23 @@ parameters: type: "string" required: true +- id: "eventbusPassword" + name: "Eventbus Password" + description: "Password for the Eventbus" + type: "string" + required: true + +- id: "postgresPassword" + name: "Postgres Password" + description: "Password for the Postgres" + type: "string" + required: true + - id: "location" name: "Region (e.g. eastus)" description: "Region" type: "string" required: false default: "eastus" + + diff --git a/Environments/eShop/resources.bicep b/Environments/eShop/resources.bicep index 7730fc8b..62543b6f 100644 --- a/Environments/eShop/resources.bicep +++ b/Environments/eShop/resources.bicep @@ -3,9 +3,14 @@ param location string = resourceGroup().location @description('Tags that will be applied to all resources') param tags object = {} + @secure() -param inputs object +@minLength(10) +param eventbusPassword string +@secure() +@minLength(10) +param postgresPassword string var resourceToken = uniqueString(resourceGroup().id) @@ -78,7 +83,7 @@ resource eventbus 'Microsoft.App/containerApps@2023-05-02-preview' = { secrets: [ { name: 'rabbitmq-default-pass' - value: inputs.eventbus.password + value: eventbusPassword } ] } @@ -122,7 +127,7 @@ resource postgres 'Microsoft.App/containerApps@2023-05-02-preview' = { secrets: [ { name: 'postgres-password' - value: inputs.postgres.password + value: postgresPassword } ] } From 92f115803b3909be2e895f31d8990528f52f0dbd Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Wed, 10 Apr 2024 07:05:12 +0000 Subject: [PATCH 083/112] Rebuild ARM templates --- Environments/eShop/azuredeploy.json | 52 +++++++++++++---------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/Environments/eShop/azuredeploy.json b/Environments/eShop/azuredeploy.json index b20d8565..1a161bb2 100644 --- a/Environments/eShop/azuredeploy.json +++ b/Environments/eShop/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5335287364340536322" + "templateHash": "6443241281576436581" } }, "parameters": { @@ -25,25 +25,13 @@ "description": "The location used for all deployed resources" } }, - "inputs": { - "type": "secureObject", - "metadata": { - "azd": { - "type": "inputs", - "autoGenerate": { - "eventbus": { - "password": { - "len": 10 - } - }, - "postgres": { - "password": { - "len": 10 - } - } - } - } - } + "eventbusPassword": { + "type": "securestring", + "minLength": 10 + }, + "postgresPassword": { + "type": "securestring", + "minLength": 10 } }, "variables": { @@ -62,14 +50,17 @@ }, "mode": "Incremental", "parameters": { + "eventbusPassword": { + "value": "[parameters('eventbusPassword')]" + }, + "postgresPassword": { + "value": "[parameters('postgresPassword')]" + }, "location": { "value": "[parameters('location')]" }, "tags": { "value": "[variables('tags')]" - }, - "inputs": { - "value": "[parameters('inputs')]" } }, "template": { @@ -79,7 +70,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "13501501416131613708" + "templateHash": "7811954589280961793" } }, "parameters": { @@ -97,8 +88,13 @@ "description": "Tags that will be applied to all resources" } }, - "inputs": { - "type": "secureObject" + "eventbusPassword": { + "type": "securestring", + "minLength": 10 + }, + "postgresPassword": { + "type": "securestring", + "minLength": 10 } }, "variables": { @@ -188,7 +184,7 @@ "secrets": [ { "name": "rabbitmq-default-pass", - "value": "[parameters('inputs').eventbus.password]" + "value": "[parameters('eventbusPassword')]" } ] }, @@ -236,7 +232,7 @@ "secrets": [ { "name": "postgres-password", - "value": "[parameters('inputs').postgres.password]" + "value": "[parameters('postgresPassword')]" } ] }, From 21586fe4237befd3f0083eb355626fb652a15cd7 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 15 Apr 2024 21:15:21 +0800 Subject: [PATCH 084/112] add aks-store-demo --- .../AKS-Store-Demo/abbreviations.json | 136 ++++++++ .../app/aks-managed-cluster.bicep | 136 ++++++++ Environments/AKS-Store-Demo/app/db.bicep | 55 +++ .../AKS-Store-Demo/app/get-keys.bicep | 35 ++ .../AKS-Store-Demo/app/identity.bicep | 53 +++ Environments/AKS-Store-Demo/app/monitor.bicep | 12 + .../AKS-Store-Demo/app/observability.bicep | 327 ++++++++++++++++++ .../AKS-Store-Demo/app/servicebus.bicep | 70 ++++ .../core/ai/cognitiveservices.bicep | 53 +++ .../core/database/cosmos/cosmos-account.bicep | 49 +++ .../cosmos/mongo/cosmos-mongo-account.bicep | 23 ++ .../cosmos/mongo/cosmos-mongo-db.bicep | 47 +++ .../cosmos/sql/cosmos-sql-account.bicep | 22 ++ .../database/cosmos/sql/cosmos-sql-db.bicep | 74 ++++ .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 ++ .../core/host/container-registry.bicep | 83 +++++ .../core/monitor/loganalytics.bicep | 22 ++ .../core/security/keyvault-access.bicep | 22 ++ .../core/security/keyvault-secret.bicep | 31 ++ .../core/security/keyvault.bicep | 26 ++ .../core/security/registry-access.bicep | 19 + .../AKS-Store-Demo/core/security/role.bicep | 21 ++ Environments/AKS-Store-Demo/main.bicep | 227 ++++++++++++ Environments/AKS-Store-Demo/manifest.yaml | 28 ++ 25 files changed, 1620 insertions(+) create mode 100644 Environments/AKS-Store-Demo/abbreviations.json create mode 100644 Environments/AKS-Store-Demo/app/aks-managed-cluster.bicep create mode 100644 Environments/AKS-Store-Demo/app/db.bicep create mode 100644 Environments/AKS-Store-Demo/app/get-keys.bicep create mode 100644 Environments/AKS-Store-Demo/app/identity.bicep create mode 100644 Environments/AKS-Store-Demo/app/monitor.bicep create mode 100644 Environments/AKS-Store-Demo/app/observability.bicep create mode 100644 Environments/AKS-Store-Demo/app/servicebus.bicep create mode 100644 Environments/AKS-Store-Demo/core/ai/cognitiveservices.bicep create mode 100644 Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/AKS-Store-Demo/core/host/container-registry.bicep create mode 100644 Environments/AKS-Store-Demo/core/monitor/loganalytics.bicep create mode 100644 Environments/AKS-Store-Demo/core/security/keyvault-access.bicep create mode 100644 Environments/AKS-Store-Demo/core/security/keyvault-secret.bicep create mode 100644 Environments/AKS-Store-Demo/core/security/keyvault.bicep create mode 100644 Environments/AKS-Store-Demo/core/security/registry-access.bicep create mode 100644 Environments/AKS-Store-Demo/core/security/role.bicep create mode 100644 Environments/AKS-Store-Demo/main.bicep create mode 100644 Environments/AKS-Store-Demo/manifest.yaml diff --git a/Environments/AKS-Store-Demo/abbreviations.json b/Environments/AKS-Store-Demo/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/AKS-Store-Demo/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/AKS-Store-Demo/app/aks-managed-cluster.bicep b/Environments/AKS-Store-Demo/app/aks-managed-cluster.bicep new file mode 100644 index 00000000..cff16f8d --- /dev/null +++ b/Environments/AKS-Store-Demo/app/aks-managed-cluster.bicep @@ -0,0 +1,136 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = '' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + securityProfile:{ + workloadIdentity: { + enabled: true + } + } + oidcIssuerProfile: { + enabled: true + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} + +output clusterId string = aks.id diff --git a/Environments/AKS-Store-Demo/app/db.bicep b/Environments/AKS-Store-Demo/app/db.bicep new file mode 100644 index 00000000..9fe6e264 --- /dev/null +++ b/Environments/AKS-Store-Demo/app/db.bicep @@ -0,0 +1,55 @@ +@allowed([ + 'MongoDB' + 'GlobalDocumentDB' +]) +param kind string +param resourceToken string +param location string +param keyVaultName string +param tags object = {} +param cosmosDatabaseName string = 'orderdb' + +@description('The collections to create in the database') +param collections array = [ + { + id: 'orders' + name: 'orders' + shardKey: 'Hash' + indexKey: '_id' + throughput: 400 + } +] + +// the application database +module cosmosMongo '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = if(kind == 'MongoDB') { + name: 'cosmos-mongo' + params: { + accountName: 'cosmos-${resourceToken}' + databaseName: cosmosDatabaseName + location: location + collections: collections + tags: tags + keyVaultName: keyVaultName + } +} + +module cosmosSql '../core/database/cosmos/sql/cosmos-sql-db.bicep' = if(kind == 'GlobalDocumentDB') { + name: 'cosmos-sql' + params: { + accountName: 'cosmos-${resourceToken}' + databaseName: cosmosDatabaseName + location: location + containers: [ + { + name: 'orders' + id: 'orders' + partitionKey: '/storeId' + } + ] + tags: tags + keyVaultName: keyVaultName + } +} + +output name string = 'cosmos-${resourceToken}' +output endpoint string = kind == 'MongoDB' ? 'mongodb://cosmos-${resourceToken}.mongo.cosmos.azure.com:10255/?retryWrites=false' : 'https://cosmos-${resourceToken}.documents.azure.com:443/' diff --git a/Environments/AKS-Store-Demo/app/get-keys.bicep b/Environments/AKS-Store-Demo/app/get-keys.bicep new file mode 100644 index 00000000..ea501e5d --- /dev/null +++ b/Environments/AKS-Store-Demo/app/get-keys.bicep @@ -0,0 +1,35 @@ +param openAiName string +param openAiKeyName string = 'AZURE-OPENAI-KEY' +param cosmosAccountName string +param cosmosKeyName string = 'AZURE-COSMOS-KEY' +param keyVaultName string + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: openAiName +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: cosmosAccountName +} + +// create key vault secrets +module openAiKey '../core/security/keyvault-secret.bicep' = { + name: 'openAiKey' + params: { + name: openAiKeyName + keyVaultName: keyVaultName + secretValue: account.listKeys().key1 + } +} + +module cosmosKey '../core/security/keyvault-secret.bicep' = { + name: 'cosmosKey' + params: { + name: cosmosKeyName + keyVaultName: keyVaultName + secretValue: cosmos.listKeys().primaryMasterKey + } +} + +output openAiKey string = openAiKeyName +output cosmosKey string = cosmosKeyName diff --git a/Environments/AKS-Store-Demo/app/identity.bicep b/Environments/AKS-Store-Demo/app/identity.bicep new file mode 100644 index 00000000..71667acd --- /dev/null +++ b/Environments/AKS-Store-Demo/app/identity.bicep @@ -0,0 +1,53 @@ +param name string +param location string +param tags object = {} +param principalId string +param AZURE_AKS_NAMESPACE string +param clusterName string + +// identity for the openai +resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: name + location: location + tags: tags +} + +// federated credential for the openai +resource federatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = { + name: name + parent: identity + properties: { + audiences: ['api://AzureADTokenExchange'] + issuer: aks.properties.oidcIssuerProfile.issuerURL + subject: 'system:serviceaccount:${AZURE_AKS_NAMESPACE}:ai-service-account' + } +} + +// role definition for the openai +var openAiUserRole = '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + +// role assignment for the openai +module roleAssignment '../core/security/role.bicep' = { + name: 'roleAssignment' + params: { + principalId: identity.properties.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: openAiUserRole + } +} + +module roleAssignmentForMe '../core/security/role.bicep' = { + name: 'roleAssignmentForMe' + params: { + principalId: principalId + principalType: 'User' + roleDefinitionId: openAiUserRole + } +} + +resource aks 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' existing = { + name: clusterName +} + +output principalId string = identity.properties.principalId +output clientId string = identity.properties.clientId diff --git a/Environments/AKS-Store-Demo/app/monitor.bicep b/Environments/AKS-Store-Demo/app/monitor.bicep new file mode 100644 index 00000000..c0cd74bc --- /dev/null +++ b/Environments/AKS-Store-Demo/app/monitor.bicep @@ -0,0 +1,12 @@ +param name string +param location string +param tags object = {} + +resource monitor 'Microsoft.Monitor/accounts@2023-04-03' = { + name: name + location: location + tags: tags +} + +output id string = monitor.id +output name string = monitor.name diff --git a/Environments/AKS-Store-Demo/app/observability.bicep b/Environments/AKS-Store-Demo/app/observability.bicep new file mode 100644 index 00000000..671d06cd --- /dev/null +++ b/Environments/AKS-Store-Demo/app/observability.bicep @@ -0,0 +1,327 @@ +param name string +param location string +param tags object = {} + +param monitorName string +param monitorId string +param principalId string + +param clusterId string +param clusterName string + +param logAnalyticsId string +param logAnalyticsName string + +resource grafana 'Microsoft.Dashboard/grafana@2022-08-01' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + grafanaIntegrations: { + azureMonitorWorkspaceIntegrations: [ + { + azureMonitorWorkspaceResourceId: monitorId + } + ] + } + } + sku: { + name: 'Standard' + } +} + +var grafanaAdminRole = '22926164-76b3-42b3-bc55-97df8dab3e41' +var monitorReaderRole = 'b0d8363b-8ddd-447d-831f-62ca05bff136' + +// role assignment for the grafana +module roleAssignmentForMe '../core/security/role.bicep' = { + name: 'grafanaRoleAssignmentForMe' + params: { + principalId: principalId + principalType: 'User' + roleDefinitionId: grafanaAdminRole + } +} + +module roleAssignment '../core/security/role.bicep' = { + name: 'monitorRoleAssignmentForGrafana' + params: { + principalId: grafana.identity.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: monitorReaderRole + } +} + +// data collection +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2022-06-01' = { + name: 'MSProm-${clusterName}' + location: location + kind: 'Linux' + properties:{} +} + +resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2022-06-01' = { + name: 'MSProm-${clusterName}' + location: location + properties: { + dataCollectionEndpointId: dataCollectionEndpoint.id + dataSources:{ + prometheusForwarder: [ + { + name: 'PrometheusDataSource' + streams: ['Microsoft-PrometheusMetrics'] + } + ] + } + destinations: { + monitoringAccounts: [ + { + accountResourceId: monitorId + name: monitorName + } + ] + } + dataFlows: [ + { + streams: ['Microsoft-PrometheusMetrics'] + destinations:[ + monitorName + ] + } + ] + } +} + +resource aks 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' existing = { + name: clusterName +} + +resource dcrToAks 'Microsoft.Insights/dataCollectionRuleAssociations@2022-06-01' = { + name: 'dcr-${clusterName}' + scope: aks + properties: { + dataCollectionRuleId: dataCollectionRule.id + } +} + +resource dcrToAksEndpoint 'Microsoft.Insights/dataCollectionRuleAssociations@2022-06-01' = { + name: 'configurationAccessEndpoint' + scope: aks + properties: { + dataCollectionEndpointId: dataCollectionEndpoint.id + } +} + +resource monitorPrometheusRuleGroup 'Microsoft.AlertsManagement/prometheusRuleGroups@2023-03-01' = { + name: 'NodeRecordingRulesRuleGroup-${clusterName}' + location: location + properties:{ + interval: 'PT1M' + enabled: true + clusterName: clusterName + scopes:[monitorId] + rules:[ + { + record: 'instance:node_num_cpu:sum' + expression: 'count without (cpu, mode) (node_cpu_seconds_total{job="node",mode="idle"})' + } + { + record: 'instance:node_cpu_utilisation:rate5m' + expression: '1 - avg without (cpu) (sum without (mode) (rate(node_cpu_seconds_total{job="node", mode="idle"}[5m])) + sum without (mode) (rate(node_cpu_seconds_total{job="node", mode="iowait"}[5m])) + sum without (mode) (rate(node_cpu_seconds_total{job="node", mode="steal"}[5m])))' + } + { + record: 'instance:node_load1_per_cpu:ratio' + expression: 'node_load1{job="node"}/instance:node_num_cpu:sum{job="node"}' + } + { + record: 'instance:node_memory_utilisation:ratio' + expression: '1 - ((node_memory_MemAvailable_bytes{job="node"} or (node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"})) / node_memory_MemTotal_bytes{job="node"})' + } + { + record: 'instance:node_vmstat_pgmajfault:rate5m' + expression: 'rate(node_vmstat_pgmajfault{job="node"}[5m])' + } + { + record: 'instance_device:node_disk_io_time_seconds:rate5m' + expression: 'rate(node_disk_io_time_seconds_total{job="node", device!=""}[5m])' + } + { + record: 'instance_device:node_disk_io_time_weighted_seconds:rate5m' + expression: 'rate(node_disk_io_time_weighted_seconds_total{job="node", device!=""}[5m])' + } + { + record: 'instance:node_network_receive_bytes_excluding_lo:rate5m' + expression: 'sum without (device) (rate(node_network_receive_bytes_total{job="node", device!="lo"}[5m]))' + } + { + record: 'instance:node_network_transmit_bytes_excluding_lo:rate5m' + expression: 'sum without (device) (rate(node_network_transmit_bytes_total{job="node", device!="lo"}[5m]))' + } + { + record: 'instance:node_network_receive_drop_excluding_lo:rate5m' + expression: 'sum without (device) (rate(node_network_receive_drop_total{job="node", device!="lo"}[5m]))' + } + { + record: 'instance:node_network_transmit_drop_excluding_lo:rate5m' + expression: 'sum without (device) (rate(node_network_transmit_drop_total{job="node", device!="lo"}[5m]))' + } + ] + } +} + +resource k8sPrometheusRuleGroup 'Microsoft.AlertsManagement/prometheusRuleGroups@2023-03-01' = { + name: 'KubernetesRecordingRulesRuleGroup-${clusterName}' + location: location + properties:{ + interval: 'PT1M' + enabled: true + clusterName: clusterName + scopes:[monitorId] + rules:[ + { + record: 'node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate' + expression: 'sum by (cluster, namespace, pod, container) (irate(container_cpu_usage_seconds_total{job="cadvisor", image!=""}[5m])) * on (cluster, namespace, pod) group_left(node) topk by (cluster, namespace, pod) (1, max by(cluster, namespace, pod, node) (kube_pod_info{node!=""}))' + } + { + record: 'node_namespace_pod_container:container_memory_working_set_bytes' + expression: 'container_memory_working_set_bytes{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))' + } + { + record: 'node_namespace_pod_container:container_memory_rss' + expression: 'container_memory_rss{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))' + } + { + record: 'node_namespace_pod_container:container_memory_cache' + expression: 'container_memory_cache{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))' + } + { + record: 'node_namespace_pod_container:container_memory_swap' + expression: 'container_memory_swap{job="cadvisor", image!=""}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=""}))' + } + { + record: 'cluster:namespace:pod_memory:active:kube_pod_container_resource_requests' + expression: 'kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"} * on(namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~"Pending|Running"} == 1))' + } + { + record: 'namespace_memory:kube_pod_container_resource_requests:sum' + expression: 'sum by (namespace, cluster) (sum by (namespace, pod, cluster) (max by (namespace, pod, container, cluster) (kube_pod_container_resource_requests{resource="memory",job="kube-state-metrics"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~"Pending|Running"} == 1)))' + } + { + record: 'cluster:namespace:pod_cpu:active:kube_pod_container_resource_requests' + expression: 'kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~"Pending|Running"} == 1))' + } + { + record: 'namespace_cpu:kube_pod_container_resource_requests:sum' + expression: 'sum by (namespace, cluster) (sum by(namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_requests{resource="cpu",job="kube-state-metrics"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~"Pending|Running"} == 1)))' + } + { + record: 'cluster:namespace:pod_memory:active:kube_pod_container_resource_limits' + expression: 'kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~"Pending|Running"} == 1))' + } + { + record: 'namespace_memory:kube_pod_container_resource_limits:sum' + expression: 'sum by (namespace, cluster) (sum by (namespace, pod, cluster) (max by (namespace, pod, container, cluster) (kube_pod_container_resource_limits{resource="memory",job="kube-state-metrics"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~"Pending|Running"} == 1)))' + } + { + record: 'cluster:namespace:pod_cpu:active:kube_pod_container_resource_limits' + expression: 'kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~"Pending|Running"} == 1))' + } + { + record: 'namespace_cpu:kube_pod_container_resource_limits:sum' + expression: 'sum by (namespace, cluster) (sum by (namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_limits{resource="cpu",job="kube-state-metrics"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~"Pending|Running"} == 1)))' + } + { + record: 'namespace_workload_pod:kube_pod_owner:relabel' + expression: 'max by (cluster, namespace, workload, pod) (label_replace(label_replace(kube_pod_owner{job="kube-state-metrics", owner_kind="ReplicaSet"}, "replicaset", "$1", "owner_name", "(.*)") * on(replicaset, namespace) group_left(owner_name) topk by(replicaset, namespace) (1, max by (replicaset, namespace, owner_name) (kube_replicaset_owner{job="kube-state-metrics"})), "workload", "$1", "owner_name", "(.*)"))' + labels: { + workload_type: 'deployment' + } + } + { + record: 'namespace_workload_pod:kube_pod_owner:relabel' + expression: 'max by (cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job="kube-state-metrics", owner_kind="DaemonSet"}, "workload", "$1", "owner_name", "(.*)"))' + labels: { + workload_type: 'daemonset' + } + } + { + record: 'namespace_workload_pod:kube_pod_owner:relabel' + expression: 'max by (cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job="kube-state-metrics", owner_kind="StatefulSet"}, "workload", "$1", "owner_name", "(.*)"))' + labels: { + workload_type: 'statefulset' + } + } + { + record: 'namespace_workload_pod:kube_pod_owner:relabel' + expression: 'max by (cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job="kube-state-metrics", owner_kind="Job"}, "workload", "$1", "owner_name", "(.*)"))' + labels: { + workload_type: 'job' + } + } + { + record: ':node_memory_MemAvailable_bytes:sum' + expression: 'sum(node_memory_MemAvailable_bytes{job="node"} or (node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"})) by (cluster)' + } + { + record: 'cluster:node_cpu:ratio_rate5m' + expression: 'sum(rate(node_cpu_seconds_total{job="node",mode!="idle",mode!="iowait",mode!="steal"}[5m])) by (cluster) /count(sum(node_cpu_seconds_total{job="node"}) by (cluster, instance, cpu)) by (cluster)' + } + ] + } +} + +resource monitorDataCollectionRule 'Microsoft.Insights/dataCollectionRules@2022-06-01' = { + name: 'MSCI-${clusterName}' + location: location + kind: 'Linux' + properties: { + dataSources: { + extensions: [ + { + streams: [ + 'Microsoft-ContainerInsights-Group-Default' + ] + extensionName: 'ContainerInsights' + extensionSettings: { + dataCollectionSettings: { + enableContainerLogV2: true + interval: '1m' + namespaceFilteringMode: 'Off' + } + } + name: 'ContainerInsightsExtension' + } + ] + } + destinations: { + logAnalytics:[ + { + workspaceResourceId: logAnalyticsId + name: logAnalyticsName + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-ContainerInsights-Group-Default' + ] + destinations: [ + logAnalyticsName + ] + } + ] + } +} + + +resource msciToAks 'Microsoft.Insights/dataCollectionRuleAssociations@2022-06-01' = { + name: 'msci-${clusterName}' + scope: aks + properties: { + dataCollectionRuleId: dataCollectionRule.id + } +} diff --git a/Environments/AKS-Store-Demo/app/servicebus.bicep b/Environments/AKS-Store-Demo/app/servicebus.bicep new file mode 100644 index 00000000..778485d4 --- /dev/null +++ b/Environments/AKS-Store-Demo/app/servicebus.bicep @@ -0,0 +1,70 @@ +param name string +param location string +param tags object = {} + +param keyVaultName string +param listenerKeyName string = 'AZURE-SERVICE-BUS-LISTENER-KEY' +param senderKeyName string = 'AZURE-SERVICE-BUS-SENDER-KEY' + +// Service Bus Namespace +resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-01-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: 'Standard' + } +} + +// Service Bus Namespace Authorization Rule +resource serviceBusNamespaceAuthorizationRule 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2022-10-01-preview' = { + name: 'listener' + parent: serviceBusNamespace + properties: { + rights: [ + 'Listen' + ] + } +} + +// Service Bus Queue +resource serviceBusQueue 'Microsoft.ServiceBus/namespaces/queues@2022-01-01-preview' = { + name: 'orders' + parent: serviceBusNamespace +} + +// Service Bus Namespace Authorization Rule +resource serviceBusNamespaceQueueAuthorizationRule 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2022-10-01-preview' = { + name: 'sender' + parent: serviceBusNamespace + properties: { + rights: [ + 'Send' + ] + } +} + +module senderKey '../core/security/keyvault-secret.bicep' = { + name: 'senderKey' + params: { + name: senderKeyName + keyVaultName: keyVaultName + secretValue: serviceBusNamespaceQueueAuthorizationRule.listKeys().primaryKey + } +} + +module listenerKey '../core/security/keyvault-secret.bicep' = { + name: 'listenerKey' + params: { + name: listenerKeyName + keyVaultName: keyVaultName + secretValue: serviceBusNamespaceAuthorizationRule.listKeys().primaryKey + } +} + +output serviceBusEndpoint string = serviceBusNamespace.properties.serviceBusEndpoint +output serviceBusListenerName string = serviceBusNamespaceAuthorizationRule.name +output serviceBusSenderName string = serviceBusNamespaceQueueAuthorizationRule.name +output serviceBusListenerKey string = listenerKeyName +output serviceBusSenderKey string = senderKeyName +output serviceBusNamespaceName string = serviceBusNamespace.name diff --git a/Environments/AKS-Store-Demo/core/ai/cognitiveservices.bicep b/Environments/AKS-Store-Demo/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..1bf5666b --- /dev/null +++ b/Environments/AKS-Store-Demo/core/ai/cognitiveservices.bicep @@ -0,0 +1,53 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/AKS-Store-Demo/core/host/container-registry.bicep b/Environments/AKS-Store-Demo/core/host/container-registry.bicep new file mode 100644 index 00000000..9c64531b --- /dev/null +++ b/Environments/AKS-Store-Demo/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/Environments/AKS-Store-Demo/core/monitor/loganalytics.bicep b/Environments/AKS-Store-Demo/core/monitor/loganalytics.bicep new file mode 100644 index 00000000..33f9dc29 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/Environments/AKS-Store-Demo/core/security/keyvault-access.bicep b/Environments/AKS-Store-Demo/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/AKS-Store-Demo/core/security/keyvault-secret.bicep b/Environments/AKS-Store-Demo/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/AKS-Store-Demo/core/security/keyvault.bicep b/Environments/AKS-Store-Demo/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/AKS-Store-Demo/core/security/registry-access.bicep b/Environments/AKS-Store-Demo/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/AKS-Store-Demo/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/AKS-Store-Demo/core/security/role.bicep b/Environments/AKS-Store-Demo/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/AKS-Store-Demo/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/AKS-Store-Demo/main.bicep b/Environments/AKS-Store-Demo/main.bicep new file mode 100644 index 00000000..23842254 --- /dev/null +++ b/Environments/AKS-Store-Demo/main.bicep @@ -0,0 +1,227 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: +// "resourceGroupName": { +// "value": "myGroupName" +// } +param k8s_namespace string = 'default' +param openAiServiceName string = '' +param openAiModelName string = 'gpt-35-turbo' +param identityName string = '' +param kubernetesName string = '' +param keyVaultName string = '' +param servicebusName string = '' +param logAnalyticsName string = '' +param monitorAccountName string = '' +param containerRegistryName string = '' + +@description('Deploy an Azure Container Registry or not') +param deployAcr bool = false + +@allowed([ + 'MongoDB' + 'GlobalDocumentDB' +]) +param cosmosdbAccountKind string = 'MongoDB' + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// the node pool base configuration +var nodePoolBase = { + name: 'system' + count: 3 + vmSize: 'Standard_D4s_v4' +} + +// the openai deployments to create +var openAiDeployment = [ + { + name: openAiModelName + sku: { + name: 'Standard' + capacity: 30 + } + model: { + format: 'OpenAI' + name: openAiModelName + version: '0613' + } + } +] + +// create the openai resources +module openAi './core/ai/cognitiveservices.bicep' = { + name: 'openai' + params: { + name: !empty(openAiServiceName) ? openAiServiceName : '${abbrs.cognitiveServicesAccounts}${resourceToken}' + location: location + tags: tags + deployments: openAiDeployment + } +} + +// create the identity and assign roles +module identity './app/identity.bicep' = { + name: 'identity' + params: { + name: !empty(identityName) ? identityName : '${abbrs.managedIdentityUserAssignedIdentities}${resourceToken}' + location: location + tags: tags + principalId: principalId + AZURE_AKS_NAMESPACE: k8s_namespace + clusterName: kubernetes.outputs.clusterName + } +} + +// create the kunbernetes cluster +module kubernetes './app/aks-managed-cluster.bicep' = { + name: 'kubernetes' + params: { + name: !empty(kubernetesName) ? kubernetesName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + location: location + tags: tags + networkPlugin: 'kubenet' + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase + ) + dnsPrefix: !empty(kubernetesName) ? kubernetesName : '${abbrs.containerServiceManagedClusters}${resourceToken}' + } +} + +// store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +// create the cosmosdb +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + resourceToken: resourceToken + location: location + tags: tags + kind: cosmosdbAccountKind + keyVaultName: keyVault.outputs.name + } +} + + +// create the service bus +module serviceBus './app/servicebus.bicep' = { + name: 'servicebus' + params: { + name: !empty(servicebusName) ? servicebusName : '${abbrs.serviceBusNamespaces}${resourceToken}' + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +// get keys from the openAi and cosmosdb +module getKeys './app/get-keys.bicep' = { + name: 'get-keys' + params:{ + keyVaultName: keyVault.outputs.name + openAiName: openAi.outputs.name + cosmosAccountName: cosmos.outputs.name + } +} + +// create the monitor workspace +module monitor './app/monitor.bicep' = { + name: 'monitor' + params: { + name: !empty(monitorAccountName) ? monitorAccountName : 'amon-${resourceToken}' + location: location + tags: tags + } +} + +// create the log analytics workspace +module logAnalytics './core/monitor/loganalytics.bicep' = { + name: 'log-analytics' + params: { + name: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + location: location + tags: tags + } +} + +// create observability +module observability './app/observability.bicep' = { + name: 'observability' + params: { + name: 'amg-${resourceToken}' + principalId: principalId + clusterId: kubernetes.outputs.clusterId + clusterName: kubernetes.outputs.clusterName + logAnalyticsName: logAnalytics.outputs.name + logAnalyticsId: logAnalytics.outputs.id + monitorName: monitor.outputs.name + monitorId: monitor.outputs.id + location: location + tags: tags + } +} + +// create the container if the deployAcr is true +module containerRegistry './core/host/container-registry.bicep' = if(deployAcr) { + name: 'container-registry' + params: { + name: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + location: location + tags: tags + sku: { + name: 'Premium' + } + } +} + +// acr pull role assignment +module acrPullRoleAssignment './core/security/registry-access.bicep' = if(deployAcr) { + name: 'acr-pull-role-assignment' + params: { + containerRegistryName: deployAcr ? containerRegistry.outputs.name : '' + principalId: kubernetes.outputs.clusterIdentity.objectId + } +} + +// outputs data +output AZURE_RESOURCEGROUP_NAME string = resourceGroup().name +output AZURE_AKS_CLUSTER_NAME string = kubernetes.outputs.clusterName +output AZURE_OPENAI_MODEL_NAME string = openAiModelName +output AZURE_OPENAI_ENDPOINT string = openAi.outputs.endpoint +output AZURE_IDENTITY_CLIENT_ID string = identity.outputs.clientId +output AZURE_SERVICE_BUS_HOST string = '${serviceBus.outputs.serviceBusNamespaceName}.servicebus.windows.net' +output AZURE_SERVICE_BUS_URI string = 'amqps://${serviceBus.outputs.serviceBusNamespaceName}.servicebus.windows.net' +output AZURE_SERVICE_BUS_LISTENER_NAME string = serviceBus.outputs.serviceBusListenerName +output AZURE_SERVICE_BUS_LISTENER_KEY string = serviceBus.outputs.serviceBusListenerKey +output AZURE_SERVICE_BUS_SENDER_NAME string = serviceBus.outputs.serviceBusSenderName +output AZURE_SERVICE_BUS_SENDER_KEY string = serviceBus.outputs.serviceBusSenderKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.name +output AZURE_COSMOS_DATABASE_URI string = cosmos.outputs.endpoint +output AZURE_COSMOS_DATABASE_KEY string = getKeys.outputs.cosmosKey +output AZURE_AKS_NAMESPACE string = k8s_namespace +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name +output AZURE_DATABASE_API string = cosmosdbAccountKind == 'MongoDB' ? 'mongodb': 'cosmosdbsql' +output AZURE_REGISTRY_NAME string = deployAcr ? containerRegistry.outputs.name : '' +output AZURE_REGISTRY_URI string = deployAcr ? containerRegistry.outputs.loginServer : 'ghcr.io/azure-samples' diff --git a/Environments/AKS-Store-Demo/manifest.yaml b/Environments/AKS-Store-Demo/manifest.yaml new file mode 100644 index 00000000..d7cc143c --- /dev/null +++ b/Environments/AKS-Store-Demo/manifest.yaml @@ -0,0 +1,28 @@ +name: eShop +version: 1.0.0 +summary: eShop Reference Application +description: eShop Reference Application - "Northern Mountains" +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. test)" + description: "Name of the Environment" + type: "string" + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Region" + type: "string" + required: false + default: "eastus" + +- id: "principalId" + name: "Principal Id (e.g. )" + description: "Id of the user or app to assign application roles" + type: "string" + required: true + + From 8c5d9a90ecc25a6ea82767574f35f61f86d12283 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Mon, 15 Apr 2024 13:17:52 +0000 Subject: [PATCH 085/112] Rebuild ARM templates --- Environments/AKS-Store-Demo/azuredeploy.json | 3426 ++++++++++++++++++ 1 file changed, 3426 insertions(+) create mode 100644 Environments/AKS-Store-Demo/azuredeploy.json diff --git a/Environments/AKS-Store-Demo/azuredeploy.json b/Environments/AKS-Store-Demo/azuredeploy.json new file mode 100644 index 00000000..60633bb0 --- /dev/null +++ b/Environments/AKS-Store-Demo/azuredeploy.json @@ -0,0 +1,3426 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "12442973100557358859" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "k8s_namespace": { + "type": "string", + "defaultValue": "default" + }, + "openAiServiceName": { + "type": "string", + "defaultValue": "" + }, + "openAiModelName": { + "type": "string", + "defaultValue": "gpt-35-turbo" + }, + "identityName": { + "type": "string", + "defaultValue": "" + }, + "kubernetesName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "servicebusName": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsName": { + "type": "string", + "defaultValue": "" + }, + "monitorAccountName": { + "type": "string", + "defaultValue": "" + }, + "containerRegistryName": { + "type": "string", + "defaultValue": "" + }, + "deployAcr": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Deploy an Azure Container Registry or not" + } + }, + "cosmosdbAccountKind": { + "type": "string", + "defaultValue": "MongoDB", + "allowedValues": [ + "MongoDB", + "GlobalDocumentDB" + ] + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Id of the user or app to assign application roles" + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + }, + "nodePoolBase": { + "name": "system", + "count": 3, + "vmSize": "Standard_D4s_v4" + }, + "openAiDeployment": [ + { + "name": "[parameters('openAiModelName')]", + "sku": { + "name": "Standard", + "capacity": 30 + }, + "model": { + "format": "OpenAI", + "name": "[parameters('openAiModelName')]", + "version": "0613" + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "openai", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('openAiServiceName'))), createObject('value', parameters('openAiServiceName')), createObject('value', format('{0}{1}', variables('abbrs').cognitiveServicesAccounts, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "deployments": { + "value": "[variables('openAiDeployment')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "15117202545295723007" + }, + "description": "Creates an Azure Cognitive Services instance." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "customSubDomainName": { + "type": "string", + "defaultValue": "[parameters('name')]", + "metadata": { + "description": "The custom subdomain name used to access the API. Defaults to the value of the name parameter." + } + }, + "deployments": { + "type": "array", + "defaultValue": [] + }, + "kind": { + "type": "string", + "defaultValue": "OpenAI" + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ] + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "S0" + } + }, + "allowedIpRules": { + "type": "array", + "defaultValue": [] + }, + "networkAcls": { + "type": "object", + "defaultValue": "[if(empty(parameters('allowedIpRules')), createObject('defaultAction', 'Allow'), createObject('ipRules', parameters('allowedIpRules'), 'defaultAction', 'Deny'))]" + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2023-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "customSubDomainName": "[parameters('customSubDomainName')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "networkAcls": "[parameters('networkAcls')]" + }, + "sku": "[parameters('sku')]" + }, + { + "copy": { + "name": "deployment", + "count": "[length(parameters('deployments'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", + "properties": { + "model": "[parameters('deployments')[copyIndex()].model]", + "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]" + }, + "sku": "[if(contains(parameters('deployments')[copyIndex()], 'sku'), parameters('deployments')[copyIndex()].sku, createObject('name', 'Standard', 'capacity', 20))]", + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + ] + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').endpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "identity", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('identityName'))), createObject('value', parameters('identityName')), createObject('value', format('{0}{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "AZURE_AKS_NAMESPACE": { + "value": "[parameters('k8s_namespace')]" + }, + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'kubernetes'), '2022-09-01').outputs.clusterName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "15628495845320500833" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string" + }, + "AZURE_AKS_NAMESPACE": { + "type": "string" + }, + "clusterName": { + "type": "string" + } + }, + "variables": { + "openAiUserRole": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd" + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials", + "apiVersion": "2023-01-31", + "name": "[format('{0}/{1}', parameters('name'), parameters('name'))]", + "properties": { + "audiences": [ + "api://AzureADTokenExchange" + ], + "issuer": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('clusterName')), '2023-03-02-preview').oidcIssuerProfile.issuerURL]", + "subject": "[format('system:serviceaccount:{0}:ai-service-account', parameters('AZURE_AKS_NAMESPACE'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "roleAssignment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').principalId]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "roleDefinitionId": { + "value": "[variables('openAiUserRole')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "5795525499710207356" + }, + "description": "Creates a role assignment for a service principal." + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "roleAssignmentForMe", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "principalType": { + "value": "User" + }, + "roleDefinitionId": { + "value": "[variables('openAiUserRole')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "5795525499710207356" + }, + "description": "Creates a role assignment for a service principal." + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + } + ], + "outputs": { + "principalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').principalId]" + }, + "clientId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').clientId]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'kubernetes')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "kubernetes", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('kubernetesName'))), createObject('value', parameters('kubernetesName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "networkPlugin": { + "value": "kubenet" + }, + "systemPoolConfig": { + "value": "[union(createObject('name', 'npsystem', 'mode', 'System'), variables('nodePoolBase'))]" + }, + "dnsPrefix": "[if(not(empty(parameters('kubernetesName'))), createObject('value', parameters('kubernetesName')), createObject('value', format('{0}{1}', variables('abbrs').containerServiceManagedClusters, variables('resourceToken'))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "10925305564301015943" + }, + "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "The name for the AKS managed cluster" + } + }, + "nodeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the resource group for the managed resources of the AKS cluster" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The Azure region/location for the AKS resources" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Custom tags to apply to the AKS resources" + } + }, + "enableRbac": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether RBAC is enabled for local accounts" + } + }, + "webAppRoutingAddon": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Whether web app routing (preview) add-on is enabled" + } + }, + "enableAad": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory integration" + } + }, + "enableAzureRbac": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable RBAC using AAD" + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The Tenant ID associated to the Azure Active Directory" + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "The load balancer SKU to use for ingress into the AKS cluster" + } + }, + "networkPlugin": { + "type": "string", + "defaultValue": "azure", + "allowedValues": [ + "azure", + "kubenet", + "none" + ], + "metadata": { + "description": "Network plugin used for building the Kubernetes network." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "If set to true, getting static credentials will be disabled for this cluster." + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Paid", + "Standard" + ], + "metadata": { + "description": "The managed cluster SKU." + } + }, + "addOns": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Configuration of AKS add-ons" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace id used for logging & monitoring" + } + }, + "systemPoolConfig": { + "type": "object", + "metadata": { + "description": "The node pool configuration for the System agent pool" + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The DNS prefix to associate with the AKS cluster" + } + } + }, + "variables": { + "aksDiagCategories": [ + "cluster-autoscaler", + "kube-controller-manager", + "kube-audit-admin", + "guard" + ] + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2023-03-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "Base", + "tier": "[parameters('sku')]" + }, + "properties": { + "nodeResourceGroup": "[if(not(empty(parameters('nodeResourceGroupName'))), parameters('nodeResourceGroupName'), format('rg-mc-{0}', parameters('name')))]", + "dnsPrefix": "[if(empty(parameters('dnsPrefix')), format('{0}-dns', parameters('name')), parameters('dnsPrefix'))]", + "enableRBAC": "[parameters('enableRbac')]", + "aadProfile": "[if(parameters('enableAad'), createObject('managed', true(), 'enableAzureRBAC', parameters('enableAzureRbac'), 'tenantID', parameters('aadTenantId')), null())]", + "agentPoolProfiles": [ + "[parameters('systemPoolConfig')]" + ], + "networkProfile": { + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "networkPlugin": "[parameters('networkPlugin')]" + }, + "disableLocalAccounts": "[and(parameters('disableLocalAccounts'), parameters('enableAad'))]", + "addonProfiles": "[parameters('addOns')]", + "securityProfile": { + "workloadIdentity": { + "enabled": true + } + }, + "oidcIssuerProfile": { + "enabled": true + } + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "aks-diagnostics", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(variables('aksDiagCategories'))]", + "input": { + "category": "[variables('aksDiagCategories')[copyIndex('logs')]]", + "enabled": true + } + } + ], + "workspaceId": "[parameters('workspaceId')]", + "metrics": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "metadata": { + "description": "The resource name of the AKS cluster" + }, + "value": "[parameters('name')]" + }, + "clusterIdentity": { + "type": "object", + "metadata": { + "description": "The AKS cluster identity" + }, + "value": { + "clientId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-03-02-preview').identityProfile.kubeletidentity.clientId]", + "objectId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-03-02-preview').identityProfile.kubeletidentity.objectId]", + "resourceId": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), '2023-03-02-preview').identityProfile.kubeletidentity.resourceId]" + } + }, + "clusterId": { + "type": "string", + "value": "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4552321833419182500" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourceToken": { + "value": "[variables('resourceToken')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "kind": { + "value": "[parameters('cosmosdbAccountKind')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "16337532685264148186" + } + }, + "parameters": { + "kind": { + "type": "string", + "allowedValues": [ + "MongoDB", + "GlobalDocumentDB" + ] + }, + "resourceToken": { + "type": "string" + }, + "location": { + "type": "string" + }, + "keyVaultName": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "orderdb" + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "id": "orders", + "name": "orders", + "shardKey": "Hash", + "indexKey": "_id", + "throughput": 400 + } + ], + "metadata": { + "description": "The collections to create in the database" + } + } + }, + "resources": [ + { + "condition": "[equals(parameters('kind'), 'MongoDB')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[format('cosmos-{0}', parameters('resourceToken'))]" + }, + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "13527588968236853243" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "3220231753590578091" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + }, + { + "condition": "[equals(parameters('kind'), 'GlobalDocumentDB')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-sql", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[format('cosmos-{0}', parameters('resourceToken'))]" + }, + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "containers": { + "value": [ + { + "name": "orders", + "id": "orders", + "partitionKey": "/storeId" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "498024263025124773" + }, + "description": "Creates an Azure Cosmos DB for NoSQL account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containers": { + "type": "array", + "defaultValue": [] + }, + "keyVaultName": { + "type": "string" + }, + "principalIds": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('containers'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('containers')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('containers')[copyIndex()].id]", + "partitionKey": { + "paths": [ + "[parameters('containers')[copyIndex()].partitionKey]" + ] + } + }, + "options": {} + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-sql-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "9969697426957325100" + }, + "description": "Creates an Azure Cosmos DB for NoSQL account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "GlobalDocumentDB" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "11668109951104513744" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.name.value]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-sql-role-definition", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "16222518383215599293" + }, + "description": "Creates a SQL role definition under an Azure Cosmos DB account." + }, + "parameters": { + "accountName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName')), parameters('accountName'), 'sql-role'))]", + "properties": { + "assignableScopes": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))]" + ], + "permissions": [ + { + "dataActions": [ + "Microsoft.DocumentDB/databaseAccounts/readMetadata", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*" + ], + "notDataActions": [] + } + ], + "roleName": "Reader Writer", + "type": "CustomRole" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('accountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName')), parameters('accountName'), 'sql-role'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "copy": { + "name": "userRole", + "count": "[length(parameters('principalIds'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[not(empty(parameters('principalIds')[copyIndex()]))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('cosmos-sql-user-role-{0}', uniqueString(parameters('principalIds')[copyIndex()]))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "roleDefinitionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-role-definition'), '2022-09-01').outputs.id.value]" + }, + "principalId": { + "value": "[parameters('principalIds')[copyIndex()]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "13520160008714254891" + }, + "description": "Creates a SQL role assignment under an Azure Cosmos DB account." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('accountName'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-role-definition')]" + ] + } + ], + "outputs": { + "accountId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.id.value]" + }, + "accountName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.name.value]" + }, + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.endpoint.value]" + }, + "roleDefinitionId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-role-definition'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[format('cosmos-{0}', parameters('resourceToken'))]" + }, + "endpoint": { + "type": "string", + "value": "[if(equals(parameters('kind'), 'MongoDB'), format('mongodb://cosmos-{0}.mongo.cosmos.azure.com:10255/?retryWrites=false', parameters('resourceToken')), format('https://cosmos-{0}.documents.azure.com:443/', parameters('resourceToken')))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "servicebus", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('servicebusName'))), createObject('value', parameters('servicebusName')), createObject('value', format('{0}{1}', variables('abbrs').serviceBusNamespaces, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "1269692203398953166" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "listenerKeyName": { + "type": "string", + "defaultValue": "AZURE-SERVICE-BUS-LISTENER-KEY" + }, + "senderKeyName": { + "type": "string", + "defaultValue": "AZURE-SERVICE-BUS-SENDER-KEY" + } + }, + "resources": [ + { + "type": "Microsoft.ServiceBus/namespaces", + "apiVersion": "2022-01-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "Standard" + } + }, + { + "type": "Microsoft.ServiceBus/namespaces/AuthorizationRules", + "apiVersion": "2022-10-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'listener')]", + "properties": { + "rights": [ + "Listen" + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ServiceBus/namespaces', parameters('name'))]" + ] + }, + { + "type": "Microsoft.ServiceBus/namespaces/queues", + "apiVersion": "2022-01-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'orders')]", + "dependsOn": [ + "[resourceId('Microsoft.ServiceBus/namespaces', parameters('name'))]" + ] + }, + { + "type": "Microsoft.ServiceBus/namespaces/AuthorizationRules", + "apiVersion": "2022-10-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'sender')]", + "properties": { + "rights": [ + "Send" + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ServiceBus/namespaces', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "senderKey", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('senderKeyName')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.ServiceBus/namespaces/AuthorizationRules', parameters('name'), 'sender'), '2022-10-01-preview').primaryKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4135456988027214147" + }, + "description": "Creates or updates a secret in an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "contentType": { + "type": "string", + "defaultValue": "string" + }, + "secretValue": { + "type": "securestring", + "metadata": { + "description": "The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates" + } + }, + "enabled": { + "type": "bool", + "defaultValue": true + }, + "exp": { + "type": "int", + "defaultValue": 0 + }, + "nbf": { + "type": "int", + "defaultValue": 0 + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "attributes": { + "enabled": "[parameters('enabled')]", + "exp": "[parameters('exp')]", + "nbf": "[parameters('nbf')]" + }, + "contentType": "[parameters('contentType')]", + "value": "[parameters('secretValue')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ServiceBus/namespaces/AuthorizationRules', parameters('name'), 'sender')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "listenerKey", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('listenerKeyName')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.ServiceBus/namespaces/AuthorizationRules', parameters('name'), 'listener'), '2022-10-01-preview').primaryKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4135456988027214147" + }, + "description": "Creates or updates a secret in an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "contentType": { + "type": "string", + "defaultValue": "string" + }, + "secretValue": { + "type": "securestring", + "metadata": { + "description": "The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates" + } + }, + "enabled": { + "type": "bool", + "defaultValue": true + }, + "exp": { + "type": "int", + "defaultValue": 0 + }, + "nbf": { + "type": "int", + "defaultValue": 0 + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "attributes": { + "enabled": "[parameters('enabled')]", + "exp": "[parameters('exp')]", + "nbf": "[parameters('nbf')]" + }, + "contentType": "[parameters('contentType')]", + "value": "[parameters('secretValue')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ServiceBus/namespaces/AuthorizationRules', parameters('name'), 'listener')]" + ] + } + ], + "outputs": { + "serviceBusEndpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ServiceBus/namespaces', parameters('name')), '2022-01-01-preview').serviceBusEndpoint]" + }, + "serviceBusListenerName": { + "type": "string", + "value": "listener" + }, + "serviceBusSenderName": { + "type": "string", + "value": "sender" + }, + "serviceBusListenerKey": { + "type": "string", + "value": "[parameters('listenerKeyName')]" + }, + "serviceBusSenderKey": { + "type": "string", + "value": "[parameters('senderKeyName')]" + }, + "serviceBusNamespaceName": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "get-keys", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "openAiName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'openai'), '2022-09-01').outputs.name.value]" + }, + "cosmosAccountName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "17291248993155438634" + } + }, + "parameters": { + "openAiName": { + "type": "string" + }, + "openAiKeyName": { + "type": "string", + "defaultValue": "AZURE-OPENAI-KEY" + }, + "cosmosAccountName": { + "type": "string" + }, + "cosmosKeyName": { + "type": "string", + "defaultValue": "AZURE-COSMOS-KEY" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "openAiKey", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('openAiKeyName')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAiName')), '2023-05-01').key1]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4135456988027214147" + }, + "description": "Creates or updates a secret in an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "contentType": { + "type": "string", + "defaultValue": "string" + }, + "secretValue": { + "type": "securestring", + "metadata": { + "description": "The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates" + } + }, + "enabled": { + "type": "bool", + "defaultValue": true + }, + "exp": { + "type": "int", + "defaultValue": 0 + }, + "nbf": { + "type": "int", + "defaultValue": 0 + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "attributes": { + "enabled": "[parameters('enabled')]", + "exp": "[parameters('exp')]", + "nbf": "[parameters('nbf')]" + }, + "contentType": "[parameters('contentType')]", + "value": "[parameters('secretValue')]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmosKey", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('cosmosKeyName')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosAccountName')), '2022-08-15').primaryMasterKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4135456988027214147" + }, + "description": "Creates or updates a secret in an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "contentType": { + "type": "string", + "defaultValue": "string" + }, + "secretValue": { + "type": "securestring", + "metadata": { + "description": "The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates" + } + }, + "enabled": { + "type": "bool", + "defaultValue": true + }, + "exp": { + "type": "int", + "defaultValue": 0 + }, + "nbf": { + "type": "int", + "defaultValue": 0 + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "attributes": { + "enabled": "[parameters('enabled')]", + "exp": "[parameters('exp')]", + "nbf": "[parameters('nbf')]" + }, + "contentType": "[parameters('contentType')]", + "value": "[parameters('secretValue')]" + } + } + ] + } + } + } + ], + "outputs": { + "openAiKey": { + "type": "string", + "value": "[parameters('openAiKeyName')]" + }, + "cosmosKey": { + "type": "string", + "value": "[parameters('cosmosKeyName')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]", + "[resourceId('Microsoft.Resources/deployments', 'openai')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitor", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('monitorAccountName'))), createObject('value', parameters('monitorAccountName')), createObject('value', format('amon-{0}', variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "8130588533889525221" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Monitor/accounts", + "apiVersion": "2023-04-03", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Monitor/accounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "log-analytics", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('logAnalyticsName'))), createObject('value', parameters('logAnalyticsName')), createObject('value', format('{0}{1}', variables('abbrs').operationalInsightsWorkspaces, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "5766449277789384912" + }, + "description": "Creates a Log Analytics workspace." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-12-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "retentionInDays": 30, + "features": { + "searchVersion": 1 + }, + "sku": { + "name": "PerGB2018" + } + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "observability", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('amg-{0}', variables('resourceToken'))]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "clusterId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'kubernetes'), '2022-09-01').outputs.clusterId.value]" + }, + "clusterName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'kubernetes'), '2022-09-01').outputs.clusterName.value]" + }, + "logAnalyticsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'log-analytics'), '2022-09-01').outputs.name.value]" + }, + "logAnalyticsId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'log-analytics'), '2022-09-01').outputs.id.value]" + }, + "monitorName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitor'), '2022-09-01').outputs.name.value]" + }, + "monitorId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'monitor'), '2022-09-01').outputs.id.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "446387552123988459" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "monitorName": { + "type": "string" + }, + "monitorId": { + "type": "string" + }, + "principalId": { + "type": "string" + }, + "clusterId": { + "type": "string" + }, + "clusterName": { + "type": "string" + }, + "logAnalyticsId": { + "type": "string" + }, + "logAnalyticsName": { + "type": "string" + } + }, + "variables": { + "grafanaAdminRole": "22926164-76b3-42b3-bc55-97df8dab3e41", + "monitorReaderRole": "b0d8363b-8ddd-447d-831f-62ca05bff136" + }, + "resources": [ + { + "type": "Microsoft.Dashboard/grafana", + "apiVersion": "2022-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "grafanaIntegrations": { + "azureMonitorWorkspaceIntegrations": [ + { + "azureMonitorWorkspaceResourceId": "[parameters('monitorId')]" + } + ] + } + }, + "sku": { + "name": "Standard" + } + }, + { + "type": "Microsoft.Insights/dataCollectionEndpoints", + "apiVersion": "2022-06-01", + "name": "[format('MSProm-{0}', parameters('clusterName'))]", + "location": "[parameters('location')]", + "kind": "Linux", + "properties": {} + }, + { + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2022-06-01", + "name": "[format('MSProm-{0}', parameters('clusterName'))]", + "location": "[parameters('location')]", + "properties": { + "dataCollectionEndpointId": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', format('MSProm-{0}', parameters('clusterName')))]", + "dataSources": { + "prometheusForwarder": [ + { + "name": "PrometheusDataSource", + "streams": [ + "Microsoft-PrometheusMetrics" + ] + } + ] + }, + "destinations": { + "monitoringAccounts": [ + { + "accountResourceId": "[parameters('monitorId')]", + "name": "[parameters('monitorName')]" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "Microsoft-PrometheusMetrics" + ], + "destinations": [ + "[parameters('monitorName')]" + ] + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/dataCollectionEndpoints', format('MSProm-{0}', parameters('clusterName')))]" + ] + }, + { + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2022-06-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[format('dcr-{0}', parameters('clusterName'))]", + "properties": { + "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', format('MSProm-{0}', parameters('clusterName')))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/dataCollectionRules', format('MSProm-{0}', parameters('clusterName')))]" + ] + }, + { + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2022-06-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "configurationAccessEndpoint", + "properties": { + "dataCollectionEndpointId": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', format('MSProm-{0}', parameters('clusterName')))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/dataCollectionEndpoints', format('MSProm-{0}', parameters('clusterName')))]" + ] + }, + { + "type": "Microsoft.AlertsManagement/prometheusRuleGroups", + "apiVersion": "2023-03-01", + "name": "[format('NodeRecordingRulesRuleGroup-{0}', parameters('clusterName'))]", + "location": "[parameters('location')]", + "properties": { + "interval": "PT1M", + "enabled": true, + "clusterName": "[parameters('clusterName')]", + "scopes": [ + "[parameters('monitorId')]" + ], + "rules": [ + { + "record": "instance:node_num_cpu:sum", + "expression": "count without (cpu, mode) (node_cpu_seconds_total{job=\"node\",mode=\"idle\"})" + }, + { + "record": "instance:node_cpu_utilisation:rate5m", + "expression": "1 - avg without (cpu) (sum without (mode) (rate(node_cpu_seconds_total{job=\"node\", mode=\"idle\"}[5m])) + sum without (mode) (rate(node_cpu_seconds_total{job=\"node\", mode=\"iowait\"}[5m])) + sum without (mode) (rate(node_cpu_seconds_total{job=\"node\", mode=\"steal\"}[5m])))" + }, + { + "record": "instance:node_load1_per_cpu:ratio", + "expression": "node_load1{job=\"node\"}/instance:node_num_cpu:sum{job=\"node\"}" + }, + { + "record": "instance:node_memory_utilisation:ratio", + "expression": "1 - ((node_memory_MemAvailable_bytes{job=\"node\"} or (node_memory_Buffers_bytes{job=\"node\"} + node_memory_Cached_bytes{job=\"node\"} + node_memory_MemFree_bytes{job=\"node\"} + node_memory_Slab_bytes{job=\"node\"})) / node_memory_MemTotal_bytes{job=\"node\"})" + }, + { + "record": "instance:node_vmstat_pgmajfault:rate5m", + "expression": "rate(node_vmstat_pgmajfault{job=\"node\"}[5m])" + }, + { + "record": "instance_device:node_disk_io_time_seconds:rate5m", + "expression": "rate(node_disk_io_time_seconds_total{job=\"node\", device!=\"\"}[5m])" + }, + { + "record": "instance_device:node_disk_io_time_weighted_seconds:rate5m", + "expression": "rate(node_disk_io_time_weighted_seconds_total{job=\"node\", device!=\"\"}[5m])" + }, + { + "record": "instance:node_network_receive_bytes_excluding_lo:rate5m", + "expression": "sum without (device) (rate(node_network_receive_bytes_total{job=\"node\", device!=\"lo\"}[5m]))" + }, + { + "record": "instance:node_network_transmit_bytes_excluding_lo:rate5m", + "expression": "sum without (device) (rate(node_network_transmit_bytes_total{job=\"node\", device!=\"lo\"}[5m]))" + }, + { + "record": "instance:node_network_receive_drop_excluding_lo:rate5m", + "expression": "sum without (device) (rate(node_network_receive_drop_total{job=\"node\", device!=\"lo\"}[5m]))" + }, + { + "record": "instance:node_network_transmit_drop_excluding_lo:rate5m", + "expression": "sum without (device) (rate(node_network_transmit_drop_total{job=\"node\", device!=\"lo\"}[5m]))" + } + ] + } + }, + { + "type": "Microsoft.AlertsManagement/prometheusRuleGroups", + "apiVersion": "2023-03-01", + "name": "[format('KubernetesRecordingRulesRuleGroup-{0}', parameters('clusterName'))]", + "location": "[parameters('location')]", + "properties": { + "interval": "PT1M", + "enabled": true, + "clusterName": "[parameters('clusterName')]", + "scopes": [ + "[parameters('monitorId')]" + ], + "rules": [ + { + "record": "node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate", + "expression": "sum by (cluster, namespace, pod, container) (irate(container_cpu_usage_seconds_total{job=\"cadvisor\", image!=\"\"}[5m])) * on (cluster, namespace, pod) group_left(node) topk by (cluster, namespace, pod) (1, max by(cluster, namespace, pod, node) (kube_pod_info{node!=\"\"}))" + }, + { + "record": "node_namespace_pod_container:container_memory_working_set_bytes", + "expression": "container_memory_working_set_bytes{job=\"cadvisor\", image!=\"\"}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))" + }, + { + "record": "node_namespace_pod_container:container_memory_rss", + "expression": "container_memory_rss{job=\"cadvisor\", image!=\"\"}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))" + }, + { + "record": "node_namespace_pod_container:container_memory_cache", + "expression": "container_memory_cache{job=\"cadvisor\", image!=\"\"}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))" + }, + { + "record": "node_namespace_pod_container:container_memory_swap", + "expression": "container_memory_swap{job=\"cadvisor\", image!=\"\"}* on (namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))" + }, + { + "record": "cluster:namespace:pod_memory:active:kube_pod_container_resource_requests", + "expression": "kube_pod_container_resource_requests{resource=\"memory\",job=\"kube-state-metrics\"} * on(namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))" + }, + { + "record": "namespace_memory:kube_pod_container_resource_requests:sum", + "expression": "sum by (namespace, cluster) (sum by (namespace, pod, cluster) (max by (namespace, pod, container, cluster) (kube_pod_container_resource_requests{resource=\"memory\",job=\"kube-state-metrics\"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))" + }, + { + "record": "cluster:namespace:pod_cpu:active:kube_pod_container_resource_requests", + "expression": "kube_pod_container_resource_requests{resource=\"cpu\",job=\"kube-state-metrics\"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))" + }, + { + "record": "namespace_cpu:kube_pod_container_resource_requests:sum", + "expression": "sum by (namespace, cluster) (sum by(namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_requests{resource=\"cpu\",job=\"kube-state-metrics\"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))" + }, + { + "record": "cluster:namespace:pod_memory:active:kube_pod_container_resource_limits", + "expression": "kube_pod_container_resource_limits{resource=\"memory\",job=\"kube-state-metrics\"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))" + }, + { + "record": "namespace_memory:kube_pod_container_resource_limits:sum", + "expression": "sum by (namespace, cluster) (sum by (namespace, pod, cluster) (max by (namespace, pod, container, cluster) (kube_pod_container_resource_limits{resource=\"memory\",job=\"kube-state-metrics\"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))" + }, + { + "record": "cluster:namespace:pod_cpu:active:kube_pod_container_resource_limits", + "expression": "kube_pod_container_resource_limits{resource=\"cpu\",job=\"kube-state-metrics\"} * on (namespace, pod, cluster)group_left() max by (namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))" + }, + { + "record": "namespace_cpu:kube_pod_container_resource_limits:sum", + "expression": "sum by (namespace, cluster) (sum by (namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_limits{resource=\"cpu\",job=\"kube-state-metrics\"}) * on(namespace, pod, cluster) group_left() max by (namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))" + }, + { + "record": "namespace_workload_pod:kube_pod_owner:relabel", + "expression": "max by (cluster, namespace, workload, pod) (label_replace(label_replace(kube_pod_owner{job=\"kube-state-metrics\", owner_kind=\"ReplicaSet\"}, \"replicaset\", \"$1\", \"owner_name\", \"(.*)\") * on(replicaset, namespace) group_left(owner_name) topk by(replicaset, namespace) (1, max by (replicaset, namespace, owner_name) (kube_replicaset_owner{job=\"kube-state-metrics\"})), \"workload\", \"$1\", \"owner_name\", \"(.*)\"))", + "labels": { + "workload_type": "deployment" + } + }, + { + "record": "namespace_workload_pod:kube_pod_owner:relabel", + "expression": "max by (cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job=\"kube-state-metrics\", owner_kind=\"DaemonSet\"}, \"workload\", \"$1\", \"owner_name\", \"(.*)\"))", + "labels": { + "workload_type": "daemonset" + } + }, + { + "record": "namespace_workload_pod:kube_pod_owner:relabel", + "expression": "max by (cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job=\"kube-state-metrics\", owner_kind=\"StatefulSet\"}, \"workload\", \"$1\", \"owner_name\", \"(.*)\"))", + "labels": { + "workload_type": "statefulset" + } + }, + { + "record": "namespace_workload_pod:kube_pod_owner:relabel", + "expression": "max by (cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job=\"kube-state-metrics\", owner_kind=\"Job\"}, \"workload\", \"$1\", \"owner_name\", \"(.*)\"))", + "labels": { + "workload_type": "job" + } + }, + { + "record": ":node_memory_MemAvailable_bytes:sum", + "expression": "sum(node_memory_MemAvailable_bytes{job=\"node\"} or (node_memory_Buffers_bytes{job=\"node\"} + node_memory_Cached_bytes{job=\"node\"} + node_memory_MemFree_bytes{job=\"node\"} + node_memory_Slab_bytes{job=\"node\"})) by (cluster)" + }, + { + "record": "cluster:node_cpu:ratio_rate5m", + "expression": "sum(rate(node_cpu_seconds_total{job=\"node\",mode!=\"idle\",mode!=\"iowait\",mode!=\"steal\"}[5m])) by (cluster) /count(sum(node_cpu_seconds_total{job=\"node\"}) by (cluster, instance, cpu)) by (cluster)" + } + ] + } + }, + { + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2022-06-01", + "name": "[format('MSCI-{0}', parameters('clusterName'))]", + "location": "[parameters('location')]", + "kind": "Linux", + "properties": { + "dataSources": { + "extensions": [ + { + "streams": [ + "Microsoft-ContainerInsights-Group-Default" + ], + "extensionName": "ContainerInsights", + "extensionSettings": { + "dataCollectionSettings": { + "enableContainerLogV2": true, + "interval": "1m", + "namespaceFilteringMode": "Off" + } + }, + "name": "ContainerInsightsExtension" + } + ] + }, + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "[parameters('logAnalyticsId')]", + "name": "[parameters('logAnalyticsName')]" + } + ] + }, + "dataFlows": [ + { + "streams": [ + "Microsoft-ContainerInsights-Group-Default" + ], + "destinations": [ + "[parameters('logAnalyticsName')]" + ] + } + ] + } + }, + { + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2022-06-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[format('msci-{0}', parameters('clusterName'))]", + "properties": { + "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', format('MSProm-{0}', parameters('clusterName')))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/dataCollectionRules', format('MSProm-{0}', parameters('clusterName')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "grafanaRoleAssignmentForMe", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('principalId')]" + }, + "principalType": { + "value": "User" + }, + "roleDefinitionId": { + "value": "[variables('grafanaAdminRole')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "5795525499710207356" + }, + "description": "Creates a role assignment for a service principal." + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "monitorRoleAssignmentForGrafana", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.Dashboard/grafana', parameters('name')), '2022-08-01', 'full').identity.principalId]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "roleDefinitionId": { + "value": "[variables('monitorReaderRole')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "5795525499710207356" + }, + "description": "Creates a role assignment for a service principal." + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Dashboard/grafana', parameters('name'))]" + ] + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'kubernetes')]", + "[resourceId('Microsoft.Resources/deployments', 'log-analytics')]", + "[resourceId('Microsoft.Resources/deployments', 'monitor')]" + ] + }, + { + "condition": "[parameters('deployAcr')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-registry", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('containerRegistryName'))), createObject('value', parameters('containerRegistryName')), createObject('value', format('{0}{1}', variables('abbrs').containerRegistryRegistries, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "sku": { + "value": { + "name": "Premium" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "2826959983387823188" + }, + "description": "Creates an Azure Container Registry." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "adminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether admin user is enabled" + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether anonymous pull is enabled" + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Indicates whether data endpoint is enabled" + } + }, + "encryption": { + "type": "object", + "defaultValue": { + "status": "disabled" + }, + "metadata": { + "description": "Encryption settings" + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "metadata": { + "description": "Options for bypassing network rules" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting" + } + }, + "sku": { + "type": "object", + "defaultValue": { + "name": "Basic" + }, + "metadata": { + "description": "SKU settings" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting" + } + }, + "workspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The log analytics workspace ID used for logging and monitoring" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "adminUserEnabled": "[parameters('adminUserEnabled')]", + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "encryption": "[parameters('encryption')]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + }, + { + "condition": "[not(empty(parameters('workspaceId')))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "registry-diagnostics", + "properties": { + "workspaceId": "[parameters('workspaceId')]", + "logs": [ + { + "category": "ContainerRegistryRepositoryEvents", + "enabled": true + }, + { + "category": "ContainerRegistryLoginEvents", + "enabled": true + } + ], + "metrics": [ + { + "category": "AllMetrics", + "enabled": true, + "timeGrain": "PT1M" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + ] + } + ], + "outputs": { + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').loginServer]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "condition": "[parameters('deployAcr')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "acr-pull-role-assignment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": "[if(parameters('deployAcr'), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value), createObject('value', ''))]", + "principalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'kubernetes'), '2022-09-01').outputs.clusterIdentity.value.objectId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "16513018934906094568" + }, + "description": "Assigns ACR Pull permissions to access an Azure Container Registry." + }, + "parameters": { + "containerRegistryName": { + "type": "string" + }, + "principalId": { + "type": "string" + } + }, + "variables": { + "acrPullRole": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('containerRegistryName'))]", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), variables('acrPullRole'))]", + "properties": { + "roleDefinitionId": "[variables('acrPullRole')]", + "principalType": "ServicePrincipal", + "principalId": "[parameters('principalId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'container-registry')]", + "[resourceId('Microsoft.Resources/deployments', 'kubernetes')]" + ] + } + ], + "outputs": { + "AZURE_RESOURCEGROUP_NAME": { + "type": "string", + "value": "[resourceGroup().name]" + }, + "AZURE_AKS_CLUSTER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'kubernetes'), '2022-09-01').outputs.clusterName.value]" + }, + "AZURE_OPENAI_MODEL_NAME": { + "type": "string", + "value": "[parameters('openAiModelName')]" + }, + "AZURE_OPENAI_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'openai'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_IDENTITY_CLIENT_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2022-09-01').outputs.clientId.value]" + }, + "AZURE_SERVICE_BUS_HOST": { + "type": "string", + "value": "[format('{0}.servicebus.windows.net', reference(resourceId('Microsoft.Resources/deployments', 'servicebus'), '2022-09-01').outputs.serviceBusNamespaceName.value)]" + }, + "AZURE_SERVICE_BUS_URI": { + "type": "string", + "value": "[format('amqps://{0}.servicebus.windows.net', reference(resourceId('Microsoft.Resources/deployments', 'servicebus'), '2022-09-01').outputs.serviceBusNamespaceName.value)]" + }, + "AZURE_SERVICE_BUS_LISTENER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'servicebus'), '2022-09-01').outputs.serviceBusListenerName.value]" + }, + "AZURE_SERVICE_BUS_LISTENER_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'servicebus'), '2022-09-01').outputs.serviceBusListenerKey.value]" + }, + "AZURE_SERVICE_BUS_SENDER_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'servicebus'), '2022-09-01').outputs.serviceBusSenderName.value]" + }, + "AZURE_SERVICE_BUS_SENDER_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'servicebus'), '2022-09-01').outputs.serviceBusSenderKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.name.value]" + }, + "AZURE_COSMOS_DATABASE_URI": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_COSMOS_DATABASE_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'get-keys'), '2022-09-01').outputs.cosmosKey.value]" + }, + "AZURE_AKS_NAMESPACE": { + "type": "string", + "value": "[parameters('k8s_namespace')]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "AZURE_DATABASE_API": { + "type": "string", + "value": "[if(equals(parameters('cosmosdbAccountKind'), 'MongoDB'), 'mongodb', 'cosmosdbsql')]" + }, + "AZURE_REGISTRY_NAME": { + "type": "string", + "value": "[if(parameters('deployAcr'), reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.name.value, '')]" + }, + "AZURE_REGISTRY_URI": { + "type": "string", + "value": "[if(parameters('deployAcr'), reference(resourceId('Microsoft.Resources/deployments', 'container-registry'), '2022-09-01').outputs.loginServer.value, 'ghcr.io/azure-samples')]" + } + } +} \ No newline at end of file From 8f4899ded53467ebedbb76c0ab0b089353017b3b Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 15 Apr 2024 21:19:11 +0800 Subject: [PATCH 086/112] refine info --- Environments/AKS-Store-Demo/manifest.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Environments/AKS-Store-Demo/manifest.yaml b/Environments/AKS-Store-Demo/manifest.yaml index d7cc143c..6df8c680 100644 --- a/Environments/AKS-Store-Demo/manifest.yaml +++ b/Environments/AKS-Store-Demo/manifest.yaml @@ -1,13 +1,13 @@ -name: eShop +name: AKS-Store-Demo version: 1.0.0 -summary: eShop Reference Application -description: eShop Reference Application - "Northern Mountains" +summary: AKS Store Demo +description: A realistic scenario using a polyglot architecture, event-driven design, and common open source back-end services (eg - RabbitMQ, MongoDB) runner: ARM templatePath: azuredeploy.json parameters: - id: "environmentName" - name: "Environment Name (e.g. test)" + name: "Environment Name (e.g. aksstoreenv1)" description: "Name of the Environment" type: "string" required: true From 8a318bf54d819fbe68ac0c214074ad78951b54f7 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 19 Apr 2024 20:00:32 +0800 Subject: [PATCH 087/112] add app config store --- Environments/Todo-Mongo-AKS/main.bicep | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index 378c06a8..3dd31c7a 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -12,6 +12,7 @@ param cosmosDatabaseName string = '' param keyVaultName string = '' param principalId string = '' param aksClusterIdentityObjectId string +param configStoreName string = '' var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) @@ -48,6 +49,48 @@ module clusterKeyVaultAccess './core/security/keyvault-access.bicep' = { } } +@description('Specifies the content type of the key-value resources. For feature flag, the value should be application/vnd.microsoft.appconfig.ff+json;charset=utf-8. For Key Value reference, the value should be application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8. Otherwise, it\'s optional.') +param contentType string = '' + + +resource configStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' = { + name: !empty(configStoreName) ? configStoreName : '${abbrs.appConfigurationConfigurationStores}-${resourceToken}' + location: location + sku: { + name: 'standard' + } +} + +resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { + parent: configStore + name: 'AZURE_COSMOS_CONNECTION_STRING_KEY' + properties: { + value: cosmos.outputs.connectionStringKey + contentType: contentType + tags: tags + } +} + +resource configStoreKeyValue2 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { + parent: configStore + name: 'AZURE_COSMOS_DATABASE_NAME' + properties: { + value: cosmos.outputs.databaseName + contentType: contentType + tags: tags + } +} + +resource configStoreKeyValue3 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { + parent: configStore + name: 'AZURE_KEY_VAULT_ENDPOINT' + properties: { + value: keyVault.outputs.endpoint + contentType: contentType + tags: tags + } +} + // Data outputs output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName From 84bac103da09ce6737c5320bb19954b2cb0f1d2c Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 19 Apr 2024 12:03:02 +0000 Subject: [PATCH 088/112] Rebuild ARM templates --- Environments/AKS-Store-Demo/azuredeploy.json | 116 +++++++++--------- Environments/AKS/azuredeploy.json | 64 +++++----- Environments/APIM/azuredeploy.json | 44 +++---- .../App-Base-WebApp-ACA/azuredeploy.json | 96 +++++++-------- .../App-Base-WebApp-AKS/azuredeploy.json | 28 ++--- Environments/AppVNet/azuredeploy.json | 12 +- Environments/ContainerApp/azuredeploy.json | 32 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 44 +++---- .../azuredeploy.json | 44 +++---- .../azuredeploy.json | 44 +++---- Environments/FunctionApp/azuredeploy.json | 4 +- Environments/OpenAISearch/azuredeploy.json | 64 +++++----- .../OpenAISummarization/azuredeploy.json | 36 +++--- Environments/Sandbox/azuredeploy.json | 4 +- Environments/Spring/azuredeploy.json | 12 +- Environments/Todo-Mongo-ACA/azuredeploy.json | 72 +++++------ Environments/Todo-Mongo-AKS/azuredeploy.json | 90 +++++++++++--- .../Todo-Nodejs-Mongo-ACA/azuredeploy.json | 96 +++++++-------- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 68 +++++----- Environments/Todo-Shared-ACA/azuredeploy.json | 28 ++--- Environments/Todo-Shared-AKS/azuredeploy.json | 44 +++---- Environments/WebApp/azuredeploy.json | 4 +- Environments/eShop/azuredeploy.json | 8 +- 26 files changed, 600 insertions(+), 538 deletions(-) diff --git a/Environments/AKS-Store-Demo/azuredeploy.json b/Environments/AKS-Store-Demo/azuredeploy.json index 60633bb0..c2d2d0ad 100644 --- a/Environments/AKS-Store-Demo/azuredeploy.json +++ b/Environments/AKS-Store-Demo/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12442973100557358859" + "version": "0.26.170.59819", + "templateHash": "575350053962925419" } }, "parameters": { @@ -278,8 +278,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15117202545295723007" + "version": "0.26.170.59819", + "templateHash": "4186512015795284910" }, "description": "Creates an Azure Cognitive Services instance." }, @@ -418,8 +418,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15628495845320500833" + "version": "0.26.170.59819", + "templateHash": "2445544970077435003" } }, "parameters": { @@ -495,8 +495,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5795525499710207356" + "version": "0.26.170.59819", + "templateHash": "2390256577307700589" }, "description": "Creates a role assignment for a service principal." }, @@ -563,8 +563,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5795525499710207356" + "version": "0.26.170.59819", + "templateHash": "2390256577307700589" }, "description": "Creates a role assignment for a service principal." }, @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10925305564301015943" + "version": "0.26.170.59819", + "templateHash": "1580517862479751439" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -918,8 +918,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4552321833419182500" + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" }, "description": "Creates an Azure Key Vault." }, @@ -1002,8 +1002,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16337532685264148186" + "version": "0.26.170.59819", + "templateHash": "12563724890341115562" } }, "parameters": { @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13527588968236853243" + "version": "0.26.170.59819", + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1192,8 +1192,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3220231753590578091" + "version": "0.26.170.59819", + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1253,8 +1253,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1427,8 +1427,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "498024263025124773" + "version": "0.26.170.59819", + "templateHash": "7737694719083989185" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -1525,8 +1525,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "9969697426957325100" + "version": "0.26.170.59819", + "templateHash": "15106079368020532347" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1579,8 +1579,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1717,8 +1717,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16222518383215599293" + "version": "0.26.170.59819", + "templateHash": "16206905209322787989" }, "description": "Creates a SQL role definition under an Azure Cosmos DB account." }, @@ -1797,8 +1797,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13520160008714254891" + "version": "0.26.170.59819", + "templateHash": "5580476706925703677" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -1908,8 +1908,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1269692203398953166" + "version": "0.26.170.59819", + "templateHash": "2125607583208873548" } }, "parameters": { @@ -2006,8 +2006,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4135456988027214147" + "version": "0.26.170.59819", + "templateHash": "12761061430428978799" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2094,8 +2094,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4135456988027214147" + "version": "0.26.170.59819", + "templateHash": "12761061430428978799" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2215,8 +2215,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17291248993155438634" + "version": "0.26.170.59819", + "templateHash": "3109754192914374009" } }, "parameters": { @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4135456988027214147" + "version": "0.26.170.59819", + "templateHash": "12761061430428978799" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2350,8 +2350,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4135456988027214147" + "version": "0.26.170.59819", + "templateHash": "12761061430428978799" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2452,8 +2452,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "8130588533889525221" + "version": "0.26.170.59819", + "templateHash": "2288712867213955884" } }, "parameters": { @@ -2514,8 +2514,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -2610,8 +2610,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "446387552123988459" + "version": "0.26.170.59819", + "templateHash": "11818783974622592424" } }, "parameters": { @@ -2993,8 +2993,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5795525499710207356" + "version": "0.26.170.59819", + "templateHash": "2390256577307700589" }, "description": "Creates a role assignment for a service principal." }, @@ -3058,8 +3058,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5795525499710207356" + "version": "0.26.170.59819", + "templateHash": "2390256577307700589" }, "description": "Creates a role assignment for a service principal." }, @@ -3139,8 +3139,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -3308,8 +3308,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index b7567a4f..a1e89c59 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14705962401270339344" + "version": "0.26.170.59819", + "templateHash": "2114239967834469967" } }, "parameters": { @@ -243,8 +243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4834662464872905634" + "version": "0.26.170.59819", + "templateHash": "15147942591900454453" } }, "parameters": { @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17255267993693195617" + "version": "0.26.170.59819", + "templateHash": "16475877113553841949" } }, "parameters": { @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "398383251887108056" + "version": "0.26.170.59819", + "templateHash": "9199765229548176326" } }, "parameters": { @@ -787,8 +787,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5711878933290893029" + "version": "0.26.170.59819", + "templateHash": "1313507774670351919" } }, "parameters": { @@ -932,8 +932,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17324306373551752378" + "version": "0.26.170.59819", + "templateHash": "9803835955542413586" } }, "parameters": { @@ -990,8 +990,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4546930119972086224" + "version": "0.26.170.59819", + "templateHash": "593635419190044178" } }, "parameters": { @@ -1105,8 +1105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6446084179936994854" + "version": "0.26.170.59819", + "templateHash": "18381885242494360931" } }, "parameters": { @@ -1186,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10221031687472873373" + "version": "0.26.170.59819", + "templateHash": "16622766300017925980" } }, "parameters": { @@ -1293,8 +1293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6004410298470182087" + "version": "0.26.170.59819", + "templateHash": "17196584048072227928" } }, "parameters": { @@ -1353,8 +1353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2712602865217069438" + "version": "0.26.170.59819", + "templateHash": "7688648017235778909" } }, "parameters": { @@ -1532,8 +1532,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15989205648669899326" + "version": "0.26.170.59819", + "templateHash": "13433119241471746696" } }, "parameters": { @@ -1609,8 +1609,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "8004480784637054546" + "version": "0.26.170.59819", + "templateHash": "6525308576612279557" } }, "parameters": { @@ -1659,8 +1659,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11537399683787126617" + "version": "0.26.170.59819", + "templateHash": "17741619895875240893" } }, "parameters": { @@ -1739,8 +1739,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10878265115034450635" + "version": "0.26.170.59819", + "templateHash": "1554147265545062632" } }, "parameters": { @@ -1801,8 +1801,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1426875865049868025" + "version": "0.26.170.59819", + "templateHash": "8407206116835390544" } }, "parameters": { diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json index ae091996..d1f9f27d 100644 --- a/Environments/APIM/azuredeploy.json +++ b/Environments/APIM/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6279479381596719762" + "version": "0.26.170.59819", + "templateHash": "8180529666560946925" } }, "parameters": { @@ -82,8 +82,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "8004480784637054546" + "version": "0.26.170.59819", + "templateHash": "6525308576612279557" } }, "parameters": { @@ -132,8 +132,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11537399683787126617" + "version": "0.26.170.59819", + "templateHash": "17741619895875240893" } }, "parameters": { @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10878265115034450635" + "version": "0.26.170.59819", + "templateHash": "1554147265545062632" } }, "parameters": { @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1426875865049868025" + "version": "0.26.170.59819", + "templateHash": "8407206116835390544" } }, "parameters": { @@ -1582,8 +1582,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3986547581209693951" + "version": "0.26.170.59819", + "templateHash": "15657907071797536763" } }, "parameters": { @@ -1711,8 +1711,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5977381490546492812" + "version": "0.26.170.59819", + "templateHash": "10221529978834853957" } }, "parameters": { @@ -1811,8 +1811,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13689214219732288912" + "version": "0.26.170.59819", + "templateHash": "9806810283516288067" } }, "parameters": { @@ -2015,8 +2015,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13412871015897770137" + "version": "0.26.170.59819", + "templateHash": "16674553724797042504" } }, "parameters": { @@ -2263,8 +2263,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7053410133338541789" + "version": "0.26.170.59819", + "templateHash": "16898575566825802440" } }, "parameters": { @@ -2397,8 +2397,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5630097546441939340" + "version": "0.26.170.59819", + "templateHash": "13872673163903146271" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-ACA/azuredeploy.json b/Environments/App-Base-WebApp-ACA/azuredeploy.json index 0fb8ad41..9cbe6845 100644 --- a/Environments/App-Base-WebApp-ACA/azuredeploy.json +++ b/Environments/App-Base-WebApp-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5906921002916319723" + "version": "0.26.170.59819", + "templateHash": "14812900086505163857" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18420764662990746250" + "version": "0.26.170.59819", + "templateHash": "13349567422847897213" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18309103684351395002" + "version": "0.26.170.59819", + "templateHash": "18400100082982052090" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "8837302822615064546" + "version": "0.26.170.59819", + "templateHash": "10296126065300176669" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15518016269231653446" + "version": "0.26.170.59819", + "templateHash": "1456353489486868071" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7330114957522770158" + "version": "0.26.170.59819", + "templateHash": "16188590616914828195" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15133572285528423972" + "version": "0.26.170.59819", + "templateHash": "12637092007762978392" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4480412712998156633" + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15518016269231653446" + "version": "0.26.170.59819", + "templateHash": "1456353489486868071" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7330114957522770158" + "version": "0.26.170.59819", + "templateHash": "16188590616914828195" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3191030148362316528" + "version": "0.26.170.59819", + "templateHash": "9506725499169300305" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13527588968236853243" + "version": "0.26.170.59819", + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3220231753590578091" + "version": "0.26.170.59819", + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2695,8 +2695,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4552321833419182500" + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" }, "description": "Creates an Azure Key Vault." }, @@ -2773,8 +2773,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2825,8 +2825,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -2906,8 +2906,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2971,8 +2971,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4282,8 +4282,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3385554986928067105" + "version": "0.26.170.59819", + "templateHash": "9679689483306263421" }, "description": "Creates an Azure API Management instance." }, @@ -4432,8 +4432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5801551854136161809" + "version": "0.26.170.59819", + "templateHash": "15612687543846078835" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json index d1ee4b7c..f6454f39 100644 --- a/Environments/App-Base-WebApp-AKS/azuredeploy.json +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1751087585348510436" + "version": "0.26.170.59819", + "templateHash": "8340638631147543749" } }, "parameters": { @@ -219,8 +219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3191030148362316528" + "version": "0.26.170.59819", + "templateHash": "9506725499169300305" } }, "parameters": { @@ -300,8 +300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13527588968236853243" + "version": "0.26.170.59819", + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -408,8 +408,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3220231753590578091" + "version": "0.26.170.59819", + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -469,8 +469,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4552321833419182500" + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" }, "description": "Creates an Azure Key Vault." }, @@ -724,8 +724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4480412712998156633" + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" }, "description": "Assigns an Azure Key Vault access policy." }, diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json index ee5d8f75..9d2c108e 100644 --- a/Environments/AppVNet/azuredeploy.json +++ b/Environments/AppVNet/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5876871532578709430" + "version": "0.26.170.59819", + "templateHash": "5481419951669703164" } }, "parameters": { @@ -81,8 +81,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "800240006004044154" + "version": "0.26.170.59819", + "templateHash": "12886199108543213420" } }, "parameters": { @@ -369,8 +369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2391425913980408554" + "version": "0.26.170.59819", + "templateHash": "2047106680514800790" } }, "parameters": { diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json index e7d3e340..c551c521 100644 --- a/Environments/ContainerApp/azuredeploy.json +++ b/Environments/ContainerApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15939569857305320444" + "version": "0.26.170.59819", + "templateHash": "3405922185286300434" } }, "parameters": { @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15989205648669899326" + "version": "0.26.170.59819", + "templateHash": "13433119241471746696" } }, "parameters": { @@ -161,8 +161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17990678596458258198" + "version": "0.26.170.59819", + "templateHash": "9674021159051080173" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6228823906425620387" + "version": "0.26.170.59819", + "templateHash": "1904463018937934078" } }, "parameters": { @@ -293,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6416926961886581978" + "version": "0.26.170.59819", + "templateHash": "15787024188471042356" } }, "parameters": { @@ -434,8 +434,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "8313026216379119506" + "version": "0.26.170.59819", + "templateHash": "2050257844492594652" } }, "parameters": { @@ -508,8 +508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5397344132030627297" + "version": "0.26.170.59819", + "templateHash": "9910213086950599767" } }, "parameters": { @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11537399683787126617" + "version": "0.26.170.59819", + "templateHash": "17741619895875240893" } }, "parameters": { diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json index 51b71b5a..dfa2ee0e 100644 --- a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15561992108809927454" + "version": "0.26.170.59819", + "templateHash": "1541560146893703868" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1868577110995523309" + "version": "0.26.170.59819", + "templateHash": "3938742814317506649" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18309103684351395002" + "version": "0.26.170.59819", + "templateHash": "18400100082982052090" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json index 51b71b5a..dfa2ee0e 100644 --- a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15561992108809927454" + "version": "0.26.170.59819", + "templateHash": "1541560146893703868" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1868577110995523309" + "version": "0.26.170.59819", + "templateHash": "3938742814317506649" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18309103684351395002" + "version": "0.26.170.59819", + "templateHash": "18400100082982052090" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json index 51b71b5a..dfa2ee0e 100644 --- a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15561992108809927454" + "version": "0.26.170.59819", + "templateHash": "1541560146893703868" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1868577110995523309" + "version": "0.26.170.59819", + "templateHash": "3938742814317506649" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18309103684351395002" + "version": "0.26.170.59819", + "templateHash": "18400100082982052090" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json index 63f70f55..7e8f6ecd 100644 --- a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1499570709905675063" + "version": "0.26.170.59819", + "templateHash": "8818437751297515128" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5116950368212491210" + "version": "0.26.170.59819", + "templateHash": "11747651847294354491" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1485594887423027715" + "version": "0.26.170.59819", + "templateHash": "2601248910853177384" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6919390155052012096" + "version": "0.26.170.59819", + "templateHash": "6475340733870349157" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16891164712798672456" + "version": "0.26.170.59819", + "templateHash": "17295385747004895580" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json index 63f70f55..7e8f6ecd 100644 --- a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1499570709905675063" + "version": "0.26.170.59819", + "templateHash": "8818437751297515128" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5116950368212491210" + "version": "0.26.170.59819", + "templateHash": "11747651847294354491" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1485594887423027715" + "version": "0.26.170.59819", + "templateHash": "2601248910853177384" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6919390155052012096" + "version": "0.26.170.59819", + "templateHash": "6475340733870349157" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16891164712798672456" + "version": "0.26.170.59819", + "templateHash": "17295385747004895580" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json index 63f70f55..7e8f6ecd 100644 --- a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1499570709905675063" + "version": "0.26.170.59819", + "templateHash": "8818437751297515128" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5116950368212491210" + "version": "0.26.170.59819", + "templateHash": "11747651847294354491" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1485594887423027715" + "version": "0.26.170.59819", + "templateHash": "2601248910853177384" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6919390155052012096" + "version": "0.26.170.59819", + "templateHash": "6475340733870349157" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16891164712798672456" + "version": "0.26.170.59819", + "templateHash": "17295385747004895580" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index f18e17bf..1f2bc2a8 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17337791585652966371" + "version": "0.26.170.59819", + "templateHash": "11565551485009026934" } }, "parameters": { diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 53a31f4d..3eb4f829 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7270598933171374278" + "version": "0.26.170.59819", + "templateHash": "17572017660740017147" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11596624744744955282" + "version": "0.26.170.59819", + "templateHash": "17253896351883621545" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13746138269004593570" + "version": "0.26.170.59819", + "templateHash": "11772465959055468166" } }, "parameters": { @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15261049305082890883" + "version": "0.26.170.59819", + "templateHash": "8783925983412484416" } }, "parameters": { @@ -772,8 +772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15261049305082890883" + "version": "0.26.170.59819", + "templateHash": "8783925983412484416" } }, "parameters": { @@ -902,8 +902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15486167099316024236" + "version": "0.26.170.59819", + "templateHash": "11486757407246904206" } }, "parameters": { @@ -1029,8 +1029,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15302819503019861766" + "version": "0.26.170.59819", + "templateHash": "13296914309705716766" } }, "parameters": { @@ -1201,8 +1201,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1265,8 +1265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1329,8 +1329,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1393,8 +1393,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1457,8 +1457,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1521,8 +1521,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1585,8 +1585,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1652,8 +1652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { @@ -1719,8 +1719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2682974829435550284" + "version": "0.26.170.59819", + "templateHash": "16195430160742886805" } }, "parameters": { diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index 8d750669..6e90351d 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2872422258645152072" + "version": "0.26.170.59819", + "templateHash": "3965327044584118956" } }, "parameters": { @@ -249,8 +249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16884556765387010262" + "version": "0.26.170.59819", + "templateHash": "12580763560407085691" } }, "parameters": { @@ -343,8 +343,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14111957338638220978" + "version": "0.26.170.59819", + "templateHash": "1464507928829179730" } }, "parameters": { @@ -438,8 +438,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "9798428962718279486" + "version": "0.26.170.59819", + "templateHash": "16047572591945988050" } }, "parameters": { @@ -603,8 +603,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14238317958653849476" + "version": "0.26.170.59819", + "templateHash": "5885215772604838392" } }, "parameters": { @@ -660,8 +660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14238317958653849476" + "version": "0.26.170.59819", + "templateHash": "5885215772604838392" } }, "parameters": { @@ -717,8 +717,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14238317958653849476" + "version": "0.26.170.59819", + "templateHash": "5885215772604838392" } }, "parameters": { @@ -774,8 +774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14238317958653849476" + "version": "0.26.170.59819", + "templateHash": "5885215772604838392" } }, "parameters": { @@ -831,8 +831,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14238317958653849476" + "version": "0.26.170.59819", + "templateHash": "5885215772604838392" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index 0a957344..0accad9f 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6609161197123188811" + "version": "0.26.170.59819", + "templateHash": "9728838765300640554" } }, "resources": [] diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index 48d05374..2431aefd 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "392445969946894024" + "version": "0.26.170.59819", + "templateHash": "7459537732843475328" } }, "parameters": { @@ -143,8 +143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3493762751568630334" + "version": "0.26.170.59819", + "templateHash": "15588594590211857731" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "9105768340151577374" + "version": "0.26.170.59819", + "templateHash": "16898314659086455378" } }, "parameters": { diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index 166348eb..659c655f 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15721463991948645518" + "version": "0.26.170.59819", + "templateHash": "7670045821900577611" } }, "parameters": { @@ -295,8 +295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15275434175670248923" + "version": "0.26.170.59819", + "templateHash": "2219027145645725366" } }, "parameters": { @@ -410,8 +410,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12646603870028624690" + "version": "0.26.170.59819", + "templateHash": "18035806118199538117" }, "description": "Creates or updates an existing Azure Container App." }, @@ -669,8 +669,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17904700932623043957" + "version": "0.26.170.59819", + "templateHash": "7489884223496332039" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -923,8 +923,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10402974300023168185" + "version": "0.26.170.59819", + "templateHash": "17852591206127636459" } }, "parameters": { @@ -1162,8 +1162,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4480412712998156633" + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1283,8 +1283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12646603870028624690" + "version": "0.26.170.59819", + "templateHash": "18035806118199538117" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1542,8 +1542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17904700932623043957" + "version": "0.26.170.59819", + "templateHash": "7489884223496332039" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1796,8 +1796,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1937,8 +1937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -2114,8 +2114,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3191030148362316528" + "version": "0.26.170.59819", + "templateHash": "9506725499169300305" } }, "parameters": { @@ -2195,8 +2195,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13527588968236853243" + "version": "0.26.170.59819", + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2303,8 +2303,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3220231753590578091" + "version": "0.26.170.59819", + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2364,8 +2364,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2544,8 +2544,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4552321833419182500" + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" }, "description": "Creates an Azure Key Vault." }, @@ -2624,8 +2624,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3385554986928067105" + "version": "0.26.170.59819", + "templateHash": "9679689483306263421" }, "description": "Creates an Azure API Management instance." }, @@ -2771,8 +2771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5801551854136161809" + "version": "0.26.170.59819", + "templateHash": "15612687543846078835" } }, "parameters": { diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index d1ee4b7c..39663ee3 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1751087585348510436" + "version": "0.26.170.59819", + "templateHash": "11835380275388562123" } }, "parameters": { @@ -43,6 +43,17 @@ }, "aksClusterIdentityObjectId": { "type": "string" + }, + "configStoreName": { + "type": "string", + "defaultValue": "" + }, + "contentType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Specifies the content type of the key-value resources. For feature flag, the value should be application/vnd.microsoft.appconfig.ff+json;charset=utf-8. For Key Value reference, the value should be application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8. Otherwise, it's optional." + } } }, "variables": { @@ -189,6 +200,57 @@ } }, "resources": [ + { + "type": "Microsoft.AppConfiguration/configurationStores", + "apiVersion": "2021-10-01-preview", + "name": "[if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken')))]", + "location": "[parameters('location')]", + "sku": { + "name": "standard" + } + }, + { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2021-10-01-preview", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_CONNECTION_STRING_KEY')]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]", + "contentType": "[parameters('contentType')]", + "tags": "[variables('tags')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" + ] + }, + { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2021-10-01-preview", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_DATABASE_NAME')]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]", + "contentType": "[parameters('contentType')]", + "tags": "[variables('tags')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" + ] + }, + { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2021-10-01-preview", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_KEY_VAULT_ENDPOINT')]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]", + "contentType": "[parameters('contentType')]", + "tags": "[variables('tags')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -219,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3191030148362316528" + "version": "0.26.170.59819", + "templateHash": "9506725499169300305" } }, "parameters": { @@ -300,8 +362,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13527588968236853243" + "version": "0.26.170.59819", + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -408,8 +470,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3220231753590578091" + "version": "0.26.170.59819", + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -469,8 +531,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -649,8 +711,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4552321833419182500" + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" }, "description": "Creates an Azure Key Vault." }, @@ -724,8 +786,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4480412712998156633" + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" }, "description": "Assigns an Azure Key Vault access policy." }, diff --git a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json index 0fb8ad41..9cbe6845 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5906921002916319723" + "version": "0.26.170.59819", + "templateHash": "14812900086505163857" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18420764662990746250" + "version": "0.26.170.59819", + "templateHash": "13349567422847897213" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18309103684351395002" + "version": "0.26.170.59819", + "templateHash": "18400100082982052090" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "8837302822615064546" + "version": "0.26.170.59819", + "templateHash": "10296126065300176669" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15518016269231653446" + "version": "0.26.170.59819", + "templateHash": "1456353489486868071" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7330114957522770158" + "version": "0.26.170.59819", + "templateHash": "16188590616914828195" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15133572285528423972" + "version": "0.26.170.59819", + "templateHash": "12637092007762978392" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4480412712998156633" + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15518016269231653446" + "version": "0.26.170.59819", + "templateHash": "1456353489486868071" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7330114957522770158" + "version": "0.26.170.59819", + "templateHash": "16188590616914828195" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3191030148362316528" + "version": "0.26.170.59819", + "templateHash": "9506725499169300305" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13527588968236853243" + "version": "0.26.170.59819", + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3220231753590578091" + "version": "0.26.170.59819", + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2695,8 +2695,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4552321833419182500" + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" }, "description": "Creates an Azure Key Vault." }, @@ -2773,8 +2773,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2825,8 +2825,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -2906,8 +2906,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2971,8 +2971,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4282,8 +4282,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3385554986928067105" + "version": "0.26.170.59819", + "templateHash": "9679689483306263421" }, "description": "Creates an Azure API Management instance." }, @@ -4432,8 +4432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5801551854136161809" + "version": "0.26.170.59819", + "templateHash": "15612687543846078835" } }, "parameters": { diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json index 1cd7f8ca..88e2fce1 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16064494889423816665" + "version": "0.26.170.59819", + "templateHash": "18446031272399398207" } }, "parameters": { @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5221068689828227975" + "version": "0.26.170.59819", + "templateHash": "6968919828729736056" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -593,8 +593,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1485594887423027715" + "version": "0.26.170.59819", + "templateHash": "2601248910853177384" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -874,8 +874,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6919390155052012096" + "version": "0.26.170.59819", + "templateHash": "6475340733870349157" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -937,8 +937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -1107,8 +1107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1167,8 +1167,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16891164712798672456" + "version": "0.26.170.59819", + "templateHash": "17295385747004895580" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1225,8 +1225,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4480412712998156633" + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1341,8 +1341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3191030148362316528" + "version": "0.26.170.59819", + "templateHash": "9506725499169300305" } }, "parameters": { @@ -1422,8 +1422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13527588968236853243" + "version": "0.26.170.59819", + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1530,8 +1530,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3220231753590578091" + "version": "0.26.170.59819", + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1591,8 +1591,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11668109951104513744" + "version": "0.26.170.59819", + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1771,8 +1771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4552321833419182500" + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" }, "description": "Creates an Azure Key Vault." }, @@ -1849,8 +1849,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1901,8 +1901,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -1982,8 +1982,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2047,8 +2047,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-ACA/azuredeploy.json b/Environments/Todo-Shared-ACA/azuredeploy.json index 51b71b5a..dfa2ee0e 100644 --- a/Environments/Todo-Shared-ACA/azuredeploy.json +++ b/Environments/Todo-Shared-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15561992108809927454" + "version": "0.26.170.59819", + "templateHash": "1541560146893703868" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1868577110995523309" + "version": "0.26.170.59819", + "templateHash": "3938742814317506649" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18309103684351395002" + "version": "0.26.170.59819", + "templateHash": "18400100082982052090" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-AKS/azuredeploy.json b/Environments/Todo-Shared-AKS/azuredeploy.json index 63f70f55..7e8f6ecd 100644 --- a/Environments/Todo-Shared-AKS/azuredeploy.json +++ b/Environments/Todo-Shared-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1499570709905675063" + "version": "0.26.170.59819", + "templateHash": "8818437751297515128" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5116950368212491210" + "version": "0.26.170.59819", + "templateHash": "11747651847294354491" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1485594887423027715" + "version": "0.26.170.59819", + "templateHash": "2601248910853177384" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6919390155052012096" + "version": "0.26.170.59819", + "templateHash": "6475340733870349157" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2826959983387823188" + "version": "0.26.170.59819", + "templateHash": "13368167970047811163" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16513018934906094568" + "version": "0.26.170.59819", + "templateHash": "16305036506142974258" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16891164712798672456" + "version": "0.26.170.59819", + "templateHash": "17295385747004895580" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "54270050405814058" + "version": "0.26.170.59819", + "templateHash": "10048108379044846923" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5766449277789384912" + "version": "0.26.170.59819", + "templateHash": "17101038721523251751" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1697740909761260680" + "version": "0.26.170.59819", + "templateHash": "5016503703443937813" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "623850250427461329" + "version": "0.26.170.59819", + "templateHash": "4596399009213720452" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 036bbe64..581c11db 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5321697744522399711" + "version": "0.26.170.59819", + "templateHash": "9942579113430500584" } }, "parameters": { diff --git a/Environments/eShop/azuredeploy.json b/Environments/eShop/azuredeploy.json index 1a161bb2..95fc95a9 100644 --- a/Environments/eShop/azuredeploy.json +++ b/Environments/eShop/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6443241281576436581" + "version": "0.26.170.59819", + "templateHash": "16311174785231252405" } }, "parameters": { @@ -69,8 +69,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7811954589280961793" + "version": "0.26.170.59819", + "templateHash": "18074359320610316650" } }, "parameters": { From 71db9a4726f2f04a76cf5edbb124c7aa954d8b1c Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 22 Apr 2024 09:50:08 +0800 Subject: [PATCH 089/112] disable local auth for service bus and cosmos db --- Environments/AKS-Store-Demo/app/servicebus.bicep | 3 +++ .../AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep | 1 + Environments/AKS/core/database/cosmos/cosmos-account.bicep | 1 + Environments/APIM/core/database/cosmos/cosmos-account.bicep | 1 + .../core/database/cosmos/cosmos-account.bicep | 1 + .../core/database/cosmos/cosmos-account.bicep | 1 + .../ContainerApp/core/database/cosmos/cosmos-account.bicep | 1 + .../core/database/cosmos/cosmos-account.bicep | 1 + .../core/database/cosmos/cosmos-account.bicep | 1 + .../core/database/cosmos/cosmos-account.bicep | 1 + .../StaticWeb/core/database/cosmos/cosmos-account.bicep | 1 + .../Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep | 1 + .../Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep | 1 + .../core/database/cosmos/cosmos-account.bicep | 1 + .../core/database/cosmos/cosmos-account.bicep | 1 + .../Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep | 1 + 16 files changed, 18 insertions(+) diff --git a/Environments/AKS-Store-Demo/app/servicebus.bicep b/Environments/AKS-Store-Demo/app/servicebus.bicep index 778485d4..b940c47b 100644 --- a/Environments/AKS-Store-Demo/app/servicebus.bicep +++ b/Environments/AKS-Store-Demo/app/servicebus.bicep @@ -14,6 +14,9 @@ resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-01-01-preview sku: { name: 'Standard' } + properties: { + disableLocalAuth: true + } } // Service Bus Namespace Authorization Rule diff --git a/Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep b/Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep +++ b/Environments/AKS-Store-Demo/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/AKS/core/database/cosmos/cosmos-account.bicep b/Environments/AKS/core/database/cosmos/cosmos-account.bicep index 6bc1f2eb..e77ee4b8 100644 --- a/Environments/AKS/core/database/cosmos/cosmos-account.bicep +++ b/Environments/AKS/core/database/cosmos/cosmos-account.bicep @@ -27,6 +27,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/APIM/core/database/cosmos/cosmos-account.bicep b/Environments/APIM/core/database/cosmos/cosmos-account.bicep index 6bc1f2eb..e77ee4b8 100644 --- a/Environments/APIM/core/database/cosmos/cosmos-account.bicep +++ b/Environments/APIM/core/database/cosmos/cosmos-account.bicep @@ -27,6 +27,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep b/Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep +++ b/Environments/App-Base-WebApp-ACA/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep +++ b/Environments/App-Base-WebApp-AKS/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep b/Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep index 6bc1f2eb..e77ee4b8 100644 --- a/Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep +++ b/Environments/ContainerApp/core/database/cosmos/cosmos-account.bicep @@ -27,6 +27,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Contoso-Base-Shared-AKS-Prod/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Contoso-Base-Shared-AKS-Test/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep b/Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep index 6bc1f2eb..e77ee4b8 100644 --- a/Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep +++ b/Environments/StaticWeb/core/database/cosmos/cosmos-account.bicep @@ -27,6 +27,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Todo-Mongo-ACA/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Todo-Mongo-AKS/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Todo-Nodejs-Mongo-ACA/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Todo-Nodejs-Mongo-AKS/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } diff --git a/Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep index 6f8747f5..a742d875 100644 --- a/Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep +++ b/Environments/Todo-Shared-AKS/core/database/cosmos/cosmos-account.bicep @@ -28,6 +28,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { enableMultipleWriteLocations: false apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} capabilities: [ { name: 'EnableServerless' } ] + disableLocalAuth: true } } From 7916153341597adebb38bd1102a219491d7daf30 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Mon, 22 Apr 2024 01:52:45 +0000 Subject: [PATCH 090/112] Rebuild ARM templates --- Environments/AKS-Store-Demo/azuredeploy.json | 27 +++++++++++-------- Environments/AKS/azuredeploy.json | 13 ++++----- .../App-Base-WebApp-ACA/azuredeploy.json | 13 ++++----- .../App-Base-WebApp-AKS/azuredeploy.json | 13 ++++----- Environments/Todo-Mongo-ACA/azuredeploy.json | 13 ++++----- Environments/Todo-Mongo-AKS/azuredeploy.json | 13 ++++----- .../Todo-Nodejs-Mongo-ACA/azuredeploy.json | 13 ++++----- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 13 ++++----- 8 files changed, 65 insertions(+), 53 deletions(-) diff --git a/Environments/AKS-Store-Demo/azuredeploy.json b/Environments/AKS-Store-Demo/azuredeploy.json index c2d2d0ad..49cd5e1c 100644 --- a/Environments/AKS-Store-Demo/azuredeploy.json +++ b/Environments/AKS-Store-Demo/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "575350053962925419" + "templateHash": "10704493879961365284" } }, "parameters": { @@ -1003,7 +1003,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "12563724890341115562" + "templateHash": "1712593165933286164" } }, "parameters": { @@ -1085,7 +1085,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "templateHash": "3051764932488625981" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1193,7 +1193,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "templateHash": "4693794629197446458" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1254,7 +1254,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1313,7 +1313,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { @@ -1428,7 +1429,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7737694719083989185" + "templateHash": "16116103805544296619" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -1526,7 +1527,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "15106079368020532347" + "templateHash": "18220013070549790672" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1580,7 +1581,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1639,7 +1640,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { @@ -1909,7 +1911,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "2125607583208873548" + "templateHash": "2154407021129517742" } }, "parameters": { @@ -1944,6 +1946,9 @@ "tags": "[parameters('tags')]", "sku": { "name": "Standard" + }, + "properties": { + "disableLocalAuth": true } }, { diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index a1e89c59..8dc763de 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "2114239967834469967" + "templateHash": "16034083465139663619" } }, "parameters": { @@ -1106,7 +1106,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "18381885242494360931" + "templateHash": "10664478597037280079" } }, "parameters": { @@ -1187,7 +1187,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "16622766300017925980" + "templateHash": "11639574980696241038" } }, "parameters": { @@ -1294,7 +1294,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "17196584048072227928" + "templateHash": "15854138267854445037" } }, "parameters": { @@ -1354,7 +1354,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7688648017235778909" + "templateHash": "15842445230570505343" } }, "parameters": { @@ -1412,7 +1412,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { diff --git a/Environments/App-Base-WebApp-ACA/azuredeploy.json b/Environments/App-Base-WebApp-ACA/azuredeploy.json index 9cbe6845..c384657c 100644 --- a/Environments/App-Base-WebApp-ACA/azuredeploy.json +++ b/Environments/App-Base-WebApp-ACA/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "14812900086505163857" + "templateHash": "6959012947984384186" } }, "parameters": { @@ -2266,7 +2266,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "9506725499169300305" + "templateHash": "11074299330608515845" } }, "parameters": { @@ -2347,7 +2347,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "templateHash": "3051764932488625981" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2455,7 +2455,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "templateHash": "4693794629197446458" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2516,7 +2516,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2575,7 +2575,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json index f6454f39..bb92ee14 100644 --- a/Environments/App-Base-WebApp-AKS/azuredeploy.json +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "8340638631147543749" + "templateHash": "3554149725070251677" } }, "parameters": { @@ -220,7 +220,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "9506725499169300305" + "templateHash": "11074299330608515845" } }, "parameters": { @@ -301,7 +301,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "templateHash": "3051764932488625981" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -409,7 +409,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "templateHash": "4693794629197446458" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -470,7 +470,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -529,7 +529,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index 659c655f..fad37734 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7670045821900577611" + "templateHash": "2826290446221151175" } }, "parameters": { @@ -2115,7 +2115,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "9506725499169300305" + "templateHash": "11074299330608515845" } }, "parameters": { @@ -2196,7 +2196,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "templateHash": "3051764932488625981" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2304,7 +2304,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "templateHash": "4693794629197446458" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2365,7 +2365,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2424,7 +2424,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index 39663ee3..ac54c969 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "11835380275388562123" + "templateHash": "4758816057439609930" } }, "parameters": { @@ -282,7 +282,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "9506725499169300305" + "templateHash": "11074299330608515845" } }, "parameters": { @@ -363,7 +363,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "templateHash": "3051764932488625981" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -471,7 +471,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "templateHash": "4693794629197446458" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -532,7 +532,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -591,7 +591,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { diff --git a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json index 9cbe6845..c384657c 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "14812900086505163857" + "templateHash": "6959012947984384186" } }, "parameters": { @@ -2266,7 +2266,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "9506725499169300305" + "templateHash": "11074299330608515845" } }, "parameters": { @@ -2347,7 +2347,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "templateHash": "3051764932488625981" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2455,7 +2455,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "templateHash": "4693794629197446458" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2516,7 +2516,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2575,7 +2575,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json index 88e2fce1..f83b8d53 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "18446031272399398207" + "templateHash": "7077090465907669075" } }, "parameters": { @@ -1342,7 +1342,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "9506725499169300305" + "templateHash": "11074299330608515845" } }, "parameters": { @@ -1423,7 +1423,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "templateHash": "3051764932488625981" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1531,7 +1531,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "templateHash": "4693794629197446458" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1592,7 +1592,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "templateHash": "10034279666465122994" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1651,7 +1651,8 @@ { "name": "EnableServerless" } - ] + ], + "disableLocalAuth": true } }, { From 1a47765c8c6d103c23cae5576ba0b5c6d929d046 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Mon, 22 Apr 2024 21:29:40 +0800 Subject: [PATCH 091/112] update app config name --- Environments/Todo-Mongo-AKS/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index 3dd31c7a..c29dada3 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -54,7 +54,7 @@ param contentType string = '' resource configStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' = { - name: !empty(configStoreName) ? configStoreName : '${abbrs.appConfigurationConfigurationStores}-${resourceToken}' + name: !empty(configStoreName) ? configStoreName : '${abbrs.appConfigurationConfigurationStores}${resourceToken}' location: location sku: { name: 'standard' From f3fa9aacb19e0fb55fc4fa844905edf9c01d8e3e Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Mon, 22 Apr 2024 13:32:10 +0000 Subject: [PATCH 092/112] Rebuild ARM templates --- Environments/Todo-Mongo-AKS/azuredeploy.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index ac54c969..fb7cf37c 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "4758816057439609930" + "templateHash": "8189193153050722730" } }, "parameters": { @@ -203,7 +203,7 @@ { "type": "Microsoft.AppConfiguration/configurationStores", "apiVersion": "2021-10-01-preview", - "name": "[if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken')))]", + "name": "[if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken')))]", "location": "[parameters('location')]", "sku": { "name": "standard" @@ -212,42 +212,42 @@ { "type": "Microsoft.AppConfiguration/configurationStores/keyValues", "apiVersion": "2021-10-01-preview", - "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_CONNECTION_STRING_KEY')]", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_CONNECTION_STRING_KEY')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]", "contentType": "[parameters('contentType')]", "tags": "[variables('tags')]" }, "dependsOn": [ - "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" ] }, { "type": "Microsoft.AppConfiguration/configurationStores/keyValues", "apiVersion": "2021-10-01-preview", - "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_DATABASE_NAME')]", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_DATABASE_NAME')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]", "contentType": "[parameters('contentType')]", "tags": "[variables('tags')]" }, "dependsOn": [ - "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" ] }, { "type": "Microsoft.AppConfiguration/configurationStores/keyValues", "apiVersion": "2021-10-01-preview", - "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_KEY_VAULT_ENDPOINT')]", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_KEY_VAULT_ENDPOINT')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]", "contentType": "[parameters('contentType')]", "tags": "[variables('tags')]" }, "dependsOn": [ - "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}-{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" ] }, From 295846e5d6cb0b417888a204edb4f959e9b49665 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Tue, 23 Apr 2024 16:12:04 +0800 Subject: [PATCH 093/112] use sql instead of mongo because mongo does not support disable localauth --- Environments/Todo-Mongo-AKS/app/db.bicep | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/app/db.bicep b/Environments/Todo-Mongo-AKS/app/db.bicep index 938949c6..331f22f6 100644 --- a/Environments/Todo-Mongo-AKS/app/db.bicep +++ b/Environments/Todo-Mongo-AKS/app/db.bicep @@ -2,39 +2,42 @@ param accountName string param location string = resourceGroup().location param tags object = {} -param collections array = [ +param containers array = [ { name: 'TodoList' id: 'TodoList' - shardKey: 'Hash' - indexKey: '_id' + partitionKey: '/id' } { name: 'TodoItem' id: 'TodoItem' - shardKey: 'Hash' - indexKey: '_id' + partitionKey: '/id' } ] + param databaseName string = '' param keyVaultName string +param principalIds array = [] // Because databaseName is optional in main.bicep, we make sure the database name is set here. var defaultDatabaseName = 'Todo' var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName -module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { - name: 'cosmos-mongo' +module cosmos '../core/database/cosmos/sql/cosmos-sql-db.bicep' = { + name: 'cosmos-sql' params: { accountName: accountName - databaseName: actualDatabaseName location: location - collections: collections - keyVaultName: keyVaultName tags: tags + containers: containers + databaseName: actualDatabaseName + keyVaultName: keyVaultName + principalIds: principalIds } } +output accountName string = cosmos.outputs.accountName output connectionStringKey string = cosmos.outputs.connectionStringKey output databaseName string = cosmos.outputs.databaseName output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = cosmos.outputs.roleDefinitionId From b0a6d5b835f8f8a8a6d321ce5c5fa935f44a3a6b Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Tue, 23 Apr 2024 16:12:40 +0800 Subject: [PATCH 094/112] use shared aks project name and environment name instead of identity profile object id --- .../Todo-Mongo-AKS/get-aks-info.bicep | 5 ++ .../Todo-Mongo-AKS/get-shared-aks-name.bicep | 62 ++++++++++++++++ Environments/Todo-Mongo-AKS/main.bicep | 70 +++++++++++++++---- Environments/Todo-Mongo-AKS/manifest.yaml | 12 +++- 4 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 Environments/Todo-Mongo-AKS/get-aks-info.bicep create mode 100644 Environments/Todo-Mongo-AKS/get-shared-aks-name.bicep diff --git a/Environments/Todo-Mongo-AKS/get-aks-info.bicep b/Environments/Todo-Mongo-AKS/get-aks-info.bicep new file mode 100644 index 00000000..9cd133a4 --- /dev/null +++ b/Environments/Todo-Mongo-AKS/get-aks-info.bicep @@ -0,0 +1,5 @@ +param aksName string +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: aksName +} +output aksIdentityObjectId string = aks.properties.identityProfile.kubeletidentity.objectId diff --git a/Environments/Todo-Mongo-AKS/get-shared-aks-name.bicep b/Environments/Todo-Mongo-AKS/get-shared-aks-name.bicep new file mode 100644 index 00000000..9bbd7aa0 --- /dev/null +++ b/Environments/Todo-Mongo-AKS/get-shared-aks-name.bicep @@ -0,0 +1,62 @@ +@description('app deployment name') +param appDeployName string + +@description('Shared AKS resource group') +param aksResourceGroupName string + +@description('Timestamp - utcNow can only be called as a default value of a parameter.') +param timestamp string = utcNow() + +@description('The location to run the deployment script in') +param location string = resourceGroup().location + +param identityName string + +var scriptToExecute = ''' +$output = Get-AzResource -ResourceGroupName $Env:RESOURCEGROUP -ResourceType Microsoft.ContainerService/ManagedClusters + +Write-Output $output +$DeploymentScriptOutputs = @{} +$DeploymentScriptOutputs['text'] = $output.Name +''' + +resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module roleAssignemnt './core/security/role.bicep' = { + name: 'read-role-assignment-to-aks' + scope: resourceGroup(aksResourceGroupName) + params: { + principalId: identity.properties.principalId + roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + } +} + +resource script 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + kind: 'AzurePowerShell' + name: '${appDeployName}-get-aks-script' + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${identity.id}' : {} + } + } + properties: { + forceUpdateTag: timestamp + azPowerShellVersion: '7.2.0' + retentionInterval: 'PT1H' + scriptContent: scriptToExecute + cleanupPreference: 'Always' + environmentVariables: [ + { + name: 'RESOURCEGROUP' + value: aksResourceGroupName + } + ] + } +} + +output clusterName string = empty(script.properties.outputs.text) ? '' : script.properties.outputs.text diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index c29dada3..df2a90dc 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -11,25 +11,15 @@ param cosmosAccountName string = '' param cosmosDatabaseName string = '' param keyVaultName string = '' param principalId string = '' -param aksClusterIdentityObjectId string param configStoreName string = '' +param sharedAKSProjectName string +param sharedAKSEnvironmentName string +var sharedAKSResourceGroup = '${sharedAKSProjectName}-${sharedAKSEnvironmentName}' var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } -// The application database -module cosmos './app/db.bicep' = { - name: 'cosmos' - params: { - accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' - databaseName: cosmosDatabaseName - location: location - tags: tags - keyVaultName: keyVault.outputs.name - } -} - // Store secrets in a keyvault module keyVault './core/security/keyvault.bicep' = { name: 'keyvault' @@ -41,14 +31,66 @@ module keyVault './core/security/keyvault.bicep' = { } } + +module aksName 'get-shared-aks-name.bicep' = { + name: 'get-aks-name' + params: { + appDeployName: 'todo-deploy' + aksResourceGroupName: sharedAKSResourceGroup + identityName : '${abbrs.managedIdentityUserAssignedIdentities}dp-${resourceToken}' + location: location + } +} + +module aks 'get-aks-info.bicep' = { + name: 'aks' + scope: resourceGroup(sharedAKSResourceGroup) + params: { + aksName: aksName.outputs.clusterName + } +} + module clusterKeyVaultAccess './core/security/keyvault-access.bicep' = { name: 'cluster-keyvault-access' params: { keyVaultName: keyVault.outputs.name - principalId: aksClusterIdentityObjectId + principalId: aks.outputs.aksIdentityObjectId } } +// Give the API the role to access Cosmos +module apiCosmosSqlRoleAssign './core/database/cosmos/sql/cosmos-sql-role-assign.bicep' = { + name: 'api-cosmos-access' + params: { + accountName: cosmos.outputs.accountName + roleDefinitionId: cosmos.outputs.roleDefinitionId + principalId: aks.outputs.aksIdentityObjectId + } +} + +// Give the API the role to access Cosmos +module userComsosSqlRoleAssign './core/database/cosmos/sql/cosmos-sql-role-assign.bicep' = if (principalId != '') { + name: 'user-cosmos-access' + params: { + accountName: cosmos.outputs.accountName + roleDefinitionId: cosmos.outputs.roleDefinitionId + principalId: principalId + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + + @description('Specifies the content type of the key-value resources. For feature flag, the value should be application/vnd.microsoft.appconfig.ff+json;charset=utf-8. For Key Value reference, the value should be application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8. Otherwise, it\'s optional.') param contentType string = '' diff --git a/Environments/Todo-Mongo-AKS/manifest.yaml b/Environments/Todo-Mongo-AKS/manifest.yaml index 06485057..dd2945e4 100644 --- a/Environments/Todo-Mongo-AKS/manifest.yaml +++ b/Environments/Todo-Mongo-AKS/manifest.yaml @@ -18,8 +18,14 @@ parameters: type: string required: true -- id: "aksClusterIdentityObjectId" - name: "AKS Cluster Identity Object Id" - description: "Object Id of the identity used by the AKS cluster to access the KeyVault" +- id: "sharedAKSProjectName" + name: "sharedAKSProjectName" + description: "ADE Project name for the shared AKS cluster" + type: string + required: true + +- id: "sharedAKSEnvironmentName" + name: "sharedAKSEnvironmentName" + description: "ADE environment name for the shared AKS cluster" type: string required: true From 720f0d60528e99f9a88f0731f1dd012a08f5c60c Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Tue, 23 Apr 2024 08:14:59 +0000 Subject: [PATCH 095/112] Rebuild ARM templates --- Environments/Todo-Mongo-AKS/azuredeploy.json | 1212 +++++++++++++----- 1 file changed, 856 insertions(+), 356 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index fb7cf37c..03f17410 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "8189193153050722730" + "templateHash": "926438374472775395" } }, "parameters": { @@ -41,13 +41,16 @@ "type": "string", "defaultValue": "" }, - "aksClusterIdentityObjectId": { - "type": "string" - }, "configStoreName": { "type": "string", "defaultValue": "" }, + "sharedAKSProjectName": { + "type": "string" + }, + "sharedAKSEnvironmentName": { + "type": "string" + }, "contentType": { "type": "string", "defaultValue": "", @@ -193,6 +196,7 @@ "webSitesFunctions": "func-", "webStaticSites": "stapp-" }, + "sharedAKSResourceGroup": "[format('{0}-{1}', parameters('sharedAKSProjectName'), parameters('sharedAKSEnvironmentName'))]", "abbrs": "[variables('$fxv#0')]", "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", "tags": { @@ -254,25 +258,22 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "cosmos", + "name": "keyvault", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", - "databaseName": { - "value": "[parameters('cosmosDatabaseName')]" - }, + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", "location": { "value": "[parameters('location')]" }, "tags": { "value": "[variables('tags')]" }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + "principalId": { + "value": "[parameters('principalId')]" } }, "template": { @@ -282,11 +283,12 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "11074299330608515845" - } + "templateHash": "18407114162280426775" + }, + "description": "Creates an Azure Key Vault." }, "parameters": { - "accountName": { + "name": { "type": "string" }, "location": { @@ -297,63 +299,160 @@ "type": "object", "defaultValue": {} }, - "collections": { - "type": "array", - "defaultValue": [ - { - "name": "TodoList", - "id": "TodoList", - "shardKey": "Hash", - "indexKey": "_id" + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" }, - { - "name": "TodoItem", - "id": "TodoItem", - "shardKey": "Hash", - "indexKey": "_id" - } - ] + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" }, - "databaseName": { + "name": { "type": "string", - "defaultValue": "" + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "get-aks-name", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appDeployName": { + "value": "todo-deploy" + }, + "aksResourceGroupName": { + "value": "[variables('sharedAKSResourceGroup')]" + }, + "identityName": { + "value": "[format('{0}dp-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15711762243647057476" + } + }, + "parameters": { + "appDeployName": { + "type": "string", + "metadata": { + "description": "app deployment name" + } }, - "keyVaultName": { + "aksResourceGroupName": { + "type": "string", + "metadata": { + "description": "Shared AKS resource group" + } + }, + "timestamp": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "Timestamp - utcNow can only be called as a default value of a parameter." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location to run the deployment script in" + } + }, + "identityName": { "type": "string" } }, "variables": { - "defaultDatabaseName": "Todo", - "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + "scriptToExecute": "$output = Get-AzResource -ResourceGroupName $Env:RESOURCEGROUP -ResourceType Microsoft.ContainerService/ManagedClusters\n\nWrite-Output $output\n$DeploymentScriptOutputs = @{}\n$DeploymentScriptOutputs['text'] = $output.Name\n" }, "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('{0}-get-aks-script', parameters('appDeployName'))]", + "kind": "AzurePowerShell", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))]": {} + } + }, + "properties": { + "forceUpdateTag": "[parameters('timestamp')]", + "azPowerShellVersion": "7.2.0", + "retentionInterval": "PT1H", + "scriptContent": "[variables('scriptToExecute')]", + "cleanupPreference": "Always", + "environmentVariables": [ + { + "name": "RESOURCEGROUP", + "value": "[parameters('aksResourceGroupName')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "cosmos-mongo", + "name": "read-role-assignment-to-aks", + "resourceGroup": "[parameters('aksResourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "accountName": { - "value": "[parameters('accountName')]" - }, - "databaseName": { - "value": "[variables('actualDatabaseName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "collections": { - "value": "[parameters('collections')]" + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "tags": { - "value": "[parameters('tags')]" + "roleDefinitionId": { + "value": "acdd72a7-3385-48ef-bd42-f606fba81ae7" } }, "template": { @@ -363,171 +462,561 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "templateHash": "2390256577307700589" }, - "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + "description": "Creates a role assignment for a service principal." }, "parameters": { - "accountName": { - "type": "string" - }, - "databaseName": { + "principalId": { "type": "string" }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "collections": { - "type": "array", - "defaultValue": [] - }, - "connectionStringKey": { + "principalType": { "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] }, - "keyVaultName": { + "roleDefinitionId": { "type": "string" } }, "resources": [ { - "copy": { - "name": "list", - "count": "[length(parameters('collections'))]" - }, - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", - "properties": { - "resource": { - "id": "[parameters('collections')[copyIndex()].id]", - "shardKey": { - "_id": "[parameters('collections')[copyIndex()].shardKey]" - }, - "indexes": [ - { - "key": { - "keys": [ - "[parameters('collections')[copyIndex()].indexKey]" - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" - ] - }, - { - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", - "apiVersion": "2022-08-15", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": { - "id": "[parameters('databaseName')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-mongo-account", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('accountName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4693794629197446458" - }, - "description": "Creates an Azure Cosmos DB for MongoDB account." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "keyVaultName": { - "type": "string" - }, - "connectionStringKey": { - "type": "string", - "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cosmos-account", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "connectionStringKey": { - "value": "[parameters('connectionStringKey')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "kind": { - "value": "MongoDB" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "value": "[if(empty(reference(resourceId('Microsoft.Resources/deploymentScripts', format('{0}-get-aks-script', parameters('appDeployName'))), '2020-10-01').outputs.text), '', reference(resourceId('Microsoft.Resources/deploymentScripts', format('{0}-get-aks-script', parameters('appDeployName'))), '2020-10-01').outputs.text)]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "resourceGroup": "[variables('sharedAKSResourceGroup')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "aksName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'get-aks-name'), '2022-09-01').outputs.clusterName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "889274472255022532" + } + }, + "parameters": { + "aksName": { + "type": "string" + } + }, + "resources": [], + "outputs": { + "aksIdentityObjectId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('aksName')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'get-aks-name')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('sharedAKSResourceGroup')), 'Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.aksIdentityObjectId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('sharedAKSResourceGroup')), 'Microsoft.Resources/deployments', 'aks')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "api-cosmos-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.accountName.value]" + }, + "roleDefinitionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.roleDefinitionId.value]" + }, + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('sharedAKSResourceGroup')), 'Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.aksIdentityObjectId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "5580476706925703677" + }, + "description": "Creates a SQL role assignment under an Azure Cosmos DB account." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('accountName'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))]" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('sharedAKSResourceGroup')), 'Microsoft.Resources/deployments', 'aks')]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" + ] + }, + { + "condition": "[not(equals(parameters('principalId'), ''))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "user-cosmos-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.accountName.value]" + }, + "roleDefinitionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.roleDefinitionId.value]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "5580476706925703677" + }, + "description": "Creates a SQL role assignment under an Azure Cosmos DB account." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('accountName'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "4256008595520895847" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containers": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "partitionKey": "/id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "partitionKey": "/id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + }, + "principalIds": { + "type": "array", + "defaultValue": [] + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-sql", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "containers": { + "value": "[parameters('containers')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "principalIds": { + "value": "[parameters('principalIds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "16116103805544296619" + }, + "description": "Creates an Azure Cosmos DB for NoSQL account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "containers": { + "type": "array", + "defaultValue": [] + }, + "keyVaultName": { + "type": "string" + }, + "principalIds": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('containers'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('containers')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('containers')[copyIndex()].id]", + "partitionKey": { + "paths": [ + "[parameters('containers')[copyIndex()].partitionKey]" + ] + } + }, + "options": {} + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-sql-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18220013070549790672" + }, + "description": "Creates an Azure Cosmos DB for NoSQL account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "GlobalDocumentDB" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", @@ -641,16 +1130,165 @@ "id": { "type": "string", "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.name.value]" } } } } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-sql-role-definition", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "16206905209322787989" + }, + "description": "Creates a SQL role definition under an Azure Cosmos DB account." + }, + "parameters": { + "accountName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName')), parameters('accountName'), 'sql-role'))]", + "properties": { + "assignableScopes": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))]" + ], + "permissions": [ + { + "dataActions": [ + "Microsoft.DocumentDB/databaseAccounts/readMetadata", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*" + ], + "notDataActions": [] + } + ], + "roleName": "Reader Writer", + "type": "CustomRole" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('accountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName')), parameters('accountName'), 'sql-role'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "copy": { + "name": "userRole", + "count": "[length(parameters('principalIds'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[not(empty(parameters('principalIds')[copyIndex()]))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('cosmos-sql-user-role-{0}', uniqueString(parameters('principalIds')[copyIndex()]))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "roleDefinitionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-role-definition'), '2022-09-01').outputs.id.value]" + }, + "principalId": { + "value": "[parameters('principalIds')[copyIndex()]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "5580476706925703677" + }, + "description": "Creates a SQL role assignment under an Azure Cosmos DB account." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('accountName'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos-sql-role-definition')]" + ] } ], "outputs": { + "accountId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.id.value]" + }, + "accountName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.name.value]" + }, "connectionStringKey": { "type": "string", - "value": "[parameters('connectionStringKey')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.connectionStringKey.value]" }, "databaseName": { "type": "string", @@ -658,7 +1296,11 @@ }, "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-account'), '2022-09-01').outputs.endpoint.value]" + }, + "roleDefinitionId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql-role-definition'), '2022-09-01').outputs.id.value]" } } } @@ -666,170 +1308,28 @@ } ], "outputs": { - "connectionStringKey": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" - }, - "databaseName": { + "accountName": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql'), '2022-09-01').outputs.accountName.value]" }, - "endpoint": { + "connectionStringKey": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "keyvault", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "principalId": { - "value": "[parameters('principalId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" - }, - "description": "Creates an Azure Key Vault." - }, - "parameters": { - "name": { - "type": "string" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql'), '2022-09-01').outputs.connectionStringKey.value]" }, - "location": { + "databaseName": { "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql'), '2022-09-01').outputs.databaseName.value]" }, - "principalId": { - "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" - } - } - ], - "outputs": { "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql'), '2022-09-01').outputs.endpoint.value]" }, - "name": { + "roleDefinitionId": { "type": "string", - "value": "[parameters('name')]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-sql'), '2022-09-01').outputs.roleDefinitionId.value]" } } } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cluster-keyvault-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, - "principalId": { - "value": "[parameters('aksClusterIdentityObjectId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" - }, - "description": "Assigns an Azure Key Vault access policy." - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "add" - }, - "keyVaultName": { - "type": "string" - }, - "permissions": { - "type": "object", - "defaultValue": { - "secrets": [ - "get", - "list" - ] - } - }, - "principalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "properties": { - "accessPolicies": [ - { - "objectId": "[parameters('principalId')]", - "tenantId": "[subscription().tenantId]", - "permissions": "[parameters('permissions')]" - } - ] - } - } - ] - } }, "dependsOn": [ "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" From 3f9480379417038bb62f309b8498d9557373dd05 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 26 Apr 2024 10:43:26 +0800 Subject: [PATCH 096/112] add back local auth for cosmosdb --- .../Todo-Mongo-AKS-LA/abbreviations.json | 136 +++ Environments/Todo-Mongo-AKS-LA/app/db.bicep | 40 + .../Todo-Mongo-AKS-LA/azuredeploy.json | 865 ++++++++++++++++++ .../core/database/cosmos/cosmos-account.bicep | 49 + .../cosmos/mongo/cosmos-mongo-account.bicep | 23 + .../cosmos/mongo/cosmos-mongo-db.bicep | 47 + .../cosmos/sql/cosmos-sql-account.bicep | 22 + .../database/cosmos/sql/cosmos-sql-db.bicep | 74 ++ .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 + .../cosmos/sql/cosmos-sql-role-def.bicep | 30 + .../security/aks-managed-cluster-access.bicep | 19 + .../core/security/keyvault-access.bicep | 22 + .../core/security/keyvault-secret.bicep | 31 + .../core/security/keyvault.bicep | 26 + .../core/security/registry-access.bicep | 19 + .../core/security/role.bicep | 21 + .../Todo-Mongo-AKS-LA/get-aks-info.bicep | 5 + .../get-shared-aks-name.bicep | 62 ++ Environments/Todo-Mongo-AKS-LA/main.bicep | 122 +++ .../Todo-Mongo-AKS-LA/main.parameters.json | 15 + Environments/Todo-Mongo-AKS-LA/manifest.yaml | 31 + 21 files changed, 1678 insertions(+) create mode 100644 Environments/Todo-Mongo-AKS-LA/abbreviations.json create mode 100644 Environments/Todo-Mongo-AKS-LA/app/db.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/azuredeploy.json create mode 100644 Environments/Todo-Mongo-AKS-LA/core/database/cosmos/cosmos-account.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/security/aks-managed-cluster-access.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/security/keyvault-access.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/security/keyvault-secret.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/security/keyvault.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/security/registry-access.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/core/security/role.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/get-aks-info.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/get-shared-aks-name.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/main.bicep create mode 100644 Environments/Todo-Mongo-AKS-LA/main.parameters.json create mode 100644 Environments/Todo-Mongo-AKS-LA/manifest.yaml diff --git a/Environments/Todo-Mongo-AKS-LA/abbreviations.json b/Environments/Todo-Mongo-AKS-LA/abbreviations.json new file mode 100644 index 00000000..289a08aa --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/Environments/Todo-Mongo-AKS-LA/app/db.bicep b/Environments/Todo-Mongo-AKS-LA/app/db.bicep new file mode 100644 index 00000000..938949c6 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/app/db.bicep @@ -0,0 +1,40 @@ +param accountName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [ + { + name: 'TodoList' + id: 'TodoList' + shardKey: 'Hash' + indexKey: '_id' + } + { + name: 'TodoItem' + id: 'TodoItem' + shardKey: 'Hash' + indexKey: '_id' + } +] +param databaseName string = '' +param keyVaultName string + +// Because databaseName is optional in main.bicep, we make sure the database name is set here. +var defaultDatabaseName = 'Todo' +var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName + +module cosmos '../core/database/cosmos/mongo/cosmos-mongo-db.bicep' = { + name: 'cosmos-mongo' + params: { + accountName: accountName + databaseName: actualDatabaseName + location: location + collections: collections + keyVaultName: keyVaultName + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = cosmos.outputs.databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json new file mode 100644 index 00000000..fb7cf37c --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json @@ -0,0 +1,865 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "8189193153050722730" + } + }, + "parameters": { + "environmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the the environment which is used to generate a short unique hash used in all resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "minLength": 1, + "metadata": { + "description": "Primary location for all resources" + } + }, + "cosmosAccountName": { + "type": "string", + "defaultValue": "" + }, + "cosmosDatabaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string", + "defaultValue": "" + }, + "principalId": { + "type": "string", + "defaultValue": "" + }, + "aksClusterIdentityObjectId": { + "type": "string" + }, + "configStoreName": { + "type": "string", + "defaultValue": "" + }, + "contentType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Specifies the content type of the key-value resources. For feature flag, the value should be application/vnd.microsoft.appconfig.ff+json;charset=utf-8. For Key Value reference, the value should be application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8. Otherwise, it's optional." + } + } + }, + "variables": { + "$fxv#0": { + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "loadTesting": "lt-", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + }, + "abbrs": "[variables('$fxv#0')]", + "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", + "tags": { + "azd-env-name": "[parameters('environmentName')]" + } + }, + "resources": [ + { + "type": "Microsoft.AppConfiguration/configurationStores", + "apiVersion": "2021-10-01-preview", + "name": "[if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken')))]", + "location": "[parameters('location')]", + "sku": { + "name": "standard" + } + }, + { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2021-10-01-preview", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_CONNECTION_STRING_KEY')]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]", + "contentType": "[parameters('contentType')]", + "tags": "[variables('tags')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" + ] + }, + { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2021-10-01-preview", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_DATABASE_NAME')]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]", + "contentType": "[parameters('contentType')]", + "tags": "[variables('tags')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.Resources/deployments', 'cosmos')]" + ] + }, + { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2021-10-01-preview", + "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_KEY_VAULT_ENDPOINT')]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]", + "contentType": "[parameters('contentType')]", + "tags": "[variables('tags')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": "[if(not(empty(parameters('cosmosAccountName'))), createObject('value', parameters('cosmosAccountName')), createObject('value', format('{0}{1}', variables('abbrs').documentDBDatabaseAccounts, variables('resourceToken'))))]", + "databaseName": { + "value": "[parameters('cosmosDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "11074299330608515845" + } + }, + "parameters": { + "accountName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [ + { + "name": "TodoList", + "id": "TodoList", + "shardKey": "Hash", + "indexKey": "_id" + }, + { + "name": "TodoItem", + "id": "TodoItem", + "shardKey": "Hash", + "indexKey": "_id" + } + ] + }, + "databaseName": { + "type": "string", + "defaultValue": "" + }, + "keyVaultName": { + "type": "string" + } + }, + "variables": { + "defaultDatabaseName": "Todo", + "actualDatabaseName": "[if(not(empty(parameters('databaseName'))), parameters('databaseName'), variables('defaultDatabaseName'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "accountName": { + "value": "[parameters('accountName')]" + }, + "databaseName": { + "value": "[variables('actualDatabaseName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "collections": { + "value": "[parameters('collections')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "3051764932488625981" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account with a database." + }, + "parameters": { + "accountName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "collections": { + "type": "array", + "defaultValue": [] + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "list", + "count": "[length(parameters('collections'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}/{2}', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1], parameters('collections')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[parameters('collections')[copyIndex()].id]", + "shardKey": { + "_id": "[parameters('collections')[copyIndex()].shardKey]" + }, + "indexes": [ + { + "key": { + "keys": [ + "[parameters('collections')[copyIndex()].indexKey]" + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[0], split(format('{0}/{1}', parameters('accountName'), parameters('databaseName')), '/')[1])]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2022-08-15", + "name": "[format('{0}/{1}', parameters('accountName'), parameters('databaseName'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('databaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-mongo-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('accountName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "4693794629197446458" + }, + "description": "Creates an Azure Cosmos DB for MongoDB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "keyVaultName": { + "type": "string" + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cosmos-account", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "connectionStringKey": { + "value": "[parameters('connectionStringKey')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "kind": { + "value": "MongoDB" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "10034279666465122994" + }, + "description": "Creates an Azure Cosmos DB account." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "connectionStringKey": { + "type": "string", + "defaultValue": "AZURE-COSMOS-CONNECTION-STRING" + }, + "keyVaultName": { + "type": "string" + }, + "kind": { + "type": "string", + "allowedValues": [ + "GlobalDocumentDB", + "MongoDB", + "Parse" + ] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2022-08-15", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard", + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "apiProperties": "[if(equals(parameters('kind'), 'MongoDB'), createObject('serverVersion', '4.2'), createObject())]", + "capabilities": [ + { + "name": "EnableServerless" + } + ], + "disableLocalAuth": true + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('connectionStringKey'))]", + "properties": { + "value": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').connectionStrings[0].connectionString]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2022-08-15').documentEndpoint]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.endpoint.value]" + }, + "id": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-account'), '2022-09-01').outputs.id.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[parameters('connectionStringKey')]" + }, + "databaseName": { + "type": "string", + "value": "[parameters('databaseName')]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo-account'), '2022-09-01').outputs.endpoint.value]" + } + } + } + } + } + ], + "outputs": { + "connectionStringKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "databaseName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.databaseName.value]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos-mongo'), '2022-09-01').outputs.endpoint.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[parameters('aksClusterIdentityObjectId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + } + ], + "outputs": { + "AZURE_COSMOS_CONNECTION_STRING_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]" + }, + "AZURE_COSMOS_DATABASE_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]" + }, + "AZURE_LOCATION": { + "type": "string", + "value": "[parameters('location')]" + }, + "AZURE_TENANT_ID": { + "type": "string", + "value": "[tenant().tenantId]" + }, + "AZURE_KEY_VAULT_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]" + }, + "AZURE_KEY_VAULT_NAME": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/cosmos-account.bicep b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 00000000..6f8747f5 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 00000000..4aafbf38 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 00000000..2a670578 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-account.bicep b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 00000000..8431135e --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-db.bicep b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 00000000..265880dc --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 00000000..3949efef --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 00000000..778d6dc4 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/Environments/Todo-Mongo-AKS-LA/core/security/aks-managed-cluster-access.bicep b/Environments/Todo-Mongo-AKS-LA/core/security/aks-managed-cluster-access.bicep new file mode 100644 index 00000000..dec984e8 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/security/aks-managed-cluster-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' +param clusterName string +param principalId string + +var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + +resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aksCluster // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) + properties: { + roleDefinitionId: aksClusterAdminRole + principalType: 'User' + principalId: principalId + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: clusterName +} diff --git a/Environments/Todo-Mongo-AKS-LA/core/security/keyvault-access.bicep b/Environments/Todo-Mongo-AKS-LA/core/security/keyvault-access.bicep new file mode 100644 index 00000000..316775f2 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Mongo-AKS-LA/core/security/keyvault-secret.bicep b/Environments/Todo-Mongo-AKS-LA/core/security/keyvault-secret.bicep new file mode 100644 index 00000000..7441b296 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/Environments/Todo-Mongo-AKS-LA/core/security/keyvault.bicep b/Environments/Todo-Mongo-AKS-LA/core/security/keyvault.bicep new file mode 100644 index 00000000..314a1db6 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/Environments/Todo-Mongo-AKS-LA/core/security/registry-access.bicep b/Environments/Todo-Mongo-AKS-LA/core/security/registry-access.bicep new file mode 100644 index 00000000..5335efab --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/Environments/Todo-Mongo-AKS-LA/core/security/role.bicep b/Environments/Todo-Mongo-AKS-LA/core/security/role.bicep new file mode 100644 index 00000000..0b30cfd3 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/Environments/Todo-Mongo-AKS-LA/get-aks-info.bicep b/Environments/Todo-Mongo-AKS-LA/get-aks-info.bicep new file mode 100644 index 00000000..9cd133a4 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/get-aks-info.bicep @@ -0,0 +1,5 @@ +param aksName string +resource aks 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { + name: aksName +} +output aksIdentityObjectId string = aks.properties.identityProfile.kubeletidentity.objectId diff --git a/Environments/Todo-Mongo-AKS-LA/get-shared-aks-name.bicep b/Environments/Todo-Mongo-AKS-LA/get-shared-aks-name.bicep new file mode 100644 index 00000000..9bbd7aa0 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/get-shared-aks-name.bicep @@ -0,0 +1,62 @@ +@description('app deployment name') +param appDeployName string + +@description('Shared AKS resource group') +param aksResourceGroupName string + +@description('Timestamp - utcNow can only be called as a default value of a parameter.') +param timestamp string = utcNow() + +@description('The location to run the deployment script in') +param location string = resourceGroup().location + +param identityName string + +var scriptToExecute = ''' +$output = Get-AzResource -ResourceGroupName $Env:RESOURCEGROUP -ResourceType Microsoft.ContainerService/ManagedClusters + +Write-Output $output +$DeploymentScriptOutputs = @{} +$DeploymentScriptOutputs['text'] = $output.Name +''' + +resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: identityName + location: location +} + +module roleAssignemnt './core/security/role.bicep' = { + name: 'read-role-assignment-to-aks' + scope: resourceGroup(aksResourceGroupName) + params: { + principalId: identity.properties.principalId + roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + } +} + +resource script 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + kind: 'AzurePowerShell' + name: '${appDeployName}-get-aks-script' + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${identity.id}' : {} + } + } + properties: { + forceUpdateTag: timestamp + azPowerShellVersion: '7.2.0' + retentionInterval: 'PT1H' + scriptContent: scriptToExecute + cleanupPreference: 'Always' + environmentVariables: [ + { + name: 'RESOURCEGROUP' + value: aksResourceGroupName + } + ] + } +} + +output clusterName string = empty(script.properties.outputs.text) ? '' : script.properties.outputs.text diff --git a/Environments/Todo-Mongo-AKS-LA/main.bicep b/Environments/Todo-Mongo-AKS-LA/main.bicep new file mode 100644 index 00000000..a2d1c00d --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/main.bicep @@ -0,0 +1,122 @@ +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string = resourceGroup().location + +param cosmosAccountName string = '' +param cosmosDatabaseName string = '' +param keyVaultName string = '' +param principalId string = '' +param configStoreName string = '' +param sharedAKSProjectName string +param sharedAKSEnvironmentName string +var sharedAKSResourceGroup = '${sharedAKSProjectName}-${sharedAKSEnvironmentName}' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +module aksName 'get-shared-aks-name.bicep' = { + name: 'get-aks-name' + params: { + appDeployName: 'todo-deploy' + aksResourceGroupName: sharedAKSResourceGroup + identityName : '${abbrs.managedIdentityUserAssignedIdentities}dp-${resourceToken}' + location: location + } +} + +module aks 'get-aks-info.bicep' = { + name: 'aks' + scope: resourceGroup(sharedAKSResourceGroup) + params: { + aksName: aksName.outputs.clusterName + } +} + +module clusterKeyVaultAccess './core/security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVault.outputs.name + principalId: aks.outputs.aksIdentityObjectId + } +} + +// The application database +module cosmos './app/db.bicep' = { + name: 'cosmos' + params: { + accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + databaseName: cosmosDatabaseName + location: location + tags: tags + keyVaultName: keyVault.outputs.name + } +} + +@description('Specifies the content type of the key-value resources. For feature flag, the value should be application/vnd.microsoft.appconfig.ff+json;charset=utf-8. For Key Value reference, the value should be application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8. Otherwise, it\'s optional.') +param contentType string = '' + + +resource configStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' = { + name: !empty(configStoreName) ? configStoreName : '${abbrs.appConfigurationConfigurationStores}${resourceToken}' + location: location + sku: { + name: 'standard' + } +} + +resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { + parent: configStore + name: 'AZURE_COSMOS_CONNECTION_STRING_KEY' + properties: { + value: cosmos.outputs.connectionStringKey + contentType: contentType + tags: tags + } +} + +resource configStoreKeyValue2 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { + parent: configStore + name: 'AZURE_COSMOS_DATABASE_NAME' + properties: { + value: cosmos.outputs.databaseName + contentType: contentType + tags: tags + } +} + +resource configStoreKeyValue3 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { + parent: configStore + name: 'AZURE_KEY_VAULT_ENDPOINT' + properties: { + value: keyVault.outputs.endpoint + contentType: contentType + tags: tags + } +} + +// Data outputs +output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey +output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName + +// App outputs +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name diff --git a/Environments/Todo-Mongo-AKS-LA/main.parameters.json b/Environments/Todo-Mongo-AKS-LA/main.parameters.json new file mode 100644 index 00000000..67ad8524 --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/Environments/Todo-Mongo-AKS-LA/manifest.yaml b/Environments/Todo-Mongo-AKS-LA/manifest.yaml new file mode 100644 index 00000000..5056d21b --- /dev/null +++ b/Environments/Todo-Mongo-AKS-LA/manifest.yaml @@ -0,0 +1,31 @@ +name: Todo-Mongo-AKS +version: 1.0.0 +summary: Todo Nodejs Mongo AKS +description: Deploys a todo app with Nodejs and Mongo +runner: ARM +templatePath: azuredeploy.json + +parameters: +- id: "environmentName" + name: "Environment Name (e.g. testenv)" + description: "Name of the Environment" + type: string + required: true + +- id: "location" + name: "Region (e.g. eastus)" + description: "Location of the resources" + type: string + required: true + +- id: "sharedAKSProjectName" + name: "Shared AKS Project Name" + description: "ADE Project name for the shared AKS cluster" + type: string + required: true + +- id: "sharedAKSEnvironmentName" + name: "Shared AKS Environment Name" + description: "ADE environment name for the shared AKS cluster" + type: string + required: true From fc1c24dd908c359497fff5ee18718975dbd1cf00 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 26 Apr 2024 02:45:56 +0000 Subject: [PATCH 097/112] Rebuild ARM templates --- .../Todo-Mongo-AKS-LA/azuredeploy.json | 546 +++++++++++++----- 1 file changed, 386 insertions(+), 160 deletions(-) diff --git a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json index fb7cf37c..aab04df4 100644 --- a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "8189193153050722730" + "templateHash": "16300934613283283194" } }, "parameters": { @@ -41,13 +41,16 @@ "type": "string", "defaultValue": "" }, - "aksClusterIdentityObjectId": { - "type": "string" - }, "configStoreName": { "type": "string", "defaultValue": "" }, + "sharedAKSProjectName": { + "type": "string" + }, + "sharedAKSEnvironmentName": { + "type": "string" + }, "contentType": { "type": "string", "defaultValue": "", @@ -193,6 +196,7 @@ "webSitesFunctions": "func-", "webStaticSites": "stapp-" }, + "sharedAKSResourceGroup": "[format('{0}-{1}', parameters('sharedAKSProjectName'), parameters('sharedAKSEnvironmentName'))]", "abbrs": "[variables('$fxv#0')]", "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", "tags": { @@ -251,6 +255,379 @@ "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" ] }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyvault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18407114162280426775" + }, + "description": "Creates an Azure Key Vault." + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "principalId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" + } + } + ], + "outputs": { + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "get-aks-name", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appDeployName": { + "value": "todo-deploy" + }, + "aksResourceGroupName": { + "value": "[variables('sharedAKSResourceGroup')]" + }, + "identityName": { + "value": "[format('{0}dp-{1}', variables('abbrs').managedIdentityUserAssignedIdentities, variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15711762243647057476" + } + }, + "parameters": { + "appDeployName": { + "type": "string", + "metadata": { + "description": "app deployment name" + } + }, + "aksResourceGroupName": { + "type": "string", + "metadata": { + "description": "Shared AKS resource group" + } + }, + "timestamp": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "Timestamp - utcNow can only be called as a default value of a parameter." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location to run the deployment script in" + } + }, + "identityName": { + "type": "string" + } + }, + "variables": { + "scriptToExecute": "$output = Get-AzResource -ResourceGroupName $Env:RESOURCEGROUP -ResourceType Microsoft.ContainerService/ManagedClusters\n\nWrite-Output $output\n$DeploymentScriptOutputs = @{}\n$DeploymentScriptOutputs['text'] = $output.Name\n" + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('identityName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('{0}-get-aks-script', parameters('appDeployName'))]", + "kind": "AzurePowerShell", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')))]": {} + } + }, + "properties": { + "forceUpdateTag": "[parameters('timestamp')]", + "azPowerShellVersion": "7.2.0", + "retentionInterval": "PT1H", + "scriptContent": "[variables('scriptToExecute')]", + "cleanupPreference": "Always", + "environmentVariables": [ + { + "name": "RESOURCEGROUP", + "value": "[parameters('aksResourceGroupName')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "read-role-assignment-to-aks", + "resourceGroup": "[parameters('aksResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName')), '2023-01-31').principalId]" + }, + "roleDefinitionId": { + "value": "acdd72a7-3385-48ef-bd42-f606fba81ae7" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "2390256577307700589" + }, + "description": "Creates a role assignment for a service principal." + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceGroup().id, parameters('principalId'), parameters('roleDefinitionId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('identityName'))]" + ] + } + ], + "outputs": { + "clusterName": { + "type": "string", + "value": "[if(empty(reference(resourceId('Microsoft.Resources/deploymentScripts', format('{0}-get-aks-script', parameters('appDeployName'))), '2020-10-01').outputs.text), '', reference(resourceId('Microsoft.Resources/deploymentScripts', format('{0}-get-aks-script', parameters('appDeployName'))), '2020-10-01').outputs.text)]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "aks", + "resourceGroup": "[variables('sharedAKSResourceGroup')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "aksName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'get-aks-name'), '2022-09-01').outputs.clusterName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "889274472255022532" + } + }, + "parameters": { + "aksName": { + "type": "string" + } + }, + "resources": [], + "outputs": { + "aksIdentityObjectId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerService/managedClusters', parameters('aksName')), '2023-10-02-preview').identityProfile.kubeletidentity.objectId]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'get-aks-name')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "cluster-keyvault-access", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" + }, + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('sharedAKSResourceGroup')), 'Microsoft.Resources/deployments', 'aks'), '2022-09-01').outputs.aksIdentityObjectId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "7922086847377910894" + }, + "description": "Assigns an Azure Key Vault access policy." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "add" + }, + "keyVaultName": { + "type": "string" + }, + "permissions": { + "type": "object", + "defaultValue": { + "secrets": [ + "get", + "list" + ] + } + }, + "principalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "properties": { + "accessPolicies": [ + { + "objectId": "[parameters('principalId')]", + "tenantId": "[subscription().tenantId]", + "permissions": "[parameters('permissions')]" + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('sharedAKSResourceGroup')), 'Microsoft.Resources/deployments', 'aks')]", + "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" + ] + }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -282,7 +659,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "11074299330608515845" + "templateHash": "9506725499169300305" } }, "parameters": { @@ -363,7 +740,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "templateHash": "5808186645938946038" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -471,7 +848,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "4693794629197446458" + "templateHash": "7950805331497391447" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -532,7 +909,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "templateHash": "5772648737959552265" }, "description": "Creates an Azure Cosmos DB account." }, @@ -591,8 +968,7 @@ { "name": "EnableServerless" } - ], - "disableLocalAuth": true + ] } }, { @@ -684,156 +1060,6 @@ "dependsOn": [ "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "keyvault", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": "[if(not(empty(parameters('keyVaultName'))), createObject('value', parameters('keyVaultName')), createObject('value', format('{0}{1}', variables('abbrs').keyVaultVaults, variables('resourceToken'))))]", - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('tags')]" - }, - "principalId": { - "value": "[parameters('principalId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" - }, - "description": "Creates an Azure Key Vault." - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "principalId": { - "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[if(not(empty(parameters('principalId'))), createArray(createObject('objectId', parameters('principalId'), 'permissions', createObject('secrets', createArray('get', 'list')), 'tenantId', subscription().tenantId)), createArray())]" - } - } - ], - "outputs": { - "endpoint": { - "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '2022-07-01').vaultUri]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "cluster-keyvault-access", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" - }, - "principalId": { - "value": "[parameters('aksClusterIdentityObjectId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" - }, - "description": "Assigns an Azure Key Vault access policy." - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "add" - }, - "keyVaultName": { - "type": "string" - }, - "permissions": { - "type": "object", - "defaultValue": { - "secrets": [ - "get", - "list" - ] - } - }, - "principalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "properties": { - "accessPolicies": [ - { - "objectId": "[parameters('principalId')]", - "tenantId": "[subscription().tenantId]", - "permissions": "[parameters('permissions')]" - } - ] - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'keyvault')]" - ] } ], "outputs": { From f2c752796f9226b560b792ca695db5ac4dbdfdb9 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 26 Apr 2024 10:50:27 +0800 Subject: [PATCH 098/112] change template name --- Environments/Todo-Mongo-AKS-LA/manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/Todo-Mongo-AKS-LA/manifest.yaml b/Environments/Todo-Mongo-AKS-LA/manifest.yaml index 5056d21b..ae84c164 100644 --- a/Environments/Todo-Mongo-AKS-LA/manifest.yaml +++ b/Environments/Todo-Mongo-AKS-LA/manifest.yaml @@ -1,4 +1,4 @@ -name: Todo-Mongo-AKS +name: Todo-Mongo-AKS-LA version: 1.0.0 summary: Todo Nodejs Mongo AKS description: Deploys a todo app with Nodejs and Mongo From 0f82cfaeaf4cba3277e64aaa1c939df41bc644df Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Fri, 26 Apr 2024 11:01:53 +0800 Subject: [PATCH 099/112] remove environment name --- Environments/Todo-Mongo-AKS-LA/main.bicep | 13 +------------ Environments/Todo-Mongo-AKS-LA/manifest.yaml | 6 ------ 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/Environments/Todo-Mongo-AKS-LA/main.bicep b/Environments/Todo-Mongo-AKS-LA/main.bicep index a2d1c00d..5b937ae2 100644 --- a/Environments/Todo-Mongo-AKS-LA/main.bicep +++ b/Environments/Todo-Mongo-AKS-LA/main.bicep @@ -1,8 +1,3 @@ -@minLength(1) -@maxLength(64) -@description('Name of the the environment which is used to generate a short unique hash used in all resources.') -param environmentName string - @minLength(1) @description('Primary location for all resources') param location string = resourceGroup().location @@ -17,8 +12,7 @@ param sharedAKSEnvironmentName string var sharedAKSResourceGroup = '${sharedAKSProjectName}-${sharedAKSEnvironmentName}' var abbrs = loadJsonContent('./abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } +var resourceToken = toLower(uniqueString(subscription().id, resourceGroup().id, location)) // Store secrets in a keyvault module keyVault './core/security/keyvault.bicep' = { @@ -26,7 +20,6 @@ module keyVault './core/security/keyvault.bicep' = { params: { name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' location: location - tags: tags principalId: principalId } } @@ -64,7 +57,6 @@ module cosmos './app/db.bicep' = { accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' databaseName: cosmosDatabaseName location: location - tags: tags keyVaultName: keyVault.outputs.name } } @@ -87,7 +79,6 @@ resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/key properties: { value: cosmos.outputs.connectionStringKey contentType: contentType - tags: tags } } @@ -97,7 +88,6 @@ resource configStoreKeyValue2 'Microsoft.AppConfiguration/configurationStores/ke properties: { value: cosmos.outputs.databaseName contentType: contentType - tags: tags } } @@ -107,7 +97,6 @@ resource configStoreKeyValue3 'Microsoft.AppConfiguration/configurationStores/ke properties: { value: keyVault.outputs.endpoint contentType: contentType - tags: tags } } diff --git a/Environments/Todo-Mongo-AKS-LA/manifest.yaml b/Environments/Todo-Mongo-AKS-LA/manifest.yaml index ae84c164..ae787450 100644 --- a/Environments/Todo-Mongo-AKS-LA/manifest.yaml +++ b/Environments/Todo-Mongo-AKS-LA/manifest.yaml @@ -6,12 +6,6 @@ runner: ARM templatePath: azuredeploy.json parameters: -- id: "environmentName" - name: "Environment Name (e.g. testenv)" - description: "Name of the Environment" - type: string - required: true - - id: "location" name: "Region (e.g. eastus)" description: "Location of the resources" From 973f2ee945c2d0fd46ceedde5bcff4248cefc6ad Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Fri, 26 Apr 2024 03:04:15 +0000 Subject: [PATCH 100/112] Rebuild ARM templates --- .../Todo-Mongo-AKS-LA/azuredeploy.json | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json index aab04df4..4012e52e 100644 --- a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json @@ -5,18 +5,10 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "16300934613283283194" + "templateHash": "6446845623446854055" } }, "parameters": { - "environmentName": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "metadata": { - "description": "Name of the the environment which is used to generate a short unique hash used in all resources." - } - }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", @@ -198,10 +190,7 @@ }, "sharedAKSResourceGroup": "[format('{0}-{1}', parameters('sharedAKSProjectName'), parameters('sharedAKSEnvironmentName'))]", "abbrs": "[variables('$fxv#0')]", - "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", - "tags": { - "azd-env-name": "[parameters('environmentName')]" - } + "resourceToken": "[toLower(uniqueString(subscription().id, resourceGroup().id, parameters('location')))]" }, "resources": [ { @@ -219,8 +208,7 @@ "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_CONNECTION_STRING_KEY')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]", - "contentType": "[parameters('contentType')]", - "tags": "[variables('tags')]" + "contentType": "[parameters('contentType')]" }, "dependsOn": [ "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", @@ -233,8 +221,7 @@ "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_DATABASE_NAME')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]", - "contentType": "[parameters('contentType')]", - "tags": "[variables('tags')]" + "contentType": "[parameters('contentType')]" }, "dependsOn": [ "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", @@ -247,8 +234,7 @@ "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_KEY_VAULT_ENDPOINT')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]", - "contentType": "[parameters('contentType')]", - "tags": "[variables('tags')]" + "contentType": "[parameters('contentType')]" }, "dependsOn": [ "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", @@ -269,9 +255,6 @@ "location": { "value": "[parameters('location')]" }, - "tags": { - "value": "[variables('tags')]" - }, "principalId": { "value": "[parameters('principalId')]" } @@ -645,9 +628,6 @@ "location": { "value": "[parameters('location')]" }, - "tags": { - "value": "[variables('tags')]" - }, "keyVaultName": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" } From cf9a232e15a2acdc43ca56328965478cbdbac260 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 9 May 2024 22:56:59 +0800 Subject: [PATCH 101/112] remove environment name for individual resource --- Environments/Todo-Mongo-AKS/main.bicep | 3 --- Environments/Todo-Mongo-AKS/manifest.yaml | 10 ++-------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index df2a90dc..b8b500ad 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -18,7 +18,6 @@ var sharedAKSResourceGroup = '${sharedAKSProjectName}-${sharedAKSEnvironmentName var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } // Store secrets in a keyvault module keyVault './core/security/keyvault.bicep' = { @@ -85,7 +84,6 @@ module cosmos './app/db.bicep' = { accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' databaseName: cosmosDatabaseName location: location - tags: tags keyVaultName: keyVault.outputs.name } } @@ -109,7 +107,6 @@ resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/key properties: { value: cosmos.outputs.connectionStringKey contentType: contentType - tags: tags } } diff --git a/Environments/Todo-Mongo-AKS/manifest.yaml b/Environments/Todo-Mongo-AKS/manifest.yaml index dd2945e4..b6aecb57 100644 --- a/Environments/Todo-Mongo-AKS/manifest.yaml +++ b/Environments/Todo-Mongo-AKS/manifest.yaml @@ -6,12 +6,6 @@ runner: ARM templatePath: azuredeploy.json parameters: -- id: "environmentName" - name: "Environment Name (e.g. testenv)" - description: "Name of the Environment" - type: string - required: true - - id: "location" name: "Region (e.g. eastus)" description: "Location of the resources" @@ -19,13 +13,13 @@ parameters: required: true - id: "sharedAKSProjectName" - name: "sharedAKSProjectName" + name: "Shared AKS Project Name" description: "ADE Project name for the shared AKS cluster" type: string required: true - id: "sharedAKSEnvironmentName" - name: "sharedAKSEnvironmentName" + name: "Shared AKS Environment Name" description: "ADE environment name for the shared AKS cluster" type: string required: true From ccdaa5e7f761063ef16ecb7d2122408d0d4d0135 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 9 May 2024 23:15:10 +0800 Subject: [PATCH 102/112] remove tags --- Environments/Todo-Mongo-AKS/main.bicep | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index b8b500ad..1055c0d9 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -1,7 +1,4 @@ -@minLength(1) -@maxLength(64) -@description('Name of the the environment which is used to generate a short unique hash used in all resources.') -param environmentName string + @minLength(1) @description('Primary location for all resources') @@ -17,7 +14,7 @@ param sharedAKSEnvironmentName string var sharedAKSResourceGroup = '${sharedAKSProjectName}-${sharedAKSEnvironmentName}' var abbrs = loadJsonContent('./abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var resourceToken = toLower(uniqueString(subscription().id, resourceGroup().id, location)) // Store secrets in a keyvault module keyVault './core/security/keyvault.bicep' = { @@ -25,7 +22,6 @@ module keyVault './core/security/keyvault.bicep' = { params: { name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' location: location - tags: tags principalId: principalId } } @@ -116,7 +112,6 @@ resource configStoreKeyValue2 'Microsoft.AppConfiguration/configurationStores/ke properties: { value: cosmos.outputs.databaseName contentType: contentType - tags: tags } } @@ -126,7 +121,6 @@ resource configStoreKeyValue3 'Microsoft.AppConfiguration/configurationStores/ke properties: { value: keyVault.outputs.endpoint contentType: contentType - tags: tags } } From 3633821e0b2613d56324b071c08704f1c453063f Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Thu, 9 May 2024 15:17:32 +0000 Subject: [PATCH 103/112] Rebuild ARM templates --- Environments/Todo-Mongo-AKS/azuredeploy.json | 30 ++++---------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index 03f17410..5117d428 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -5,18 +5,10 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "926438374472775395" + "templateHash": "9330098512073283902" } }, "parameters": { - "environmentName": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "metadata": { - "description": "Name of the the environment which is used to generate a short unique hash used in all resources." - } - }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", @@ -198,10 +190,7 @@ }, "sharedAKSResourceGroup": "[format('{0}-{1}', parameters('sharedAKSProjectName'), parameters('sharedAKSEnvironmentName'))]", "abbrs": "[variables('$fxv#0')]", - "resourceToken": "[toLower(uniqueString(subscription().id, parameters('environmentName'), parameters('location')))]", - "tags": { - "azd-env-name": "[parameters('environmentName')]" - } + "resourceToken": "[toLower(uniqueString(subscription().id, resourceGroup().id, parameters('location')))]" }, "resources": [ { @@ -219,8 +208,7 @@ "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_CONNECTION_STRING_KEY')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.connectionStringKey.value]", - "contentType": "[parameters('contentType')]", - "tags": "[variables('tags')]" + "contentType": "[parameters('contentType')]" }, "dependsOn": [ "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", @@ -233,8 +221,7 @@ "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_COSMOS_DATABASE_NAME')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cosmos'), '2022-09-01').outputs.databaseName.value]", - "contentType": "[parameters('contentType')]", - "tags": "[variables('tags')]" + "contentType": "[parameters('contentType')]" }, "dependsOn": [ "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", @@ -247,8 +234,7 @@ "name": "[format('{0}/{1}', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))), 'AZURE_KEY_VAULT_ENDPOINT')]", "properties": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.endpoint.value]", - "contentType": "[parameters('contentType')]", - "tags": "[variables('tags')]" + "contentType": "[parameters('contentType')]" }, "dependsOn": [ "[resourceId('Microsoft.AppConfiguration/configurationStores', if(not(empty(parameters('configStoreName'))), parameters('configStoreName'), format('{0}{1}', variables('abbrs').appConfigurationConfigurationStores, variables('resourceToken'))))]", @@ -269,9 +255,6 @@ "location": { "value": "[parameters('location')]" }, - "tags": { - "value": "[variables('tags')]" - }, "principalId": { "value": "[parameters('principalId')]" } @@ -769,9 +752,6 @@ "location": { "value": "[parameters('location')]" }, - "tags": { - "value": "[variables('tags')]" - }, "keyVaultName": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyvault'), '2022-09-01').outputs.name.value]" } From 034824badef0fde1201655355ec21948690ea2e1 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Thu, 16 May 2024 22:18:18 +0800 Subject: [PATCH 104/112] test for basic lb --- Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep index 4a36262c..b1074b91 100644 --- a/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep +++ b/Environments/Contoso-Base-Shared-AKS-Dev/core/host/aks.bicep @@ -49,7 +49,7 @@ param sku string = 'Free' @description('The load balancer SKU to use for ingress into the AKS cluster') @allowed([ 'basic', 'standard' ]) -param loadBalancerSku string = 'standard' +param loadBalancerSku string = 'basic' @description('Network plugin used for building the Kubernetes network.') @allowed([ 'azure', 'kubenet', 'none' ]) From ed604884ab4642c1c3ceaadc97121be4f4096cf2 Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Thu, 16 May 2024 14:21:05 +0000 Subject: [PATCH 105/112] Rebuild ARM templates --- Environments/AKS-Store-Demo/azuredeploy.json | 116 +++++++++--------- Environments/AKS/azuredeploy.json | 64 +++++----- Environments/APIM/azuredeploy.json | 44 +++---- .../App-Base-WebApp-ACA/azuredeploy.json | 96 +++++++-------- .../App-Base-WebApp-AKS/azuredeploy.json | 28 ++--- Environments/AppVNet/azuredeploy.json | 12 +- Environments/ContainerApp/azuredeploy.json | 32 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 46 +++---- .../azuredeploy.json | 44 +++---- .../azuredeploy.json | 44 +++---- Environments/FunctionApp/azuredeploy.json | 4 +- Environments/OpenAISearch/azuredeploy.json | 64 +++++----- .../OpenAISummarization/azuredeploy.json | 36 +++--- Environments/Sandbox/azuredeploy.json | 4 +- Environments/Spring/azuredeploy.json | 12 +- Environments/Todo-Mongo-ACA/azuredeploy.json | 72 +++++------ .../Todo-Mongo-AKS-LA/azuredeploy.json | 40 +++--- Environments/Todo-Mongo-AKS/azuredeploy.json | 56 ++++----- .../Todo-Nodejs-Mongo-ACA/azuredeploy.json | 96 +++++++-------- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 68 +++++----- Environments/Todo-Shared-ACA/azuredeploy.json | 28 ++--- Environments/Todo-Shared-AKS/azuredeploy.json | 44 +++---- Environments/WebApp/azuredeploy.json | 4 +- Environments/eShop/azuredeploy.json | 8 +- 27 files changed, 573 insertions(+), 573 deletions(-) diff --git a/Environments/AKS-Store-Demo/azuredeploy.json b/Environments/AKS-Store-Demo/azuredeploy.json index 49cd5e1c..51c1321f 100644 --- a/Environments/AKS-Store-Demo/azuredeploy.json +++ b/Environments/AKS-Store-Demo/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10704493879961365284" + "version": "0.27.1.19265", + "templateHash": "13079103199586450317" } }, "parameters": { @@ -278,8 +278,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4186512015795284910" + "version": "0.27.1.19265", + "templateHash": "5739096619747139785" }, "description": "Creates an Azure Cognitive Services instance." }, @@ -418,8 +418,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2445544970077435003" + "version": "0.27.1.19265", + "templateHash": "15506378740875004002" } }, "parameters": { @@ -495,8 +495,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2390256577307700589" + "version": "0.27.1.19265", + "templateHash": "16440196655390446916" }, "description": "Creates a role assignment for a service principal." }, @@ -563,8 +563,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2390256577307700589" + "version": "0.27.1.19265", + "templateHash": "16440196655390446916" }, "description": "Creates a role assignment for a service principal." }, @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1580517862479751439" + "version": "0.27.1.19265", + "templateHash": "10801200485535120804" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -918,8 +918,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -1002,8 +1002,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1712593165933286164" + "version": "0.27.1.19265", + "templateHash": "13323847991157074247" } }, "parameters": { @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "version": "0.27.1.19265", + "templateHash": "17766102100339878897" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1192,8 +1192,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4693794629197446458" + "version": "0.27.1.19265", + "templateHash": "10288501472663096612" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1253,8 +1253,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1428,8 +1428,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16116103805544296619" + "version": "0.27.1.19265", + "templateHash": "16417433276198098531" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -1526,8 +1526,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18220013070549790672" + "version": "0.27.1.19265", + "templateHash": "1496821362624476728" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1580,8 +1580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1719,8 +1719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16206905209322787989" + "version": "0.27.1.19265", + "templateHash": "8405467693161911134" }, "description": "Creates a SQL role definition under an Azure Cosmos DB account." }, @@ -1799,8 +1799,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5580476706925703677" + "version": "0.27.1.19265", + "templateHash": "12223899351427459655" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -1910,8 +1910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2154407021129517742" + "version": "0.27.1.19265", + "templateHash": "850694409429803378" } }, "parameters": { @@ -2011,8 +2011,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12761061430428978799" + "version": "0.27.1.19265", + "templateHash": "5597518929385976113" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2099,8 +2099,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12761061430428978799" + "version": "0.27.1.19265", + "templateHash": "5597518929385976113" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2220,8 +2220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3109754192914374009" + "version": "0.27.1.19265", + "templateHash": "4599659220392226287" } }, "parameters": { @@ -2270,8 +2270,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12761061430428978799" + "version": "0.27.1.19265", + "templateHash": "5597518929385976113" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2355,8 +2355,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12761061430428978799" + "version": "0.27.1.19265", + "templateHash": "5597518929385976113" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2457,8 +2457,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2288712867213955884" + "version": "0.27.1.19265", + "templateHash": "4406210434030454310" } }, "parameters": { @@ -2519,8 +2519,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -2615,8 +2615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11818783974622592424" + "version": "0.27.1.19265", + "templateHash": "3772022125331107815" } }, "parameters": { @@ -2998,8 +2998,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2390256577307700589" + "version": "0.27.1.19265", + "templateHash": "16440196655390446916" }, "description": "Creates a role assignment for a service principal." }, @@ -3063,8 +3063,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2390256577307700589" + "version": "0.27.1.19265", + "templateHash": "16440196655390446916" }, "description": "Creates a role assignment for a service principal." }, @@ -3144,8 +3144,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -3313,8 +3313,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index 8dc763de..dcf1183a 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16034083465139663619" + "version": "0.27.1.19265", + "templateHash": "13993248945486590014" } }, "parameters": { @@ -243,8 +243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15147942591900454453" + "version": "0.27.1.19265", + "templateHash": "5430908891349120781" } }, "parameters": { @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16475877113553841949" + "version": "0.27.1.19265", + "templateHash": "12804735107992664841" } }, "parameters": { @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9199765229548176326" + "version": "0.27.1.19265", + "templateHash": "5127848153811356993" } }, "parameters": { @@ -787,8 +787,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1313507774670351919" + "version": "0.27.1.19265", + "templateHash": "3097865385454057406" } }, "parameters": { @@ -932,8 +932,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9803835955542413586" + "version": "0.27.1.19265", + "templateHash": "11439506449772155116" } }, "parameters": { @@ -990,8 +990,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "593635419190044178" + "version": "0.27.1.19265", + "templateHash": "16831345915498571463" } }, "parameters": { @@ -1105,8 +1105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10664478597037280079" + "version": "0.27.1.19265", + "templateHash": "10899439133881826300" } }, "parameters": { @@ -1186,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11639574980696241038" + "version": "0.27.1.19265", + "templateHash": "9882646386783730752" } }, "parameters": { @@ -1293,8 +1293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15854138267854445037" + "version": "0.27.1.19265", + "templateHash": "5612493339602931747" } }, "parameters": { @@ -1353,8 +1353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15842445230570505343" + "version": "0.27.1.19265", + "templateHash": "14336376257560695558" } }, "parameters": { @@ -1533,8 +1533,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13433119241471746696" + "version": "0.27.1.19265", + "templateHash": "18019078027896437748" } }, "parameters": { @@ -1610,8 +1610,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6525308576612279557" + "version": "0.27.1.19265", + "templateHash": "6756498604117453931" } }, "parameters": { @@ -1660,8 +1660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17741619895875240893" + "version": "0.27.1.19265", + "templateHash": "17108338328025487065" } }, "parameters": { @@ -1740,8 +1740,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1554147265545062632" + "version": "0.27.1.19265", + "templateHash": "9925153315682958000" } }, "parameters": { @@ -1802,8 +1802,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8407206116835390544" + "version": "0.27.1.19265", + "templateHash": "17332775558646578379" } }, "parameters": { diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json index d1f9f27d..a2453e24 100644 --- a/Environments/APIM/azuredeploy.json +++ b/Environments/APIM/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8180529666560946925" + "version": "0.27.1.19265", + "templateHash": "11453813357839555566" } }, "parameters": { @@ -82,8 +82,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6525308576612279557" + "version": "0.27.1.19265", + "templateHash": "6756498604117453931" } }, "parameters": { @@ -132,8 +132,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17741619895875240893" + "version": "0.27.1.19265", + "templateHash": "17108338328025487065" } }, "parameters": { @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1554147265545062632" + "version": "0.27.1.19265", + "templateHash": "9925153315682958000" } }, "parameters": { @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8407206116835390544" + "version": "0.27.1.19265", + "templateHash": "17332775558646578379" } }, "parameters": { @@ -1582,8 +1582,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15657907071797536763" + "version": "0.27.1.19265", + "templateHash": "13408910556128873377" } }, "parameters": { @@ -1711,8 +1711,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10221529978834853957" + "version": "0.27.1.19265", + "templateHash": "2474811418901585459" } }, "parameters": { @@ -1811,8 +1811,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9806810283516288067" + "version": "0.27.1.19265", + "templateHash": "5903550294648301407" } }, "parameters": { @@ -2015,8 +2015,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16674553724797042504" + "version": "0.27.1.19265", + "templateHash": "15068067030352823484" } }, "parameters": { @@ -2263,8 +2263,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16898575566825802440" + "version": "0.27.1.19265", + "templateHash": "3348854091792453779" } }, "parameters": { @@ -2397,8 +2397,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13872673163903146271" + "version": "0.27.1.19265", + "templateHash": "6349826069867355915" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-ACA/azuredeploy.json b/Environments/App-Base-WebApp-ACA/azuredeploy.json index c384657c..4f79f768 100644 --- a/Environments/App-Base-WebApp-ACA/azuredeploy.json +++ b/Environments/App-Base-WebApp-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6959012947984384186" + "version": "0.27.1.19265", + "templateHash": "624029724821603430" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13349567422847897213" + "version": "0.27.1.19265", + "templateHash": "17731094140895676797" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18400100082982052090" + "version": "0.27.1.19265", + "templateHash": "10702261280072308677" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10296126065300176669" + "version": "0.27.1.19265", + "templateHash": "16723176546093901214" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1456353489486868071" + "version": "0.27.1.19265", + "templateHash": "7382174306638540496" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16188590616914828195" + "version": "0.27.1.19265", + "templateHash": "8399579053954067815" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12637092007762978392" + "version": "0.27.1.19265", + "templateHash": "3316380109102128443" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" + "version": "0.27.1.19265", + "templateHash": "6278079210023281106" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1456353489486868071" + "version": "0.27.1.19265", + "templateHash": "7382174306638540496" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16188590616914828195" + "version": "0.27.1.19265", + "templateHash": "8399579053954067815" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11074299330608515845" + "version": "0.27.1.19265", + "templateHash": "10899806140713240148" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "version": "0.27.1.19265", + "templateHash": "17766102100339878897" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4693794629197446458" + "version": "0.27.1.19265", + "templateHash": "10288501472663096612" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2696,8 +2696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2826,8 +2826,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -2907,8 +2907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2972,8 +2972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4283,8 +4283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9679689483306263421" + "version": "0.27.1.19265", + "templateHash": "16692274638994423150" }, "description": "Creates an Azure API Management instance." }, @@ -4433,8 +4433,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15612687543846078835" + "version": "0.27.1.19265", + "templateHash": "10242874607688237506" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json index bb92ee14..f69c6e72 100644 --- a/Environments/App-Base-WebApp-AKS/azuredeploy.json +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3554149725070251677" + "version": "0.27.1.19265", + "templateHash": "4927997388734674073" } }, "parameters": { @@ -219,8 +219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11074299330608515845" + "version": "0.27.1.19265", + "templateHash": "10899806140713240148" } }, "parameters": { @@ -300,8 +300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "version": "0.27.1.19265", + "templateHash": "17766102100339878897" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -408,8 +408,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4693794629197446458" + "version": "0.27.1.19265", + "templateHash": "10288501472663096612" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -469,8 +469,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" + "version": "0.27.1.19265", + "templateHash": "6278079210023281106" }, "description": "Assigns an Azure Key Vault access policy." }, diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json index 9d2c108e..aa53bae5 100644 --- a/Environments/AppVNet/azuredeploy.json +++ b/Environments/AppVNet/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5481419951669703164" + "version": "0.27.1.19265", + "templateHash": "6497469063870400878" } }, "parameters": { @@ -81,8 +81,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12886199108543213420" + "version": "0.27.1.19265", + "templateHash": "7931438411581958329" } }, "parameters": { @@ -369,8 +369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2047106680514800790" + "version": "0.27.1.19265", + "templateHash": "5512529605705402033" } }, "parameters": { diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json index c551c521..f65d68da 100644 --- a/Environments/ContainerApp/azuredeploy.json +++ b/Environments/ContainerApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3405922185286300434" + "version": "0.27.1.19265", + "templateHash": "12824059231950103712" } }, "parameters": { @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13433119241471746696" + "version": "0.27.1.19265", + "templateHash": "18019078027896437748" } }, "parameters": { @@ -161,8 +161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9674021159051080173" + "version": "0.27.1.19265", + "templateHash": "14637495591365070882" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1904463018937934078" + "version": "0.27.1.19265", + "templateHash": "13435106474263394792" } }, "parameters": { @@ -293,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15787024188471042356" + "version": "0.27.1.19265", + "templateHash": "17228629280630120636" } }, "parameters": { @@ -434,8 +434,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2050257844492594652" + "version": "0.27.1.19265", + "templateHash": "12270213809947354925" } }, "parameters": { @@ -508,8 +508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9910213086950599767" + "version": "0.27.1.19265", + "templateHash": "6121317938663998333" } }, "parameters": { @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17741619895875240893" + "version": "0.27.1.19265", + "templateHash": "17108338328025487065" } }, "parameters": { diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json index dfa2ee0e..fa60d072 100644 --- a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1541560146893703868" + "version": "0.27.1.19265", + "templateHash": "16819065780594781984" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3938742814317506649" + "version": "0.27.1.19265", + "templateHash": "8780944729374864326" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18400100082982052090" + "version": "0.27.1.19265", + "templateHash": "10702261280072308677" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json index dfa2ee0e..fa60d072 100644 --- a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1541560146893703868" + "version": "0.27.1.19265", + "templateHash": "16819065780594781984" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3938742814317506649" + "version": "0.27.1.19265", + "templateHash": "8780944729374864326" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18400100082982052090" + "version": "0.27.1.19265", + "templateHash": "10702261280072308677" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json index dfa2ee0e..fa60d072 100644 --- a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1541560146893703868" + "version": "0.27.1.19265", + "templateHash": "16819065780594781984" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3938742814317506649" + "version": "0.27.1.19265", + "templateHash": "8780944729374864326" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18400100082982052090" + "version": "0.27.1.19265", + "templateHash": "10702261280072308677" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json index 7e8f6ecd..6e7e4b11 100644 --- a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8818437751297515128" + "version": "0.27.1.19265", + "templateHash": "15123389605069860098" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11747651847294354491" + "version": "0.27.1.19265", + "templateHash": "1928209770931444560" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -307,7 +307,7 @@ }, "loadBalancerSku": { "type": "string", - "defaultValue": "standard", + "defaultValue": "basic", "allowedValues": [ "basic", "standard" @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2601248910853177384" + "version": "0.27.1.19265", + "templateHash": "13191827042782515675" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6475340733870349157" + "version": "0.27.1.19265", + "templateHash": "18361319263990345172" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17295385747004895580" + "version": "0.27.1.19265", + "templateHash": "3217182116937520163" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json index 7e8f6ecd..8e51637d 100644 --- a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8818437751297515128" + "version": "0.27.1.19265", + "templateHash": "265872149671440984" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11747651847294354491" + "version": "0.27.1.19265", + "templateHash": "5232423214786828905" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2601248910853177384" + "version": "0.27.1.19265", + "templateHash": "13191827042782515675" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6475340733870349157" + "version": "0.27.1.19265", + "templateHash": "18361319263990345172" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17295385747004895580" + "version": "0.27.1.19265", + "templateHash": "3217182116937520163" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json index 7e8f6ecd..8e51637d 100644 --- a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8818437751297515128" + "version": "0.27.1.19265", + "templateHash": "265872149671440984" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11747651847294354491" + "version": "0.27.1.19265", + "templateHash": "5232423214786828905" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2601248910853177384" + "version": "0.27.1.19265", + "templateHash": "13191827042782515675" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6475340733870349157" + "version": "0.27.1.19265", + "templateHash": "18361319263990345172" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17295385747004895580" + "version": "0.27.1.19265", + "templateHash": "3217182116937520163" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index 1f2bc2a8..fa5e9dc1 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11565551485009026934" + "version": "0.27.1.19265", + "templateHash": "17166516891177904950" } }, "parameters": { diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 3eb4f829..39f5da27 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17572017660740017147" + "version": "0.27.1.19265", + "templateHash": "9488234616054495690" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17253896351883621545" + "version": "0.27.1.19265", + "templateHash": "17800985727643727170" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11772465959055468166" + "version": "0.27.1.19265", + "templateHash": "8348405733550822410" } }, "parameters": { @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8783925983412484416" + "version": "0.27.1.19265", + "templateHash": "674509917257188936" } }, "parameters": { @@ -772,8 +772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8783925983412484416" + "version": "0.27.1.19265", + "templateHash": "674509917257188936" } }, "parameters": { @@ -902,8 +902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11486757407246904206" + "version": "0.27.1.19265", + "templateHash": "15015909410817495216" } }, "parameters": { @@ -1029,8 +1029,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13296914309705716766" + "version": "0.27.1.19265", + "templateHash": "3747433188878313637" } }, "parameters": { @@ -1201,8 +1201,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1265,8 +1265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1329,8 +1329,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1393,8 +1393,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1457,8 +1457,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1521,8 +1521,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1585,8 +1585,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1652,8 +1652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { @@ -1719,8 +1719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16195430160742886805" + "version": "0.27.1.19265", + "templateHash": "2871329938034808195" } }, "parameters": { diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index 6e90351d..8c33b707 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3965327044584118956" + "version": "0.27.1.19265", + "templateHash": "2434669281776477496" } }, "parameters": { @@ -249,8 +249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12580763560407085691" + "version": "0.27.1.19265", + "templateHash": "4182208286687686439" } }, "parameters": { @@ -343,8 +343,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1464507928829179730" + "version": "0.27.1.19265", + "templateHash": "1809910357832786961" } }, "parameters": { @@ -438,8 +438,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16047572591945988050" + "version": "0.27.1.19265", + "templateHash": "12120842702917694042" } }, "parameters": { @@ -603,8 +603,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5885215772604838392" + "version": "0.27.1.19265", + "templateHash": "10655691629805448615" } }, "parameters": { @@ -660,8 +660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5885215772604838392" + "version": "0.27.1.19265", + "templateHash": "10655691629805448615" } }, "parameters": { @@ -717,8 +717,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5885215772604838392" + "version": "0.27.1.19265", + "templateHash": "10655691629805448615" } }, "parameters": { @@ -774,8 +774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5885215772604838392" + "version": "0.27.1.19265", + "templateHash": "10655691629805448615" } }, "parameters": { @@ -831,8 +831,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5885215772604838392" + "version": "0.27.1.19265", + "templateHash": "10655691629805448615" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index 0accad9f..9b242604 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9728838765300640554" + "version": "0.27.1.19265", + "templateHash": "1329502019101845773" } }, "resources": [] diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index 2431aefd..619e560c 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7459537732843475328" + "version": "0.27.1.19265", + "templateHash": "8218314593745915191" } }, "parameters": { @@ -143,8 +143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15588594590211857731" + "version": "0.27.1.19265", + "templateHash": "2563363243965646372" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16898314659086455378" + "version": "0.27.1.19265", + "templateHash": "13597485026003814195" } }, "parameters": { diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index fad37734..ecf68027 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2826290446221151175" + "version": "0.27.1.19265", + "templateHash": "4468923594415513011" } }, "parameters": { @@ -295,8 +295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2219027145645725366" + "version": "0.27.1.19265", + "templateHash": "11873870875999514341" } }, "parameters": { @@ -410,8 +410,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18035806118199538117" + "version": "0.27.1.19265", + "templateHash": "839245327381763200" }, "description": "Creates or updates an existing Azure Container App." }, @@ -669,8 +669,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7489884223496332039" + "version": "0.27.1.19265", + "templateHash": "17873363700595432182" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -923,8 +923,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17852591206127636459" + "version": "0.27.1.19265", + "templateHash": "6104191718527679114" } }, "parameters": { @@ -1162,8 +1162,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" + "version": "0.27.1.19265", + "templateHash": "6278079210023281106" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1283,8 +1283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18035806118199538117" + "version": "0.27.1.19265", + "templateHash": "839245327381763200" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1542,8 +1542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7489884223496332039" + "version": "0.27.1.19265", + "templateHash": "17873363700595432182" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1796,8 +1796,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1937,8 +1937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -2114,8 +2114,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11074299330608515845" + "version": "0.27.1.19265", + "templateHash": "10899806140713240148" } }, "parameters": { @@ -2195,8 +2195,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "version": "0.27.1.19265", + "templateHash": "17766102100339878897" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2303,8 +2303,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4693794629197446458" + "version": "0.27.1.19265", + "templateHash": "10288501472663096612" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2364,8 +2364,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2545,8 +2545,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -2625,8 +2625,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9679689483306263421" + "version": "0.27.1.19265", + "templateHash": "16692274638994423150" }, "description": "Creates an Azure API Management instance." }, @@ -2772,8 +2772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15612687543846078835" + "version": "0.27.1.19265", + "templateHash": "10242874607688237506" } }, "parameters": { diff --git a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json index 4012e52e..87ce1798 100644 --- a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6446845623446854055" + "version": "0.27.1.19265", + "templateHash": "7304744400116994811" } }, "parameters": { @@ -265,8 +265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -346,8 +346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15711762243647057476" + "version": "0.27.1.19265", + "templateHash": "391084325711905215" } }, "parameters": { @@ -444,8 +444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2390256577307700589" + "version": "0.27.1.19265", + "templateHash": "16440196655390446916" }, "description": "Creates a role assignment for a service principal." }, @@ -517,8 +517,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "889274472255022532" + "version": "0.27.1.19265", + "templateHash": "15775847162203128109" } }, "parameters": { @@ -562,8 +562,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" + "version": "0.27.1.19265", + "templateHash": "6278079210023281106" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -638,8 +638,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9506725499169300305" + "version": "0.27.1.19265", + "templateHash": "922358179802906048" } }, "parameters": { @@ -719,8 +719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5808186645938946038" + "version": "0.27.1.19265", + "templateHash": "11256531440558691624" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -827,8 +827,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7950805331497391447" + "version": "0.27.1.19265", + "templateHash": "3597031261101386438" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -888,8 +888,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5772648737959552265" + "version": "0.27.1.19265", + "templateHash": "5084371553872687363" }, "description": "Creates an Azure Cosmos DB account." }, diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index 5117d428..2235883e 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9330098512073283902" + "version": "0.27.1.19265", + "templateHash": "16304644104488649884" } }, "parameters": { @@ -265,8 +265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -346,8 +346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15711762243647057476" + "version": "0.27.1.19265", + "templateHash": "391084325711905215" } }, "parameters": { @@ -444,8 +444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2390256577307700589" + "version": "0.27.1.19265", + "templateHash": "16440196655390446916" }, "description": "Creates a role assignment for a service principal." }, @@ -517,8 +517,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "889274472255022532" + "version": "0.27.1.19265", + "templateHash": "15775847162203128109" } }, "parameters": { @@ -562,8 +562,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" + "version": "0.27.1.19265", + "templateHash": "6278079210023281106" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -637,8 +637,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5580476706925703677" + "version": "0.27.1.19265", + "templateHash": "12223899351427459655" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -700,8 +700,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5580476706925703677" + "version": "0.27.1.19265", + "templateHash": "12223899351427459655" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -762,8 +762,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4256008595520895847" + "version": "0.27.1.19265", + "templateHash": "7653059668295471725" } }, "parameters": { @@ -848,8 +848,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16116103805544296619" + "version": "0.27.1.19265", + "templateHash": "16417433276198098531" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -946,8 +946,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18220013070549790672" + "version": "0.27.1.19265", + "templateHash": "1496821362624476728" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1000,8 +1000,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1139,8 +1139,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16206905209322787989" + "version": "0.27.1.19265", + "templateHash": "8405467693161911134" }, "description": "Creates a SQL role definition under an Azure Cosmos DB account." }, @@ -1219,8 +1219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5580476706925703677" + "version": "0.27.1.19265", + "templateHash": "12223899351427459655" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, diff --git a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json index c384657c..4f79f768 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6959012947984384186" + "version": "0.27.1.19265", + "templateHash": "624029724821603430" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13349567422847897213" + "version": "0.27.1.19265", + "templateHash": "17731094140895676797" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18400100082982052090" + "version": "0.27.1.19265", + "templateHash": "10702261280072308677" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10296126065300176669" + "version": "0.27.1.19265", + "templateHash": "16723176546093901214" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1456353489486868071" + "version": "0.27.1.19265", + "templateHash": "7382174306638540496" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16188590616914828195" + "version": "0.27.1.19265", + "templateHash": "8399579053954067815" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12637092007762978392" + "version": "0.27.1.19265", + "templateHash": "3316380109102128443" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" + "version": "0.27.1.19265", + "templateHash": "6278079210023281106" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1456353489486868071" + "version": "0.27.1.19265", + "templateHash": "7382174306638540496" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16188590616914828195" + "version": "0.27.1.19265", + "templateHash": "8399579053954067815" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11074299330608515845" + "version": "0.27.1.19265", + "templateHash": "10899806140713240148" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "version": "0.27.1.19265", + "templateHash": "17766102100339878897" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4693794629197446458" + "version": "0.27.1.19265", + "templateHash": "10288501472663096612" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2696,8 +2696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2826,8 +2826,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -2907,8 +2907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2972,8 +2972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4283,8 +4283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9679689483306263421" + "version": "0.27.1.19265", + "templateHash": "16692274638994423150" }, "description": "Creates an Azure API Management instance." }, @@ -4433,8 +4433,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15612687543846078835" + "version": "0.27.1.19265", + "templateHash": "10242874607688237506" } }, "parameters": { diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json index f83b8d53..0cd47dd3 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7077090465907669075" + "version": "0.27.1.19265", + "templateHash": "1783418531069074676" } }, "parameters": { @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6968919828729736056" + "version": "0.27.1.19265", + "templateHash": "14518591008982344143" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -593,8 +593,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2601248910853177384" + "version": "0.27.1.19265", + "templateHash": "13191827042782515675" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -874,8 +874,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6475340733870349157" + "version": "0.27.1.19265", + "templateHash": "18361319263990345172" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -937,8 +937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -1107,8 +1107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1167,8 +1167,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17295385747004895580" + "version": "0.27.1.19265", + "templateHash": "3217182116937520163" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1225,8 +1225,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7922086847377910894" + "version": "0.27.1.19265", + "templateHash": "6278079210023281106" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1341,8 +1341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11074299330608515845" + "version": "0.27.1.19265", + "templateHash": "10899806140713240148" } }, "parameters": { @@ -1422,8 +1422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3051764932488625981" + "version": "0.27.1.19265", + "templateHash": "17766102100339878897" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1530,8 +1530,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4693794629197446458" + "version": "0.27.1.19265", + "templateHash": "10288501472663096612" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1591,8 +1591,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10034279666465122994" + "version": "0.27.1.19265", + "templateHash": "11403413224043183481" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1772,8 +1772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18407114162280426775" + "version": "0.27.1.19265", + "templateHash": "2948321361335204987" }, "description": "Creates an Azure Key Vault." }, @@ -1850,8 +1850,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1902,8 +1902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -1983,8 +1983,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2048,8 +2048,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-ACA/azuredeploy.json b/Environments/Todo-Shared-ACA/azuredeploy.json index dfa2ee0e..fa60d072 100644 --- a/Environments/Todo-Shared-ACA/azuredeploy.json +++ b/Environments/Todo-Shared-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1541560146893703868" + "version": "0.27.1.19265", + "templateHash": "16819065780594781984" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3938742814317506649" + "version": "0.27.1.19265", + "templateHash": "8780944729374864326" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18400100082982052090" + "version": "0.27.1.19265", + "templateHash": "10702261280072308677" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-AKS/azuredeploy.json b/Environments/Todo-Shared-AKS/azuredeploy.json index 7e8f6ecd..8e51637d 100644 --- a/Environments/Todo-Shared-AKS/azuredeploy.json +++ b/Environments/Todo-Shared-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8818437751297515128" + "version": "0.27.1.19265", + "templateHash": "265872149671440984" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11747651847294354491" + "version": "0.27.1.19265", + "templateHash": "5232423214786828905" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2601248910853177384" + "version": "0.27.1.19265", + "templateHash": "13191827042782515675" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6475340733870349157" + "version": "0.27.1.19265", + "templateHash": "18361319263990345172" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "13368167970047811163" + "version": "0.27.1.19265", + "templateHash": "13288322578480091129" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16305036506142974258" + "version": "0.27.1.19265", + "templateHash": "14118721380530723147" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17295385747004895580" + "version": "0.27.1.19265", + "templateHash": "3217182116937520163" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10048108379044846923" + "version": "0.27.1.19265", + "templateHash": "746484920101780722" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17101038721523251751" + "version": "0.27.1.19265", + "templateHash": "2707024492396982954" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5016503703443937813" + "version": "0.27.1.19265", + "templateHash": "6641653850884689274" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4596399009213720452" + "version": "0.27.1.19265", + "templateHash": "1759216242890853910" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 581c11db..93d59684 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "9942579113430500584" + "version": "0.27.1.19265", + "templateHash": "5225161088571609845" } }, "parameters": { diff --git a/Environments/eShop/azuredeploy.json b/Environments/eShop/azuredeploy.json index 95fc95a9..10ff7997 100644 --- a/Environments/eShop/azuredeploy.json +++ b/Environments/eShop/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16311174785231252405" + "version": "0.27.1.19265", + "templateHash": "1161667177713612303" } }, "parameters": { @@ -69,8 +69,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "18074359320610316650" + "version": "0.27.1.19265", + "templateHash": "2622504385730660318" } }, "parameters": { From 4fa4ab7cb771bba15ee2a363f91d229d61573b0a Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Tue, 18 Jun 2024 08:26:34 +0800 Subject: [PATCH 106/112] disable local auth for app config --- Environments/Todo-Mongo-AKS-LA/main.bicep | 3 +++ Environments/Todo-Mongo-AKS/main.bicep | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Environments/Todo-Mongo-AKS-LA/main.bicep b/Environments/Todo-Mongo-AKS-LA/main.bicep index 5b937ae2..ad75c47d 100644 --- a/Environments/Todo-Mongo-AKS-LA/main.bicep +++ b/Environments/Todo-Mongo-AKS-LA/main.bicep @@ -71,6 +71,9 @@ resource configStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01- sku: { name: 'standard' } + properties: { + disableLocalAuth: true + } } resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { diff --git a/Environments/Todo-Mongo-AKS/main.bicep b/Environments/Todo-Mongo-AKS/main.bicep index 1055c0d9..7d65d321 100644 --- a/Environments/Todo-Mongo-AKS/main.bicep +++ b/Environments/Todo-Mongo-AKS/main.bicep @@ -95,6 +95,9 @@ resource configStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01- sku: { name: 'standard' } + properties: { + disableLocalAuth: true + } } resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = { From 52772685a826ce3089d3928e5a2a0125a53dad4a Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Tue, 18 Jun 2024 00:29:27 +0000 Subject: [PATCH 107/112] Rebuild ARM templates --- Environments/AKS-Store-Demo/azuredeploy.json | 116 +++++++++--------- Environments/AKS/azuredeploy.json | 64 +++++----- Environments/APIM/azuredeploy.json | 44 +++---- .../App-Base-WebApp-ACA/azuredeploy.json | 96 +++++++-------- .../App-Base-WebApp-AKS/azuredeploy.json | 28 ++--- Environments/AppVNet/azuredeploy.json | 12 +- Environments/ContainerApp/azuredeploy.json | 32 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 28 ++--- .../azuredeploy.json | 44 +++---- .../azuredeploy.json | 44 +++---- .../azuredeploy.json | 44 +++---- Environments/FunctionApp/azuredeploy.json | 4 +- Environments/OpenAISearch/azuredeploy.json | 64 +++++----- .../OpenAISummarization/azuredeploy.json | 36 +++--- Environments/Sandbox/azuredeploy.json | 4 +- Environments/Spring/azuredeploy.json | 12 +- Environments/Todo-Mongo-ACA/azuredeploy.json | 72 +++++------ .../Todo-Mongo-AKS-LA/azuredeploy.json | 43 ++++--- Environments/Todo-Mongo-AKS/azuredeploy.json | 59 ++++----- .../Todo-Nodejs-Mongo-ACA/azuredeploy.json | 96 +++++++-------- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 68 +++++----- Environments/Todo-Shared-ACA/azuredeploy.json | 28 ++--- Environments/Todo-Shared-AKS/azuredeploy.json | 44 +++---- Environments/WebApp/azuredeploy.json | 4 +- Environments/eShop/azuredeploy.json | 8 +- 27 files changed, 578 insertions(+), 572 deletions(-) diff --git a/Environments/AKS-Store-Demo/azuredeploy.json b/Environments/AKS-Store-Demo/azuredeploy.json index 51c1321f..e84cc271 100644 --- a/Environments/AKS-Store-Demo/azuredeploy.json +++ b/Environments/AKS-Store-Demo/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13079103199586450317" + "version": "0.28.1.47646", + "templateHash": "14045547779495927499" } }, "parameters": { @@ -278,8 +278,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5739096619747139785" + "version": "0.28.1.47646", + "templateHash": "14628077942294609831" }, "description": "Creates an Azure Cognitive Services instance." }, @@ -418,8 +418,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15506378740875004002" + "version": "0.28.1.47646", + "templateHash": "17373256387750744571" } }, "parameters": { @@ -495,8 +495,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16440196655390446916" + "version": "0.28.1.47646", + "templateHash": "4781574865545118092" }, "description": "Creates a role assignment for a service principal." }, @@ -563,8 +563,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16440196655390446916" + "version": "0.28.1.47646", + "templateHash": "4781574865545118092" }, "description": "Creates a role assignment for a service principal." }, @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10801200485535120804" + "version": "0.28.1.47646", + "templateHash": "15621785056267403063" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -918,8 +918,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -1002,8 +1002,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13323847991157074247" + "version": "0.28.1.47646", + "templateHash": "12081223300885072546" } }, "parameters": { @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17766102100339878897" + "version": "0.28.1.47646", + "templateHash": "14863520980520874403" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1192,8 +1192,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10288501472663096612" + "version": "0.28.1.47646", + "templateHash": "3099268077942840517" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1253,8 +1253,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1428,8 +1428,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16417433276198098531" + "version": "0.28.1.47646", + "templateHash": "15673023057020158227" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -1526,8 +1526,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1496821362624476728" + "version": "0.28.1.47646", + "templateHash": "12948020232541089126" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1580,8 +1580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1719,8 +1719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8405467693161911134" + "version": "0.28.1.47646", + "templateHash": "14256189236777067468" }, "description": "Creates a SQL role definition under an Azure Cosmos DB account." }, @@ -1799,8 +1799,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12223899351427459655" + "version": "0.28.1.47646", + "templateHash": "12347506855528832746" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -1910,8 +1910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "850694409429803378" + "version": "0.28.1.47646", + "templateHash": "848810901119905217" } }, "parameters": { @@ -2011,8 +2011,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5597518929385976113" + "version": "0.28.1.47646", + "templateHash": "5566642789823657082" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2099,8 +2099,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5597518929385976113" + "version": "0.28.1.47646", + "templateHash": "5566642789823657082" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2220,8 +2220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "4599659220392226287" + "version": "0.28.1.47646", + "templateHash": "5351995879362764449" } }, "parameters": { @@ -2270,8 +2270,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5597518929385976113" + "version": "0.28.1.47646", + "templateHash": "5566642789823657082" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2355,8 +2355,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5597518929385976113" + "version": "0.28.1.47646", + "templateHash": "5566642789823657082" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2457,8 +2457,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "4406210434030454310" + "version": "0.28.1.47646", + "templateHash": "17727188530353440330" } }, "parameters": { @@ -2519,8 +2519,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -2615,8 +2615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3772022125331107815" + "version": "0.28.1.47646", + "templateHash": "290678401832833999" } }, "parameters": { @@ -2998,8 +2998,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16440196655390446916" + "version": "0.28.1.47646", + "templateHash": "4781574865545118092" }, "description": "Creates a role assignment for a service principal." }, @@ -3063,8 +3063,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16440196655390446916" + "version": "0.28.1.47646", + "templateHash": "4781574865545118092" }, "description": "Creates a role assignment for a service principal." }, @@ -3144,8 +3144,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -3313,8 +3313,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index dcf1183a..c80850c8 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13993248945486590014" + "version": "0.28.1.47646", + "templateHash": "10662390435220199562" } }, "parameters": { @@ -243,8 +243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5430908891349120781" + "version": "0.28.1.47646", + "templateHash": "6214355675580519133" } }, "parameters": { @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12804735107992664841" + "version": "0.28.1.47646", + "templateHash": "18158232024248349362" } }, "parameters": { @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5127848153811356993" + "version": "0.28.1.47646", + "templateHash": "13972666491247226289" } }, "parameters": { @@ -787,8 +787,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3097865385454057406" + "version": "0.28.1.47646", + "templateHash": "9343369783749877821" } }, "parameters": { @@ -932,8 +932,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11439506449772155116" + "version": "0.28.1.47646", + "templateHash": "16625108748665959159" } }, "parameters": { @@ -990,8 +990,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16831345915498571463" + "version": "0.28.1.47646", + "templateHash": "14275248166991781946" } }, "parameters": { @@ -1105,8 +1105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10899439133881826300" + "version": "0.28.1.47646", + "templateHash": "1436489701407670463" } }, "parameters": { @@ -1186,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "9882646386783730752" + "version": "0.28.1.47646", + "templateHash": "6969847613781204605" } }, "parameters": { @@ -1293,8 +1293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5612493339602931747" + "version": "0.28.1.47646", + "templateHash": "3730622439466297292" } }, "parameters": { @@ -1353,8 +1353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14336376257560695558" + "version": "0.28.1.47646", + "templateHash": "1879717571501978566" } }, "parameters": { @@ -1533,8 +1533,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18019078027896437748" + "version": "0.28.1.47646", + "templateHash": "14318059039743050923" } }, "parameters": { @@ -1610,8 +1610,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6756498604117453931" + "version": "0.28.1.47646", + "templateHash": "11909921929058147104" } }, "parameters": { @@ -1660,8 +1660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17108338328025487065" + "version": "0.28.1.47646", + "templateHash": "16622173900189944103" } }, "parameters": { @@ -1740,8 +1740,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "9925153315682958000" + "version": "0.28.1.47646", + "templateHash": "2696884212366598142" } }, "parameters": { @@ -1802,8 +1802,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17332775558646578379" + "version": "0.28.1.47646", + "templateHash": "16888843495579376877" } }, "parameters": { diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json index a2453e24..2a60fbcb 100644 --- a/Environments/APIM/azuredeploy.json +++ b/Environments/APIM/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11453813357839555566" + "version": "0.28.1.47646", + "templateHash": "6574778642069569003" } }, "parameters": { @@ -82,8 +82,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6756498604117453931" + "version": "0.28.1.47646", + "templateHash": "11909921929058147104" } }, "parameters": { @@ -132,8 +132,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17108338328025487065" + "version": "0.28.1.47646", + "templateHash": "16622173900189944103" } }, "parameters": { @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "9925153315682958000" + "version": "0.28.1.47646", + "templateHash": "2696884212366598142" } }, "parameters": { @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17332775558646578379" + "version": "0.28.1.47646", + "templateHash": "16888843495579376877" } }, "parameters": { @@ -1582,8 +1582,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13408910556128873377" + "version": "0.28.1.47646", + "templateHash": "10309717597850490295" } }, "parameters": { @@ -1711,8 +1711,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2474811418901585459" + "version": "0.28.1.47646", + "templateHash": "17671163087421919386" } }, "parameters": { @@ -1811,8 +1811,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5903550294648301407" + "version": "0.28.1.47646", + "templateHash": "3766433642422465287" } }, "parameters": { @@ -2015,8 +2015,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15068067030352823484" + "version": "0.28.1.47646", + "templateHash": "15474623322305787415" } }, "parameters": { @@ -2263,8 +2263,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3348854091792453779" + "version": "0.28.1.47646", + "templateHash": "3286633466338721792" } }, "parameters": { @@ -2397,8 +2397,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6349826069867355915" + "version": "0.28.1.47646", + "templateHash": "14359862944034511500" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-ACA/azuredeploy.json b/Environments/App-Base-WebApp-ACA/azuredeploy.json index 4f79f768..59c1c459 100644 --- a/Environments/App-Base-WebApp-ACA/azuredeploy.json +++ b/Environments/App-Base-WebApp-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "624029724821603430" + "version": "0.28.1.47646", + "templateHash": "159107709854502154" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17731094140895676797" + "version": "0.28.1.47646", + "templateHash": "9229668951632546517" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10702261280072308677" + "version": "0.28.1.47646", + "templateHash": "6562902683232219313" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16723176546093901214" + "version": "0.28.1.47646", + "templateHash": "17691062517567324593" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7382174306638540496" + "version": "0.28.1.47646", + "templateHash": "12400334503527367691" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8399579053954067815" + "version": "0.28.1.47646", + "templateHash": "17060328277181249956" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3316380109102128443" + "version": "0.28.1.47646", + "templateHash": "14470631914391601446" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6278079210023281106" + "version": "0.28.1.47646", + "templateHash": "17016224891593731674" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7382174306638540496" + "version": "0.28.1.47646", + "templateHash": "12400334503527367691" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8399579053954067815" + "version": "0.28.1.47646", + "templateHash": "17060328277181249956" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10899806140713240148" + "version": "0.28.1.47646", + "templateHash": "8858194042814084678" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17766102100339878897" + "version": "0.28.1.47646", + "templateHash": "14863520980520874403" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10288501472663096612" + "version": "0.28.1.47646", + "templateHash": "3099268077942840517" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2696,8 +2696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2826,8 +2826,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -2907,8 +2907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2972,8 +2972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4283,8 +4283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16692274638994423150" + "version": "0.28.1.47646", + "templateHash": "13208236383464324805" }, "description": "Creates an Azure API Management instance." }, @@ -4433,8 +4433,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10242874607688237506" + "version": "0.28.1.47646", + "templateHash": "1585643982326260567" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json index f69c6e72..bfbfa19a 100644 --- a/Environments/App-Base-WebApp-AKS/azuredeploy.json +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "4927997388734674073" + "version": "0.28.1.47646", + "templateHash": "472870751426555199" } }, "parameters": { @@ -219,8 +219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10899806140713240148" + "version": "0.28.1.47646", + "templateHash": "8858194042814084678" } }, "parameters": { @@ -300,8 +300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17766102100339878897" + "version": "0.28.1.47646", + "templateHash": "14863520980520874403" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -408,8 +408,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10288501472663096612" + "version": "0.28.1.47646", + "templateHash": "3099268077942840517" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -469,8 +469,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6278079210023281106" + "version": "0.28.1.47646", + "templateHash": "17016224891593731674" }, "description": "Assigns an Azure Key Vault access policy." }, diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json index aa53bae5..30d90da8 100644 --- a/Environments/AppVNet/azuredeploy.json +++ b/Environments/AppVNet/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6497469063870400878" + "version": "0.28.1.47646", + "templateHash": "7636150660611667575" } }, "parameters": { @@ -81,8 +81,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7931438411581958329" + "version": "0.28.1.47646", + "templateHash": "3662208610250041215" } }, "parameters": { @@ -369,8 +369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5512529605705402033" + "version": "0.28.1.47646", + "templateHash": "14525311369019494854" } }, "parameters": { diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json index f65d68da..3d3787dc 100644 --- a/Environments/ContainerApp/azuredeploy.json +++ b/Environments/ContainerApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12824059231950103712" + "version": "0.28.1.47646", + "templateHash": "15402730285575211703" } }, "parameters": { @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18019078027896437748" + "version": "0.28.1.47646", + "templateHash": "14318059039743050923" } }, "parameters": { @@ -161,8 +161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14637495591365070882" + "version": "0.28.1.47646", + "templateHash": "10823607355834314174" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13435106474263394792" + "version": "0.28.1.47646", + "templateHash": "7350827179197533189" } }, "parameters": { @@ -293,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17228629280630120636" + "version": "0.28.1.47646", + "templateHash": "11728582857791242150" } }, "parameters": { @@ -434,8 +434,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12270213809947354925" + "version": "0.28.1.47646", + "templateHash": "7712299255847522784" } }, "parameters": { @@ -508,8 +508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6121317938663998333" + "version": "0.28.1.47646", + "templateHash": "5382200499565903781" } }, "parameters": { @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17108338328025487065" + "version": "0.28.1.47646", + "templateHash": "16622173900189944103" } }, "parameters": { diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json index fa60d072..372b041a 100644 --- a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16819065780594781984" + "version": "0.28.1.47646", + "templateHash": "9446336477668144168" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8780944729374864326" + "version": "0.28.1.47646", + "templateHash": "3334036771818682335" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10702261280072308677" + "version": "0.28.1.47646", + "templateHash": "6562902683232219313" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json index fa60d072..372b041a 100644 --- a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16819065780594781984" + "version": "0.28.1.47646", + "templateHash": "9446336477668144168" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8780944729374864326" + "version": "0.28.1.47646", + "templateHash": "3334036771818682335" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10702261280072308677" + "version": "0.28.1.47646", + "templateHash": "6562902683232219313" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json index fa60d072..372b041a 100644 --- a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16819065780594781984" + "version": "0.28.1.47646", + "templateHash": "9446336477668144168" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8780944729374864326" + "version": "0.28.1.47646", + "templateHash": "3334036771818682335" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10702261280072308677" + "version": "0.28.1.47646", + "templateHash": "6562902683232219313" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json index 6e7e4b11..68c0b41b 100644 --- a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15123389605069860098" + "version": "0.28.1.47646", + "templateHash": "12015384816764600656" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1928209770931444560" + "version": "0.28.1.47646", + "templateHash": "16033815890604212137" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13191827042782515675" + "version": "0.28.1.47646", + "templateHash": "18277892820278517058" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18361319263990345172" + "version": "0.28.1.47646", + "templateHash": "8942625910471136519" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3217182116937520163" + "version": "0.28.1.47646", + "templateHash": "15583442179894633871" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json index 8e51637d..6061998e 100644 --- a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "265872149671440984" + "version": "0.28.1.47646", + "templateHash": "12592237793073238850" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5232423214786828905" + "version": "0.28.1.47646", + "templateHash": "15447839188459827691" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13191827042782515675" + "version": "0.28.1.47646", + "templateHash": "18277892820278517058" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18361319263990345172" + "version": "0.28.1.47646", + "templateHash": "8942625910471136519" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3217182116937520163" + "version": "0.28.1.47646", + "templateHash": "15583442179894633871" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json index 8e51637d..6061998e 100644 --- a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "265872149671440984" + "version": "0.28.1.47646", + "templateHash": "12592237793073238850" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5232423214786828905" + "version": "0.28.1.47646", + "templateHash": "15447839188459827691" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13191827042782515675" + "version": "0.28.1.47646", + "templateHash": "18277892820278517058" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18361319263990345172" + "version": "0.28.1.47646", + "templateHash": "8942625910471136519" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3217182116937520163" + "version": "0.28.1.47646", + "templateHash": "15583442179894633871" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index fa5e9dc1..e9434d7d 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17166516891177904950" + "version": "0.28.1.47646", + "templateHash": "15637069764028478126" } }, "parameters": { diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 39f5da27..99ac4d4e 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "9488234616054495690" + "version": "0.28.1.47646", + "templateHash": "10268709855619543733" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17800985727643727170" + "version": "0.28.1.47646", + "templateHash": "16934583072820463488" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8348405733550822410" + "version": "0.28.1.47646", + "templateHash": "1454693974758865459" } }, "parameters": { @@ -649,8 +649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "674509917257188936" + "version": "0.28.1.47646", + "templateHash": "3406865708911674251" } }, "parameters": { @@ -772,8 +772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "674509917257188936" + "version": "0.28.1.47646", + "templateHash": "3406865708911674251" } }, "parameters": { @@ -902,8 +902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15015909410817495216" + "version": "0.28.1.47646", + "templateHash": "7596708093771990692" } }, "parameters": { @@ -1029,8 +1029,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3747433188878313637" + "version": "0.28.1.47646", + "templateHash": "8279939846364871047" } }, "parameters": { @@ -1201,8 +1201,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1265,8 +1265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1329,8 +1329,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1393,8 +1393,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1457,8 +1457,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1521,8 +1521,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1585,8 +1585,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1652,8 +1652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { @@ -1719,8 +1719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2871329938034808195" + "version": "0.28.1.47646", + "templateHash": "10727688037975652837" } }, "parameters": { diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index 8c33b707..a2be62d6 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2434669281776477496" + "version": "0.28.1.47646", + "templateHash": "1217471422967356030" } }, "parameters": { @@ -249,8 +249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "4182208286687686439" + "version": "0.28.1.47646", + "templateHash": "18444247305030252620" } }, "parameters": { @@ -343,8 +343,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1809910357832786961" + "version": "0.28.1.47646", + "templateHash": "8810129407163959286" } }, "parameters": { @@ -438,8 +438,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12120842702917694042" + "version": "0.28.1.47646", + "templateHash": "14966582920785440779" } }, "parameters": { @@ -603,8 +603,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10655691629805448615" + "version": "0.28.1.47646", + "templateHash": "2407710999509835596" } }, "parameters": { @@ -660,8 +660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10655691629805448615" + "version": "0.28.1.47646", + "templateHash": "2407710999509835596" } }, "parameters": { @@ -717,8 +717,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10655691629805448615" + "version": "0.28.1.47646", + "templateHash": "2407710999509835596" } }, "parameters": { @@ -774,8 +774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10655691629805448615" + "version": "0.28.1.47646", + "templateHash": "2407710999509835596" } }, "parameters": { @@ -831,8 +831,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10655691629805448615" + "version": "0.28.1.47646", + "templateHash": "2407710999509835596" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index 9b242604..c0b6f3e4 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1329502019101845773" + "version": "0.28.1.47646", + "templateHash": "8473230506199140280" } }, "resources": [] diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index 619e560c..649f44e7 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8218314593745915191" + "version": "0.28.1.47646", + "templateHash": "4899885240471423009" } }, "parameters": { @@ -143,8 +143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2563363243965646372" + "version": "0.28.1.47646", + "templateHash": "14994092228041367908" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13597485026003814195" + "version": "0.28.1.47646", + "templateHash": "17729349875965697766" } }, "parameters": { diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index ecf68027..d57714c7 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "4468923594415513011" + "version": "0.28.1.47646", + "templateHash": "13814500763932905477" } }, "parameters": { @@ -295,8 +295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11873870875999514341" + "version": "0.28.1.47646", + "templateHash": "2873297444817767281" } }, "parameters": { @@ -410,8 +410,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "839245327381763200" + "version": "0.28.1.47646", + "templateHash": "9570147518939101777" }, "description": "Creates or updates an existing Azure Container App." }, @@ -669,8 +669,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17873363700595432182" + "version": "0.28.1.47646", + "templateHash": "9559947971567510656" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -923,8 +923,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6104191718527679114" + "version": "0.28.1.47646", + "templateHash": "5239517472094193506" } }, "parameters": { @@ -1162,8 +1162,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6278079210023281106" + "version": "0.28.1.47646", + "templateHash": "17016224891593731674" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1283,8 +1283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "839245327381763200" + "version": "0.28.1.47646", + "templateHash": "9570147518939101777" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1542,8 +1542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17873363700595432182" + "version": "0.28.1.47646", + "templateHash": "9559947971567510656" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1796,8 +1796,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1937,8 +1937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -2114,8 +2114,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10899806140713240148" + "version": "0.28.1.47646", + "templateHash": "8858194042814084678" } }, "parameters": { @@ -2195,8 +2195,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17766102100339878897" + "version": "0.28.1.47646", + "templateHash": "14863520980520874403" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2303,8 +2303,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10288501472663096612" + "version": "0.28.1.47646", + "templateHash": "3099268077942840517" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2364,8 +2364,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2545,8 +2545,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -2625,8 +2625,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16692274638994423150" + "version": "0.28.1.47646", + "templateHash": "13208236383464324805" }, "description": "Creates an Azure API Management instance." }, @@ -2772,8 +2772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10242874607688237506" + "version": "0.28.1.47646", + "templateHash": "1585643982326260567" } }, "parameters": { diff --git a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json index 87ce1798..99c44b5c 100644 --- a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7304744400116994811" + "version": "0.28.1.47646", + "templateHash": "14572900008526220543" } }, "parameters": { @@ -200,6 +200,9 @@ "location": "[parameters('location')]", "sku": { "name": "standard" + }, + "properties": { + "disableLocalAuth": true } }, { @@ -265,8 +268,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -346,8 +349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "391084325711905215" + "version": "0.28.1.47646", + "templateHash": "2413639696239709724" } }, "parameters": { @@ -444,8 +447,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16440196655390446916" + "version": "0.28.1.47646", + "templateHash": "4781574865545118092" }, "description": "Creates a role assignment for a service principal." }, @@ -517,8 +520,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15775847162203128109" + "version": "0.28.1.47646", + "templateHash": "2328751784300366789" } }, "parameters": { @@ -562,8 +565,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6278079210023281106" + "version": "0.28.1.47646", + "templateHash": "17016224891593731674" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -638,8 +641,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "922358179802906048" + "version": "0.28.1.47646", + "templateHash": "6240451752366025456" } }, "parameters": { @@ -719,8 +722,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11256531440558691624" + "version": "0.28.1.47646", + "templateHash": "815009670783988728" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -827,8 +830,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3597031261101386438" + "version": "0.28.1.47646", + "templateHash": "17789569437380831903" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -888,8 +891,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5084371553872687363" + "version": "0.28.1.47646", + "templateHash": "7232916224454914373" }, "description": "Creates an Azure Cosmos DB account." }, diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index 2235883e..d4561db4 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16304644104488649884" + "version": "0.28.1.47646", + "templateHash": "3455612940286554590" } }, "parameters": { @@ -200,6 +200,9 @@ "location": "[parameters('location')]", "sku": { "name": "standard" + }, + "properties": { + "disableLocalAuth": true } }, { @@ -265,8 +268,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -346,8 +349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "391084325711905215" + "version": "0.28.1.47646", + "templateHash": "2413639696239709724" } }, "parameters": { @@ -444,8 +447,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16440196655390446916" + "version": "0.28.1.47646", + "templateHash": "4781574865545118092" }, "description": "Creates a role assignment for a service principal." }, @@ -517,8 +520,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15775847162203128109" + "version": "0.28.1.47646", + "templateHash": "2328751784300366789" } }, "parameters": { @@ -562,8 +565,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6278079210023281106" + "version": "0.28.1.47646", + "templateHash": "17016224891593731674" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -637,8 +640,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12223899351427459655" + "version": "0.28.1.47646", + "templateHash": "12347506855528832746" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -700,8 +703,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12223899351427459655" + "version": "0.28.1.47646", + "templateHash": "12347506855528832746" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -762,8 +765,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7653059668295471725" + "version": "0.28.1.47646", + "templateHash": "3752234581129861877" } }, "parameters": { @@ -848,8 +851,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16417433276198098531" + "version": "0.28.1.47646", + "templateHash": "15673023057020158227" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -946,8 +949,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1496821362624476728" + "version": "0.28.1.47646", + "templateHash": "12948020232541089126" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1000,8 +1003,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1139,8 +1142,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8405467693161911134" + "version": "0.28.1.47646", + "templateHash": "14256189236777067468" }, "description": "Creates a SQL role definition under an Azure Cosmos DB account." }, @@ -1219,8 +1222,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12223899351427459655" + "version": "0.28.1.47646", + "templateHash": "12347506855528832746" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, diff --git a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json index 4f79f768..59c1c459 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "624029724821603430" + "version": "0.28.1.47646", + "templateHash": "159107709854502154" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17731094140895676797" + "version": "0.28.1.47646", + "templateHash": "9229668951632546517" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10702261280072308677" + "version": "0.28.1.47646", + "templateHash": "6562902683232219313" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16723176546093901214" + "version": "0.28.1.47646", + "templateHash": "17691062517567324593" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7382174306638540496" + "version": "0.28.1.47646", + "templateHash": "12400334503527367691" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8399579053954067815" + "version": "0.28.1.47646", + "templateHash": "17060328277181249956" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3316380109102128443" + "version": "0.28.1.47646", + "templateHash": "14470631914391601446" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6278079210023281106" + "version": "0.28.1.47646", + "templateHash": "17016224891593731674" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7382174306638540496" + "version": "0.28.1.47646", + "templateHash": "12400334503527367691" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8399579053954067815" + "version": "0.28.1.47646", + "templateHash": "17060328277181249956" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10899806140713240148" + "version": "0.28.1.47646", + "templateHash": "8858194042814084678" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17766102100339878897" + "version": "0.28.1.47646", + "templateHash": "14863520980520874403" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10288501472663096612" + "version": "0.28.1.47646", + "templateHash": "3099268077942840517" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2696,8 +2696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2826,8 +2826,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -2907,8 +2907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2972,8 +2972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4283,8 +4283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16692274638994423150" + "version": "0.28.1.47646", + "templateHash": "13208236383464324805" }, "description": "Creates an Azure API Management instance." }, @@ -4433,8 +4433,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10242874607688237506" + "version": "0.28.1.47646", + "templateHash": "1585643982326260567" } }, "parameters": { diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json index 0cd47dd3..776fc494 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1783418531069074676" + "version": "0.28.1.47646", + "templateHash": "17166396968645210258" } }, "parameters": { @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14518591008982344143" + "version": "0.28.1.47646", + "templateHash": "2587101185242437995" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -593,8 +593,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13191827042782515675" + "version": "0.28.1.47646", + "templateHash": "18277892820278517058" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -874,8 +874,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18361319263990345172" + "version": "0.28.1.47646", + "templateHash": "8942625910471136519" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -937,8 +937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -1107,8 +1107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1167,8 +1167,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3217182116937520163" + "version": "0.28.1.47646", + "templateHash": "15583442179894633871" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1225,8 +1225,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6278079210023281106" + "version": "0.28.1.47646", + "templateHash": "17016224891593731674" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1341,8 +1341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10899806140713240148" + "version": "0.28.1.47646", + "templateHash": "8858194042814084678" } }, "parameters": { @@ -1422,8 +1422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17766102100339878897" + "version": "0.28.1.47646", + "templateHash": "14863520980520874403" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1530,8 +1530,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10288501472663096612" + "version": "0.28.1.47646", + "templateHash": "3099268077942840517" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1591,8 +1591,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11403413224043183481" + "version": "0.28.1.47646", + "templateHash": "5295350533403586059" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1772,8 +1772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2948321361335204987" + "version": "0.28.1.47646", + "templateHash": "4250008929992735853" }, "description": "Creates an Azure Key Vault." }, @@ -1850,8 +1850,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1902,8 +1902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -1983,8 +1983,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2048,8 +2048,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-ACA/azuredeploy.json b/Environments/Todo-Shared-ACA/azuredeploy.json index fa60d072..372b041a 100644 --- a/Environments/Todo-Shared-ACA/azuredeploy.json +++ b/Environments/Todo-Shared-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "16819065780594781984" + "version": "0.28.1.47646", + "templateHash": "9446336477668144168" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8780944729374864326" + "version": "0.28.1.47646", + "templateHash": "3334036771818682335" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10702261280072308677" + "version": "0.28.1.47646", + "templateHash": "6562902683232219313" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-AKS/azuredeploy.json b/Environments/Todo-Shared-AKS/azuredeploy.json index 8e51637d..6061998e 100644 --- a/Environments/Todo-Shared-AKS/azuredeploy.json +++ b/Environments/Todo-Shared-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "265872149671440984" + "version": "0.28.1.47646", + "templateHash": "12592237793073238850" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5232423214786828905" + "version": "0.28.1.47646", + "templateHash": "15447839188459827691" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13191827042782515675" + "version": "0.28.1.47646", + "templateHash": "18277892820278517058" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18361319263990345172" + "version": "0.28.1.47646", + "templateHash": "8942625910471136519" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13288322578480091129" + "version": "0.28.1.47646", + "templateHash": "8075470313870608850" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14118721380530723147" + "version": "0.28.1.47646", + "templateHash": "1386551193288161196" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3217182116937520163" + "version": "0.28.1.47646", + "templateHash": "15583442179894633871" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "746484920101780722" + "version": "0.28.1.47646", + "templateHash": "1577011052565158954" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2707024492396982954" + "version": "0.28.1.47646", + "templateHash": "4411803171372203725" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6641653850884689274" + "version": "0.28.1.47646", + "templateHash": "3510278625614104568" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1759216242890853910" + "version": "0.28.1.47646", + "templateHash": "14575977876683967619" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 93d59684..2cc9b4df 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "5225161088571609845" + "version": "0.28.1.47646", + "templateHash": "4884715398047161988" } }, "parameters": { diff --git a/Environments/eShop/azuredeploy.json b/Environments/eShop/azuredeploy.json index 10ff7997..1073e875 100644 --- a/Environments/eShop/azuredeploy.json +++ b/Environments/eShop/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1161667177713612303" + "version": "0.28.1.47646", + "templateHash": "11275726327917555436" } }, "parameters": { @@ -69,8 +69,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2622504385730660318" + "version": "0.28.1.47646", + "templateHash": "7492460148119304331" } }, "parameters": { From ddfeb74b0147344ba3a2d807ac6c391852500b20 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 5 Mar 2025 16:12:18 +0800 Subject: [PATCH 108/112] use gpt-4o --- .../core/ai/cognitiveservices.bicep | 6 ++--- Environments/OpenAISearch/main.bicep | 23 +++++-------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/Environments/OpenAISearch/core/ai/cognitiveservices.bicep b/Environments/OpenAISearch/core/ai/cognitiveservices.bicep index 8594e16f..40b6c629 100644 --- a/Environments/OpenAISearch/core/ai/cognitiveservices.bicep +++ b/Environments/OpenAISearch/core/ai/cognitiveservices.bicep @@ -10,7 +10,7 @@ param sku object = { name: 'S0' } -resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { +resource account 'Microsoft.CognitiveServices/accounts@2024-10-01' = { name: name location: location tags: tags @@ -24,13 +24,13 @@ resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = { } @batchSize(1) -resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2022-10-01' = [for deployment in deployments: { +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = [for deployment in deployments: { parent: account name: deployment.name properties: { model: deployment.model raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null - scaleSettings: deployment.scaleSettings + } }] diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep index 8a73859c..ac823d6d 100644 --- a/Environments/OpenAISearch/main.bicep +++ b/Environments/OpenAISearch/main.bicep @@ -29,10 +29,10 @@ param formRecognizerResourceGroupLocation string = location param formRecognizerSkuName string = 'S0' -param gptDeploymentName string = 'davinci' -param gptModelName string = 'text-davinci-003' +param gptDeploymentName string = 'gpt-4o-0513' +param gptModelName string = 'gpt-4o' +param modelVersion string = '2024-05-13' param chatGptDeploymentName string = 'chat' -param chatGptModelName string = 'gpt-35-turbo' @description('Id of the user or app to assign application roles') param principalId string @@ -95,21 +95,10 @@ module openAi 'core/ai/cognitiveservices.bicep' = { model: { format: 'OpenAI' name: gptModelName - version: '1' + version: modelVersion } - scaleSettings: { - scaleType: 'Standard' - } - } - { - name: chatGptDeploymentName - model: { - format: 'OpenAI' - name: chatGptModelName - version: '0301' - } - scaleSettings: { - scaleType: 'Standard' + sku: { + name: 'Standard' } } ] From dc39bca58a729d7af68a44855e4d2bd7c72ddf5c Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Wed, 5 Mar 2025 08:14:52 +0000 Subject: [PATCH 109/112] Rebuild ARM templates --- Environments/AKS-Store-Demo/azuredeploy.json | 120 +++++++++--------- Environments/AKS/azuredeploy.json | 66 +++++----- Environments/APIM/azuredeploy.json | 46 +++---- .../App-Base-WebApp-ACA/azuredeploy.json | 98 +++++++------- .../App-Base-WebApp-AKS/azuredeploy.json | 30 ++--- Environments/AppVNet/azuredeploy.json | 12 +- Environments/ContainerApp/azuredeploy.json | 32 ++--- .../azuredeploy.json | 28 ++-- .../azuredeploy.json | 28 ++-- .../azuredeploy.json | 28 ++-- .../azuredeploy.json | 44 +++---- .../azuredeploy.json | 44 +++---- .../azuredeploy.json | 44 +++---- Environments/FunctionApp/azuredeploy.json | 4 +- Environments/OpenAISearch/azuredeploy.json | 113 ++++++++--------- .../OpenAISummarization/azuredeploy.json | 38 +++--- Environments/Sandbox/azuredeploy.json | 4 +- Environments/Spring/azuredeploy.json | 12 +- Environments/Todo-Mongo-ACA/azuredeploy.json | 74 +++++------ .../Todo-Mongo-AKS-LA/azuredeploy.json | 42 +++--- Environments/Todo-Mongo-AKS/azuredeploy.json | 58 ++++----- .../Todo-Nodejs-Mongo-ACA/azuredeploy.json | 98 +++++++------- .../Todo-Nodejs-Mongo-AKS/azuredeploy.json | 70 +++++----- Environments/Todo-Shared-ACA/azuredeploy.json | 28 ++-- Environments/Todo-Shared-AKS/azuredeploy.json | 44 +++---- Environments/WebApp/azuredeploy.json | 4 +- Environments/eShop/azuredeploy.json | 8 +- 27 files changed, 602 insertions(+), 615 deletions(-) diff --git a/Environments/AKS-Store-Demo/azuredeploy.json b/Environments/AKS-Store-Demo/azuredeploy.json index e84cc271..5192ac0c 100644 --- a/Environments/AKS-Store-Demo/azuredeploy.json +++ b/Environments/AKS-Store-Demo/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14045547779495927499" + "version": "0.33.93.31351", + "templateHash": "7622024901460977502" } }, "parameters": { @@ -278,8 +278,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14628077942294609831" + "version": "0.33.93.31351", + "templateHash": "4882699415791423654" }, "description": "Creates an Azure Cognitive Services instance." }, @@ -418,8 +418,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17373256387750744571" + "version": "0.33.93.31351", + "templateHash": "11561445000340772336" } }, "parameters": { @@ -495,8 +495,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4781574865545118092" + "version": "0.33.93.31351", + "templateHash": "504651423117869656" }, "description": "Creates a role assignment for a service principal." }, @@ -563,8 +563,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4781574865545118092" + "version": "0.33.93.31351", + "templateHash": "504651423117869656" }, "description": "Creates a role assignment for a service principal." }, @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15621785056267403063" + "version": "0.33.93.31351", + "templateHash": "4332191042945046992" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -918,8 +918,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -1002,8 +1002,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12081223300885072546" + "version": "0.33.93.31351", + "templateHash": "12717292284503044" } }, "parameters": { @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14863520980520874403" + "version": "0.33.93.31351", + "templateHash": "13409304173345641366" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1119,7 +1119,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -1192,8 +1192,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3099268077942840517" + "version": "0.33.93.31351", + "templateHash": "10077950452960822085" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1253,8 +1253,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1428,8 +1428,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15673023057020158227" + "version": "0.33.93.31351", + "templateHash": "9573743454971870853" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -1463,7 +1463,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('containers'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", @@ -1526,8 +1526,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12948020232541089126" + "version": "0.33.93.31351", + "templateHash": "13431055331596776569" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1580,8 +1580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1719,8 +1719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14256189236777067468" + "version": "0.33.93.31351", + "templateHash": "10539129850912497612" }, "description": "Creates a SQL role definition under an Azure Cosmos DB account." }, @@ -1799,8 +1799,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12347506855528832746" + "version": "0.33.93.31351", + "templateHash": "8674928823795121669" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -1910,8 +1910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "848810901119905217" + "version": "0.33.93.31351", + "templateHash": "13437194154570612640" } }, "parameters": { @@ -2011,8 +2011,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5566642789823657082" + "version": "0.33.93.31351", + "templateHash": "4983422354365180517" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2099,8 +2099,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5566642789823657082" + "version": "0.33.93.31351", + "templateHash": "4983422354365180517" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2220,8 +2220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5351995879362764449" + "version": "0.33.93.31351", + "templateHash": "10949995727225639272" } }, "parameters": { @@ -2270,8 +2270,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5566642789823657082" + "version": "0.33.93.31351", + "templateHash": "4983422354365180517" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2355,8 +2355,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5566642789823657082" + "version": "0.33.93.31351", + "templateHash": "4983422354365180517" }, "description": "Creates or updates a secret in an Azure Key Vault." }, @@ -2457,8 +2457,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17727188530353440330" + "version": "0.33.93.31351", + "templateHash": "12981011663923653002" } }, "parameters": { @@ -2519,8 +2519,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -2615,8 +2615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "290678401832833999" + "version": "0.33.93.31351", + "templateHash": "7178749416562423960" } }, "parameters": { @@ -2998,8 +2998,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4781574865545118092" + "version": "0.33.93.31351", + "templateHash": "504651423117869656" }, "description": "Creates a role assignment for a service principal." }, @@ -3063,8 +3063,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4781574865545118092" + "version": "0.33.93.31351", + "templateHash": "504651423117869656" }, "description": "Creates a role assignment for a service principal." }, @@ -3144,8 +3144,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -3313,8 +3313,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, diff --git a/Environments/AKS/azuredeploy.json b/Environments/AKS/azuredeploy.json index c80850c8..6707e316 100644 --- a/Environments/AKS/azuredeploy.json +++ b/Environments/AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10662390435220199562" + "version": "0.33.93.31351", + "templateHash": "16937678732027439190" } }, "parameters": { @@ -243,8 +243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6214355675580519133" + "version": "0.33.93.31351", + "templateHash": "1762107552049944348" } }, "parameters": { @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18158232024248349362" + "version": "0.33.93.31351", + "templateHash": "13920532535284101877" } }, "parameters": { @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13972666491247226289" + "version": "0.33.93.31351", + "templateHash": "5998911773442812740" } }, "parameters": { @@ -787,8 +787,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9343369783749877821" + "version": "0.33.93.31351", + "templateHash": "1202095050056590291" } }, "parameters": { @@ -932,8 +932,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16625108748665959159" + "version": "0.33.93.31351", + "templateHash": "18194644277235904787" } }, "parameters": { @@ -990,8 +990,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14275248166991781946" + "version": "0.33.93.31351", + "templateHash": "9644535467530164292" } }, "parameters": { @@ -1105,8 +1105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1436489701407670463" + "version": "0.33.93.31351", + "templateHash": "18260378771435656856" } }, "parameters": { @@ -1186,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6969847613781204605" + "version": "0.33.93.31351", + "templateHash": "265012454702271744" } }, "parameters": { @@ -1220,7 +1220,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -1293,8 +1293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3730622439466297292" + "version": "0.33.93.31351", + "templateHash": "6739345479442612998" } }, "parameters": { @@ -1353,8 +1353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1879717571501978566" + "version": "0.33.93.31351", + "templateHash": "5981694179131613931" } }, "parameters": { @@ -1533,8 +1533,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14318059039743050923" + "version": "0.33.93.31351", + "templateHash": "5019450926165451596" } }, "parameters": { @@ -1610,8 +1610,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11909921929058147104" + "version": "0.33.93.31351", + "templateHash": "5691955192330149839" } }, "parameters": { @@ -1660,8 +1660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16622173900189944103" + "version": "0.33.93.31351", + "templateHash": "14560898431575392550" } }, "parameters": { @@ -1740,8 +1740,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2696884212366598142" + "version": "0.33.93.31351", + "templateHash": "17741607648833201580" } }, "parameters": { @@ -1802,8 +1802,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16888843495579376877" + "version": "0.33.93.31351", + "templateHash": "3100140366179895101" } }, "parameters": { diff --git a/Environments/APIM/azuredeploy.json b/Environments/APIM/azuredeploy.json index 2a60fbcb..d9e67595 100644 --- a/Environments/APIM/azuredeploy.json +++ b/Environments/APIM/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6574778642069569003" + "version": "0.33.93.31351", + "templateHash": "18403279847108981694" } }, "parameters": { @@ -82,8 +82,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11909921929058147104" + "version": "0.33.93.31351", + "templateHash": "5691955192330149839" } }, "parameters": { @@ -132,8 +132,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16622173900189944103" + "version": "0.33.93.31351", + "templateHash": "14560898431575392550" } }, "parameters": { @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2696884212366598142" + "version": "0.33.93.31351", + "templateHash": "17741607648833201580" } }, "parameters": { @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16888843495579376877" + "version": "0.33.93.31351", + "templateHash": "3100140366179895101" } }, "parameters": { @@ -1582,8 +1582,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10309717597850490295" + "version": "0.33.93.31351", + "templateHash": "3121630196925685082" } }, "parameters": { @@ -1624,7 +1624,7 @@ "resources": [ { "copy": { - "name": "container", + "name": "storage::blobServices::container", "count": "[length(parameters('containers'))]" }, "condition": "[not(empty(parameters('containers')))]", @@ -1711,8 +1711,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17671163087421919386" + "version": "0.33.93.31351", + "templateHash": "9112659407792672495" } }, "parameters": { @@ -1811,8 +1811,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3766433642422465287" + "version": "0.33.93.31351", + "templateHash": "17871062746611407301" } }, "parameters": { @@ -2015,8 +2015,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15474623322305787415" + "version": "0.33.93.31351", + "templateHash": "3758211175953504169" } }, "parameters": { @@ -2263,8 +2263,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3286633466338721792" + "version": "0.33.93.31351", + "templateHash": "15947684177963472439" } }, "parameters": { @@ -2397,8 +2397,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14359862944034511500" + "version": "0.33.93.31351", + "templateHash": "11114943437247662200" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-ACA/azuredeploy.json b/Environments/App-Base-WebApp-ACA/azuredeploy.json index 59c1c459..dd6dd596 100644 --- a/Environments/App-Base-WebApp-ACA/azuredeploy.json +++ b/Environments/App-Base-WebApp-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "159107709854502154" + "version": "0.33.93.31351", + "templateHash": "8685798837439394592" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9229668951632546517" + "version": "0.33.93.31351", + "templateHash": "2207708574580616175" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6562902683232219313" + "version": "0.33.93.31351", + "templateHash": "181961455962728932" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17691062517567324593" + "version": "0.33.93.31351", + "templateHash": "8108297760064638286" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12400334503527367691" + "version": "0.33.93.31351", + "templateHash": "3939012368833498791" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17060328277181249956" + "version": "0.33.93.31351", + "templateHash": "16032798261150015163" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14470631914391601446" + "version": "0.33.93.31351", + "templateHash": "17760167384993713045" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17016224891593731674" + "version": "0.33.93.31351", + "templateHash": "1623829276601313671" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12400334503527367691" + "version": "0.33.93.31351", + "templateHash": "3939012368833498791" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17060328277181249956" + "version": "0.33.93.31351", + "templateHash": "16032798261150015163" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8858194042814084678" + "version": "0.33.93.31351", + "templateHash": "9291339074123332314" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14863520980520874403" + "version": "0.33.93.31351", + "templateHash": "13409304173345641366" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2381,7 +2381,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3099268077942840517" + "version": "0.33.93.31351", + "templateHash": "10077950452960822085" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2696,8 +2696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2826,8 +2826,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -2907,8 +2907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2972,8 +2972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4283,8 +4283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13208236383464324805" + "version": "0.33.93.31351", + "templateHash": "12940223009526209631" }, "description": "Creates an Azure API Management instance." }, @@ -4433,8 +4433,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1585643982326260567" + "version": "0.33.93.31351", + "templateHash": "1777775096889621317" } }, "parameters": { diff --git a/Environments/App-Base-WebApp-AKS/azuredeploy.json b/Environments/App-Base-WebApp-AKS/azuredeploy.json index bfbfa19a..eb57b914 100644 --- a/Environments/App-Base-WebApp-AKS/azuredeploy.json +++ b/Environments/App-Base-WebApp-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "472870751426555199" + "version": "0.33.93.31351", + "templateHash": "5876573075632727900" } }, "parameters": { @@ -219,8 +219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8858194042814084678" + "version": "0.33.93.31351", + "templateHash": "9291339074123332314" } }, "parameters": { @@ -300,8 +300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14863520980520874403" + "version": "0.33.93.31351", + "templateHash": "13409304173345641366" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -335,7 +335,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -408,8 +408,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3099268077942840517" + "version": "0.33.93.31351", + "templateHash": "10077950452960822085" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -469,8 +469,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -650,8 +650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -725,8 +725,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17016224891593731674" + "version": "0.33.93.31351", + "templateHash": "1623829276601313671" }, "description": "Assigns an Azure Key Vault access policy." }, diff --git a/Environments/AppVNet/azuredeploy.json b/Environments/AppVNet/azuredeploy.json index 30d90da8..683e57a0 100644 --- a/Environments/AppVNet/azuredeploy.json +++ b/Environments/AppVNet/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7636150660611667575" + "version": "0.33.93.31351", + "templateHash": "10247939741771609273" } }, "parameters": { @@ -81,8 +81,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3662208610250041215" + "version": "0.33.93.31351", + "templateHash": "5198644096566280335" } }, "parameters": { @@ -369,8 +369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14525311369019494854" + "version": "0.33.93.31351", + "templateHash": "15734384711859127101" } }, "parameters": { diff --git a/Environments/ContainerApp/azuredeploy.json b/Environments/ContainerApp/azuredeploy.json index 3d3787dc..ba36dea6 100644 --- a/Environments/ContainerApp/azuredeploy.json +++ b/Environments/ContainerApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15402730285575211703" + "version": "0.33.93.31351", + "templateHash": "16766600800051784723" } }, "parameters": { @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14318059039743050923" + "version": "0.33.93.31351", + "templateHash": "5019450926165451596" } }, "parameters": { @@ -161,8 +161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10823607355834314174" + "version": "0.33.93.31351", + "templateHash": "11747353756272991035" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7350827179197533189" + "version": "0.33.93.31351", + "templateHash": "13696641671938158116" } }, "parameters": { @@ -293,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11728582857791242150" + "version": "0.33.93.31351", + "templateHash": "2762053625539764763" } }, "parameters": { @@ -434,8 +434,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7712299255847522784" + "version": "0.33.93.31351", + "templateHash": "9986958843000507700" } }, "parameters": { @@ -508,8 +508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5382200499565903781" + "version": "0.33.93.31351", + "templateHash": "13204027561996019098" } }, "parameters": { @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16622173900189944103" + "version": "0.33.93.31351", + "templateHash": "14560898431575392550" } }, "parameters": { diff --git a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json index 372b041a..1516751d 100644 --- a/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9446336477668144168" + "version": "0.33.93.31351", + "templateHash": "17842367933944312587" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3334036771818682335" + "version": "0.33.93.31351", + "templateHash": "2087685049550457930" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6562902683232219313" + "version": "0.33.93.31351", + "templateHash": "181961455962728932" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json index 372b041a..1516751d 100644 --- a/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9446336477668144168" + "version": "0.33.93.31351", + "templateHash": "17842367933944312587" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3334036771818682335" + "version": "0.33.93.31351", + "templateHash": "2087685049550457930" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6562902683232219313" + "version": "0.33.93.31351", + "templateHash": "181961455962728932" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json index 372b041a..1516751d 100644 --- a/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-ACA-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9446336477668144168" + "version": "0.33.93.31351", + "templateHash": "17842367933944312587" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3334036771818682335" + "version": "0.33.93.31351", + "templateHash": "2087685049550457930" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6562902683232219313" + "version": "0.33.93.31351", + "templateHash": "181961455962728932" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json index 68c0b41b..21d2a49c 100644 --- a/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Dev/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12015384816764600656" + "version": "0.33.93.31351", + "templateHash": "13584291079844346886" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16033815890604212137" + "version": "0.33.93.31351", + "templateHash": "6176020911997835089" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18277892820278517058" + "version": "0.33.93.31351", + "templateHash": "10410905278989344027" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8942625910471136519" + "version": "0.33.93.31351", + "templateHash": "12676601876476438690" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15583442179894633871" + "version": "0.33.93.31351", + "templateHash": "17340621863331831612" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json index 6061998e..67d21420 100644 --- a/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Prod/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12592237793073238850" + "version": "0.33.93.31351", + "templateHash": "15952093211390305974" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15447839188459827691" + "version": "0.33.93.31351", + "templateHash": "15428929372978620622" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18277892820278517058" + "version": "0.33.93.31351", + "templateHash": "10410905278989344027" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8942625910471136519" + "version": "0.33.93.31351", + "templateHash": "12676601876476438690" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15583442179894633871" + "version": "0.33.93.31351", + "templateHash": "17340621863331831612" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json index 6061998e..67d21420 100644 --- a/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json +++ b/Environments/Contoso-Base-Shared-AKS-Test/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12592237793073238850" + "version": "0.33.93.31351", + "templateHash": "15952093211390305974" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15447839188459827691" + "version": "0.33.93.31351", + "templateHash": "15428929372978620622" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18277892820278517058" + "version": "0.33.93.31351", + "templateHash": "10410905278989344027" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8942625910471136519" + "version": "0.33.93.31351", + "templateHash": "12676601876476438690" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15583442179894633871" + "version": "0.33.93.31351", + "templateHash": "17340621863331831612" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/FunctionApp/azuredeploy.json b/Environments/FunctionApp/azuredeploy.json index e9434d7d..80030113 100644 --- a/Environments/FunctionApp/azuredeploy.json +++ b/Environments/FunctionApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15637069764028478126" + "version": "0.33.93.31351", + "templateHash": "11121480551279215553" } }, "parameters": { diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 99ac4d4e..256d5b4b 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10268709855619543733" + "version": "0.33.93.31351", + "templateHash": "7424594458485872868" } }, "parameters": { @@ -87,19 +87,19 @@ }, "gptDeploymentName": { "type": "string", - "defaultValue": "davinci" + "defaultValue": "gpt-4o-0513" }, "gptModelName": { "type": "string", - "defaultValue": "text-davinci-003" + "defaultValue": "gpt-4o" }, - "chatGptDeploymentName": { + "modelVersion": { "type": "string", - "defaultValue": "chat" + "defaultValue": "2024-05-13" }, - "chatGptModelName": { + "chatGptDeploymentName": { "type": "string", - "defaultValue": "gpt-35-turbo" + "defaultValue": "chat" }, "principalId": { "type": "string", @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16934583072820463488" + "version": "0.33.93.31351", + "templateHash": "10643458792052922105" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1454693974758865459" + "version": "0.33.93.31351", + "templateHash": "377501608988756920" } }, "parameters": { @@ -623,21 +623,10 @@ "model": { "format": "OpenAI", "name": "[parameters('gptModelName')]", - "version": "1" - }, - "scaleSettings": { - "scaleType": "Standard" - } - }, - { - "name": "[parameters('chatGptDeploymentName')]", - "model": { - "format": "OpenAI", - "name": "[parameters('chatGptModelName')]", - "version": "0301" + "version": "[parameters('modelVersion')]" }, - "scaleSettings": { - "scaleType": "Standard" + "sku": { + "name": "Standard" } } ] @@ -649,8 +638,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3406865708911674251" + "version": "0.33.93.31351", + "templateHash": "252417070114373195" } }, "parameters": { @@ -691,7 +680,7 @@ "resources": [ { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -711,12 +700,11 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", "properties": { "model": "[parameters('deployments')[copyIndex()].model]", - "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]", - "scaleSettings": "[parameters('deployments')[copyIndex()].scaleSettings]" + "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]" }, "dependsOn": [ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" @@ -726,7 +714,7 @@ "outputs": { "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2022-10-01').endpoint]" + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2024-10-01').endpoint]" }, "id": { "type": "string", @@ -772,8 +760,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3406865708911674251" + "version": "0.33.93.31351", + "templateHash": "252417070114373195" } }, "parameters": { @@ -814,7 +802,7 @@ "resources": [ { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -834,12 +822,11 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", "properties": { "model": "[parameters('deployments')[copyIndex()].model]", - "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]", - "scaleSettings": "[parameters('deployments')[copyIndex()].scaleSettings]" + "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]" }, "dependsOn": [ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" @@ -849,7 +836,7 @@ "outputs": { "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2022-10-01').endpoint]" + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2024-10-01').endpoint]" }, "id": { "type": "string", @@ -902,8 +889,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7596708093771990692" + "version": "0.33.93.31351", + "templateHash": "3771478521284452856" } }, "parameters": { @@ -1029,8 +1016,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8279939846364871047" + "version": "0.33.93.31351", + "templateHash": "11206818764871276945" } }, "parameters": { @@ -1112,7 +1099,7 @@ "resources": [ { "copy": { - "name": "container", + "name": "storage::blobServices::container", "count": "[length(parameters('containers'))]" }, "condition": "[not(empty(parameters('containers')))]", @@ -1201,8 +1188,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1265,8 +1252,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1329,8 +1316,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1393,8 +1380,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1457,8 +1444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1521,8 +1508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1585,8 +1572,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1652,8 +1639,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1719,8 +1706,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { diff --git a/Environments/OpenAISummarization/azuredeploy.json b/Environments/OpenAISummarization/azuredeploy.json index a2be62d6..4501bae8 100644 --- a/Environments/OpenAISummarization/azuredeploy.json +++ b/Environments/OpenAISummarization/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1217471422967356030" + "version": "0.33.93.31351", + "templateHash": "10860680307147770362" } }, "parameters": { @@ -249,8 +249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18444247305030252620" + "version": "0.33.93.31351", + "templateHash": "14729690604470531821" } }, "parameters": { @@ -343,8 +343,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8810129407163959286" + "version": "0.33.93.31351", + "templateHash": "17214477931047060888" } }, "parameters": { @@ -438,8 +438,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14966582920785440779" + "version": "0.33.93.31351", + "templateHash": "10211474683289759791" } }, "parameters": { @@ -517,7 +517,7 @@ "resources": [ { "copy": { - "name": "container", + "name": "storage::blobServices::container", "count": "[length(parameters('containers'))]" }, "condition": "[not(empty(parameters('containers')))]", @@ -603,8 +603,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2407710999509835596" + "version": "0.33.93.31351", + "templateHash": "10862599142903367266" } }, "parameters": { @@ -660,8 +660,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2407710999509835596" + "version": "0.33.93.31351", + "templateHash": "10862599142903367266" } }, "parameters": { @@ -717,8 +717,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2407710999509835596" + "version": "0.33.93.31351", + "templateHash": "10862599142903367266" } }, "parameters": { @@ -774,8 +774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2407710999509835596" + "version": "0.33.93.31351", + "templateHash": "10862599142903367266" } }, "parameters": { @@ -831,8 +831,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2407710999509835596" + "version": "0.33.93.31351", + "templateHash": "10862599142903367266" } }, "parameters": { diff --git a/Environments/Sandbox/azuredeploy.json b/Environments/Sandbox/azuredeploy.json index c0b6f3e4..1bcd4e85 100644 --- a/Environments/Sandbox/azuredeploy.json +++ b/Environments/Sandbox/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8473230506199140280" + "version": "0.33.93.31351", + "templateHash": "1938494866978600684" } }, "resources": [] diff --git a/Environments/Spring/azuredeploy.json b/Environments/Spring/azuredeploy.json index 649f44e7..0617ad58 100644 --- a/Environments/Spring/azuredeploy.json +++ b/Environments/Spring/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4899885240471423009" + "version": "0.33.93.31351", + "templateHash": "14573036263927817418" } }, "parameters": { @@ -143,8 +143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14994092228041367908" + "version": "0.33.93.31351", + "templateHash": "16954747342257506226" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17729349875965697766" + "version": "0.33.93.31351", + "templateHash": "13749919905659510121" } }, "parameters": { diff --git a/Environments/Todo-Mongo-ACA/azuredeploy.json b/Environments/Todo-Mongo-ACA/azuredeploy.json index d57714c7..5995b9ed 100644 --- a/Environments/Todo-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13814500763932905477" + "version": "0.33.93.31351", + "templateHash": "8198077183564601693" } }, "parameters": { @@ -295,8 +295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2873297444817767281" + "version": "0.33.93.31351", + "templateHash": "12669776031430375425" } }, "parameters": { @@ -410,8 +410,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9570147518939101777" + "version": "0.33.93.31351", + "templateHash": "12913388468827193078" }, "description": "Creates or updates an existing Azure Container App." }, @@ -669,8 +669,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9559947971567510656" + "version": "0.33.93.31351", + "templateHash": "8072146290693513715" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -923,8 +923,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1084,8 +1084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5239517472094193506" + "version": "0.33.93.31351", + "templateHash": "102507184102693386" } }, "parameters": { @@ -1162,8 +1162,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17016224891593731674" + "version": "0.33.93.31351", + "templateHash": "1623829276601313671" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1283,8 +1283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9570147518939101777" + "version": "0.33.93.31351", + "templateHash": "12913388468827193078" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1542,8 +1542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9559947971567510656" + "version": "0.33.93.31351", + "templateHash": "8072146290693513715" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1796,8 +1796,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1937,8 +1937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -2114,8 +2114,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8858194042814084678" + "version": "0.33.93.31351", + "templateHash": "9291339074123332314" } }, "parameters": { @@ -2195,8 +2195,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14863520980520874403" + "version": "0.33.93.31351", + "templateHash": "13409304173345641366" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2230,7 +2230,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -2303,8 +2303,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3099268077942840517" + "version": "0.33.93.31351", + "templateHash": "10077950452960822085" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2364,8 +2364,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2545,8 +2545,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -2625,8 +2625,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13208236383464324805" + "version": "0.33.93.31351", + "templateHash": "12940223009526209631" }, "description": "Creates an Azure API Management instance." }, @@ -2772,8 +2772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1585643982326260567" + "version": "0.33.93.31351", + "templateHash": "1777775096889621317" } }, "parameters": { diff --git a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json index 99c44b5c..72eabf21 100644 --- a/Environments/Todo-Mongo-AKS-LA/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS-LA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14572900008526220543" + "version": "0.33.93.31351", + "templateHash": "14619850146842534150" } }, "parameters": { @@ -268,8 +268,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -349,8 +349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2413639696239709724" + "version": "0.33.93.31351", + "templateHash": "17298942361779867181" } }, "parameters": { @@ -447,8 +447,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4781574865545118092" + "version": "0.33.93.31351", + "templateHash": "504651423117869656" }, "description": "Creates a role assignment for a service principal." }, @@ -520,8 +520,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2328751784300366789" + "version": "0.33.93.31351", + "templateHash": "11501201464024313517" } }, "parameters": { @@ -565,8 +565,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17016224891593731674" + "version": "0.33.93.31351", + "templateHash": "1623829276601313671" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -641,8 +641,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6240451752366025456" + "version": "0.33.93.31351", + "templateHash": "16356621498347610424" } }, "parameters": { @@ -722,8 +722,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "815009670783988728" + "version": "0.33.93.31351", + "templateHash": "17164586544258041572" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -757,7 +757,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -830,8 +830,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17789569437380831903" + "version": "0.33.93.31351", + "templateHash": "12256829549336185062" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -891,8 +891,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7232916224454914373" + "version": "0.33.93.31351", + "templateHash": "14869432155313199209" }, "description": "Creates an Azure Cosmos DB account." }, diff --git a/Environments/Todo-Mongo-AKS/azuredeploy.json b/Environments/Todo-Mongo-AKS/azuredeploy.json index d4561db4..6d8618b4 100644 --- a/Environments/Todo-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3455612940286554590" + "version": "0.33.93.31351", + "templateHash": "2223284619733816752" } }, "parameters": { @@ -268,8 +268,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -349,8 +349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2413639696239709724" + "version": "0.33.93.31351", + "templateHash": "17298942361779867181" } }, "parameters": { @@ -447,8 +447,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4781574865545118092" + "version": "0.33.93.31351", + "templateHash": "504651423117869656" }, "description": "Creates a role assignment for a service principal." }, @@ -520,8 +520,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2328751784300366789" + "version": "0.33.93.31351", + "templateHash": "11501201464024313517" } }, "parameters": { @@ -565,8 +565,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17016224891593731674" + "version": "0.33.93.31351", + "templateHash": "1623829276601313671" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -640,8 +640,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12347506855528832746" + "version": "0.33.93.31351", + "templateHash": "8674928823795121669" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -703,8 +703,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12347506855528832746" + "version": "0.33.93.31351", + "templateHash": "8674928823795121669" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, @@ -765,8 +765,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3752234581129861877" + "version": "0.33.93.31351", + "templateHash": "15095671647294378253" } }, "parameters": { @@ -851,8 +851,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15673023057020158227" + "version": "0.33.93.31351", + "templateHash": "9573743454971870853" }, "description": "Creates an Azure Cosmos DB for NoSQL account with a database." }, @@ -886,7 +886,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('containers'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", @@ -949,8 +949,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12948020232541089126" + "version": "0.33.93.31351", + "templateHash": "13431055331596776569" }, "description": "Creates an Azure Cosmos DB for NoSQL account." }, @@ -1003,8 +1003,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1142,8 +1142,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14256189236777067468" + "version": "0.33.93.31351", + "templateHash": "10539129850912497612" }, "description": "Creates a SQL role definition under an Azure Cosmos DB account." }, @@ -1222,8 +1222,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12347506855528832746" + "version": "0.33.93.31351", + "templateHash": "8674928823795121669" }, "description": "Creates a SQL role assignment under an Azure Cosmos DB account." }, diff --git a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json index 59c1c459..dd6dd596 100644 --- a/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "159107709854502154" + "version": "0.33.93.31351", + "templateHash": "8685798837439394592" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9229668951632546517" + "version": "0.33.93.31351", + "templateHash": "2207708574580616175" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -353,8 +353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6562902683232219313" + "version": "0.33.93.31351", + "templateHash": "181961455962728932" }, "description": "Creates an Azure Container Apps environment." }, @@ -456,8 +456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17691062517567324593" + "version": "0.33.93.31351", + "templateHash": "8108297760064638286" } }, "parameters": { @@ -778,8 +778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12400334503527367691" + "version": "0.33.93.31351", + "templateHash": "3939012368833498791" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17060328277181249956" + "version": "0.33.93.31351", + "templateHash": "16032798261150015163" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -1276,8 +1276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1432,8 +1432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14470631914391601446" + "version": "0.33.93.31351", + "templateHash": "17760167384993713045" } }, "parameters": { @@ -1504,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17016224891593731674" + "version": "0.33.93.31351", + "templateHash": "1623829276601313671" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1622,8 +1622,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12400334503527367691" + "version": "0.33.93.31351", + "templateHash": "3939012368833498791" }, "description": "Creates or updates an existing Azure Container App." }, @@ -1872,8 +1872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17060328277181249956" + "version": "0.33.93.31351", + "templateHash": "16032798261150015163" }, "description": "Creates a container app in an Azure Container App environment." }, @@ -2120,8 +2120,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8858194042814084678" + "version": "0.33.93.31351", + "templateHash": "9291339074123332314" } }, "parameters": { @@ -2346,8 +2346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14863520980520874403" + "version": "0.33.93.31351", + "templateHash": "13409304173345641366" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -2381,7 +2381,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -2454,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3099268077942840517" + "version": "0.33.93.31351", + "templateHash": "10077950452960822085" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -2515,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -2696,8 +2696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -2774,8 +2774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -2826,8 +2826,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -2907,8 +2907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2972,8 +2972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, @@ -4283,8 +4283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13208236383464324805" + "version": "0.33.93.31351", + "templateHash": "12940223009526209631" }, "description": "Creates an Azure API Management instance." }, @@ -4433,8 +4433,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1585643982326260567" + "version": "0.33.93.31351", + "templateHash": "1777775096889621317" } }, "parameters": { diff --git a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json index 776fc494..91130a6a 100644 --- a/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json +++ b/Environments/Todo-Nodejs-Mongo-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17166396968645210258" + "version": "0.33.93.31351", + "templateHash": "11455208835963135169" } }, "parameters": { @@ -242,8 +242,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2587101185242437995" + "version": "0.33.93.31351", + "templateHash": "9332925989989495767" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -593,8 +593,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18277892820278517058" + "version": "0.33.93.31351", + "templateHash": "10410905278989344027" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -874,8 +874,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8942625910471136519" + "version": "0.33.93.31351", + "templateHash": "12676601876476438690" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -937,8 +937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -1107,8 +1107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1167,8 +1167,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15583442179894633871" + "version": "0.33.93.31351", + "templateHash": "17340621863331831612" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1225,8 +1225,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17016224891593731674" + "version": "0.33.93.31351", + "templateHash": "1623829276601313671" }, "description": "Assigns an Azure Key Vault access policy." }, @@ -1341,8 +1341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8858194042814084678" + "version": "0.33.93.31351", + "templateHash": "9291339074123332314" } }, "parameters": { @@ -1422,8 +1422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14863520980520874403" + "version": "0.33.93.31351", + "templateHash": "13409304173345641366" }, "description": "Creates an Azure Cosmos DB for MongoDB account with a database." }, @@ -1457,7 +1457,7 @@ "resources": [ { "copy": { - "name": "list", + "name": "database::list", "count": "[length(parameters('collections'))]" }, "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", @@ -1530,8 +1530,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3099268077942840517" + "version": "0.33.93.31351", + "templateHash": "10077950452960822085" }, "description": "Creates an Azure Cosmos DB for MongoDB account." }, @@ -1591,8 +1591,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5295350533403586059" + "version": "0.33.93.31351", + "templateHash": "7546451053952178944" }, "description": "Creates an Azure Cosmos DB account." }, @@ -1772,8 +1772,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4250008929992735853" + "version": "0.33.93.31351", + "templateHash": "14225036677466865562" }, "description": "Creates an Azure Key Vault." }, @@ -1850,8 +1850,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1902,8 +1902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -1983,8 +1983,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -2048,8 +2048,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-ACA/azuredeploy.json b/Environments/Todo-Shared-ACA/azuredeploy.json index 372b041a..1516751d 100644 --- a/Environments/Todo-Shared-ACA/azuredeploy.json +++ b/Environments/Todo-Shared-ACA/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9446336477668144168" + "version": "0.33.93.31351", + "templateHash": "17842367933944312587" } }, "parameters": { @@ -220,8 +220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3334036771818682335" + "version": "0.33.93.31351", + "templateHash": "2087685049550457930" }, "description": "Creates an Azure Container Registry and an Azure Container Apps environment." }, @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6562902683232219313" + "version": "0.33.93.31351", + "templateHash": "181961455962728932" }, "description": "Creates an Azure Container Apps environment." }, @@ -402,8 +402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -535,8 +535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/Todo-Shared-AKS/azuredeploy.json b/Environments/Todo-Shared-AKS/azuredeploy.json index 6061998e..67d21420 100644 --- a/Environments/Todo-Shared-AKS/azuredeploy.json +++ b/Environments/Todo-Shared-AKS/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12592237793073238850" + "version": "0.33.93.31351", + "templateHash": "15952093211390305974" } }, "parameters": { @@ -221,8 +221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15447839188459827691" + "version": "0.33.93.31351", + "templateHash": "15428929372978620622" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool." }, @@ -566,8 +566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18277892820278517058" + "version": "0.33.93.31351", + "templateHash": "10410905278989344027" }, "description": "Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool." }, @@ -847,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8942625910471136519" + "version": "0.33.93.31351", + "templateHash": "12676601876476438690" }, "description": "Adds an agent pool to an Azure Kubernetes Service (AKS) cluster." }, @@ -910,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8075470313870608850" + "version": "0.33.93.31351", + "templateHash": "5601479367076195530" }, "description": "Creates an Azure Container Registry." }, @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1386551193288161196" + "version": "0.33.93.31351", + "templateHash": "18228807762088856507" }, "description": "Assigns ACR Pull permissions to access an Azure Container Registry." }, @@ -1140,8 +1140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15583442179894633871" + "version": "0.33.93.31351", + "templateHash": "17340621863331831612" }, "description": "Assigns RBAC role to the specified AKS cluster and principal." }, @@ -1238,8 +1238,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1577011052565158954" + "version": "0.33.93.31351", + "templateHash": "13646034712402643830" }, "description": "Creates an Application Insights instance and a Log Analytics workspace." }, @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4411803171372203725" + "version": "0.33.93.31351", + "templateHash": "10891050604045084701" }, "description": "Creates a Log Analytics workspace." }, @@ -1371,8 +1371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3510278625614104568" + "version": "0.33.93.31351", + "templateHash": "16963697269409392454" }, "description": "Creates an Application Insights instance based on an existing Log Analytics workspace." }, @@ -1436,8 +1436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14575977876683967619" + "version": "0.33.93.31351", + "templateHash": "7959658909084813949" }, "description": "Creates a dashboard for an Application Insights instance." }, diff --git a/Environments/WebApp/azuredeploy.json b/Environments/WebApp/azuredeploy.json index 2cc9b4df..881e480b 100644 --- a/Environments/WebApp/azuredeploy.json +++ b/Environments/WebApp/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4884715398047161988" + "version": "0.33.93.31351", + "templateHash": "1235614965940131441" } }, "parameters": { diff --git a/Environments/eShop/azuredeploy.json b/Environments/eShop/azuredeploy.json index 1073e875..0fac9f00 100644 --- a/Environments/eShop/azuredeploy.json +++ b/Environments/eShop/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11275726327917555436" + "version": "0.33.93.31351", + "templateHash": "4096480475685581125" } }, "parameters": { @@ -69,8 +69,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7492460148119304331" + "version": "0.33.93.31351", + "templateHash": "7010913536333183856" } }, "parameters": { From 164f5a53fdcd15d54cb149fe752ce77431a40488 Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 12 Mar 2025 22:54:07 +0800 Subject: [PATCH 110/112] add openai location --- Environments/OpenAISearch/azuredeploy.json | 117 +++++++++------------ Environments/OpenAISearch/main.bicep | 4 +- Environments/OpenAISearch/manifest.yaml | 7 ++ 3 files changed, 60 insertions(+), 68 deletions(-) diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 99ac4d4e..6183e711 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10268709855619543733" + "version": "0.25.53.49325", + "templateHash": "948045653933612696" } }, "parameters": { @@ -67,7 +67,7 @@ }, "openAiResourceGroupLocation": { "type": "string", - "defaultValue": "[parameters('location')]" + "defaultValue": "" }, "openAiSkuName": { "type": "string", @@ -87,19 +87,19 @@ }, "gptDeploymentName": { "type": "string", - "defaultValue": "davinci" + "defaultValue": "gpt-4o-0513" }, "gptModelName": { "type": "string", - "defaultValue": "text-davinci-003" + "defaultValue": "gpt-4o" }, - "chatGptDeploymentName": { + "modelVersion": { "type": "string", - "defaultValue": "chat" + "defaultValue": "2024-05-13" }, - "chatGptModelName": { + "chatGptDeploymentName": { "type": "string", - "defaultValue": "gpt-35-turbo" + "defaultValue": "chat" }, "principalId": { "type": "string", @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16934583072820463488" + "version": "0.25.53.49325", + "templateHash": "5484207409505496262" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1454693974758865459" + "version": "0.25.53.49325", + "templateHash": "13872150863523986003" } }, "parameters": { @@ -605,9 +605,7 @@ "mode": "Incremental", "parameters": { "name": "[if(not(empty(parameters('openAiServiceName'))), createObject('value', parameters('openAiServiceName')), createObject('value', format('{0}{1}', variables('abbrs').cognitiveServicesAccounts, variables('resourceToken'))))]", - "location": { - "value": "[parameters('openAiResourceGroupLocation')]" - }, + "location": "[if(not(empty(parameters('openAiResourceGroupLocation'))), createObject('value', parameters('openAiResourceGroupLocation')), createObject('value', parameters('location')))]", "tags": { "value": "[variables('tags')]" }, @@ -623,21 +621,10 @@ "model": { "format": "OpenAI", "name": "[parameters('gptModelName')]", - "version": "1" - }, - "scaleSettings": { - "scaleType": "Standard" - } - }, - { - "name": "[parameters('chatGptDeploymentName')]", - "model": { - "format": "OpenAI", - "name": "[parameters('chatGptModelName')]", - "version": "0301" + "version": "[parameters('modelVersion')]" }, - "scaleSettings": { - "scaleType": "Standard" + "sku": { + "name": "Standard" } } ] @@ -649,8 +636,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3406865708911674251" + "version": "0.25.53.49325", + "templateHash": "3135668651410476292" } }, "parameters": { @@ -691,7 +678,7 @@ "resources": [ { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -711,12 +698,11 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", "properties": { "model": "[parameters('deployments')[copyIndex()].model]", - "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]", - "scaleSettings": "[parameters('deployments')[copyIndex()].scaleSettings]" + "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]" }, "dependsOn": [ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" @@ -726,7 +712,7 @@ "outputs": { "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2022-10-01').endpoint]" + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2024-10-01').endpoint]" }, "id": { "type": "string", @@ -772,8 +758,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3406865708911674251" + "version": "0.25.53.49325", + "templateHash": "3135668651410476292" } }, "parameters": { @@ -814,7 +800,7 @@ "resources": [ { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -834,12 +820,11 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2022-10-01", + "apiVersion": "2024-10-01", "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", "properties": { "model": "[parameters('deployments')[copyIndex()].model]", - "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]", - "scaleSettings": "[parameters('deployments')[copyIndex()].scaleSettings]" + "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]" }, "dependsOn": [ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" @@ -849,7 +834,7 @@ "outputs": { "endpoint": { "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2022-10-01').endpoint]" + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2024-10-01').endpoint]" }, "id": { "type": "string", @@ -902,8 +887,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7596708093771990692" + "version": "0.25.53.49325", + "templateHash": "4262027139711048800" } }, "parameters": { @@ -1029,8 +1014,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8279939846364871047" + "version": "0.25.53.49325", + "templateHash": "10058363838811997106" } }, "parameters": { @@ -1201,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1265,8 +1250,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1329,8 +1314,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1393,8 +1378,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1457,8 +1442,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1521,8 +1506,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1585,8 +1570,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1652,8 +1637,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { @@ -1719,8 +1704,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727688037975652837" + "version": "0.25.53.49325", + "templateHash": "17557523373547944485" } }, "parameters": { diff --git a/Environments/OpenAISearch/main.bicep b/Environments/OpenAISearch/main.bicep index ac823d6d..049f3ba0 100644 --- a/Environments/OpenAISearch/main.bicep +++ b/Environments/OpenAISearch/main.bicep @@ -20,7 +20,7 @@ param storageResourceGroupLocation string = location param storageContainerName string = 'content' param openAiServiceName string = '' -param openAiResourceGroupLocation string = location +param openAiResourceGroupLocation string = '' param openAiSkuName string = 'S0' @@ -84,7 +84,7 @@ module openAi 'core/ai/cognitiveservices.bicep' = { name: 'openai' params: { name: !empty(openAiServiceName) ? openAiServiceName : '${abbrs.cognitiveServicesAccounts}${resourceToken}' - location: openAiResourceGroupLocation + location: !empty(openAiResourceGroupLocation) ? openAiResourceGroupLocation : location tags: tags sku: { name: openAiSkuName diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index 2a13d11b..c9edc408 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -67,3 +67,10 @@ parameters: type: "string" required: false default: "eastus" + +- id: "openaiLocation" + name: "Region (e.g. eastus)" + description: "Region" + type: "string" + required: false + default: "eastus" From 1a717cda5f516cd423f366d2e140380fe56d735f Mon Sep 17 00:00:00 2001 From: luxu-ms Date: Wed, 12 Mar 2025 15:02:25 +0000 Subject: [PATCH 111/112] Rebuild ARM templates --- Environments/OpenAISearch/azuredeploy.json | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Environments/OpenAISearch/azuredeploy.json b/Environments/OpenAISearch/azuredeploy.json index 6183e711..928c6f30 100644 --- a/Environments/OpenAISearch/azuredeploy.json +++ b/Environments/OpenAISearch/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "948045653933612696" + "version": "0.33.93.31351", + "templateHash": "6163857953714027108" } }, "parameters": { @@ -284,8 +284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5484207409505496262" + "version": "0.33.93.31351", + "templateHash": "10643458792052922105" } }, "parameters": { @@ -389,8 +389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "13872150863523986003" + "version": "0.33.93.31351", + "templateHash": "377501608988756920" } }, "parameters": { @@ -636,8 +636,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3135668651410476292" + "version": "0.33.93.31351", + "templateHash": "252417070114373195" } }, "parameters": { @@ -758,8 +758,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "3135668651410476292" + "version": "0.33.93.31351", + "templateHash": "252417070114373195" } }, "parameters": { @@ -887,8 +887,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4262027139711048800" + "version": "0.33.93.31351", + "templateHash": "3771478521284452856" } }, "parameters": { @@ -1014,8 +1014,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "10058363838811997106" + "version": "0.33.93.31351", + "templateHash": "11206818764871276945" } }, "parameters": { @@ -1097,7 +1097,7 @@ "resources": [ { "copy": { - "name": "container", + "name": "storage::blobServices::container", "count": "[length(parameters('containers'))]" }, "condition": "[not(empty(parameters('containers')))]", @@ -1186,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1250,8 +1250,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1314,8 +1314,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1378,8 +1378,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1442,8 +1442,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1506,8 +1506,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1570,8 +1570,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1637,8 +1637,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { @@ -1704,8 +1704,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "17557523373547944485" + "version": "0.33.93.31351", + "templateHash": "13051451403495570550" } }, "parameters": { From 6d1b0211d07ea80d8ca792fa994bc893194dabaf Mon Sep 17 00:00:00 2001 From: Lyle Xu Date: Wed, 12 Mar 2025 23:10:07 +0800 Subject: [PATCH 112/112] fix openai location mismatch --- Environments/OpenAISearch/manifest.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Environments/OpenAISearch/manifest.yaml b/Environments/OpenAISearch/manifest.yaml index c9edc408..d0432fd9 100644 --- a/Environments/OpenAISearch/manifest.yaml +++ b/Environments/OpenAISearch/manifest.yaml @@ -68,9 +68,9 @@ parameters: required: false default: "eastus" -- id: "openaiLocation" - name: "Region (e.g. eastus)" - description: "Region" +- id: "openAiResourceGroupLocation" + name: "OpenAI Region (e.g. eastus)" + description: "OpenAI Region (e.g. eastus)" type: "string" required: false default: "eastus"