From 04895cc13891ea04655b3753f9dba41984bf6f2f Mon Sep 17 00:00:00 2001 From: zedy Date: Tue, 25 Oct 2022 14:06:09 +0800 Subject: [PATCH 01/22] Azdevify eshopOnWeb --- .gitignore | 2 + azure.yaml | 14 + infra/abbreviations.json | 135 ++ infra/app/dbCatalog.bicep | 25 + infra/app/dbIdentity.bicep | 25 + infra/app/web.bicep | 18 + infra/core/database/sqlserver1.bicep | 131 ++ infra/core/database/sqlserver2.bicep | 131 ++ .../core/host/appservice-config-cosmos.bicep | 18 + infra/core/host/appservice-config-logs.bicep | 11 + .../host/appservice-config-sqlserver.bicep | 14 + infra/core/host/appservice-config-union.bicep | 9 + infra/core/host/appservice-dotnet.bicep | 35 + infra/core/host/appservice-node.bicep | 35 + infra/core/host/appservice-python.bicep | 35 + infra/core/host/appservice.bicep | 100 ++ .../core/host/appserviceplan-functions.bicep | 21 + infra/core/host/appserviceplan-sites.bicep | 15 + infra/core/host/appserviceplan.bicep | 23 + infra/core/host/container-app.bicep | 79 ++ .../host/container-apps-environment.bicep | 30 + infra/core/host/container-apps.bicep | 30 + infra/core/host/container-registry.bicep | 40 + infra/core/host/functions-node.bicep | 34 + infra/core/host/functions-python.bicep | 34 + infra/core/host/functions.bicep | 58 + infra/core/host/staticwebapp.bicep | 25 + .../applicationinsights-dashboard.bicep | 1238 +++++++++++++++++ infra/core/monitor/applicationinsights.bicep | 30 + infra/core/monitor/loganalytics.bicep | 24 + infra/core/monitor/monitoring.bicep | 24 + infra/core/security/keyvault-access.bicep | 25 + infra/core/security/keyvault.bicep | 29 + infra/core/storage/storage-account.bicep | 29 + infra/main.bicep | 46 + infra/main.parameters.json | 21 + infra/resources.bicep | 82 ++ 37 files changed, 2675 insertions(+) create mode 100644 azure.yaml create mode 100644 infra/abbreviations.json create mode 100644 infra/app/dbCatalog.bicep create mode 100644 infra/app/dbIdentity.bicep create mode 100644 infra/app/web.bicep create mode 100644 infra/core/database/sqlserver1.bicep create mode 100644 infra/core/database/sqlserver2.bicep create mode 100644 infra/core/host/appservice-config-cosmos.bicep create mode 100644 infra/core/host/appservice-config-logs.bicep create mode 100644 infra/core/host/appservice-config-sqlserver.bicep create mode 100644 infra/core/host/appservice-config-union.bicep create mode 100644 infra/core/host/appservice-dotnet.bicep create mode 100644 infra/core/host/appservice-node.bicep create mode 100644 infra/core/host/appservice-python.bicep create mode 100644 infra/core/host/appservice.bicep create mode 100644 infra/core/host/appserviceplan-functions.bicep create mode 100644 infra/core/host/appserviceplan-sites.bicep create mode 100644 infra/core/host/appserviceplan.bicep create mode 100644 infra/core/host/container-app.bicep create mode 100644 infra/core/host/container-apps-environment.bicep create mode 100644 infra/core/host/container-apps.bicep create mode 100644 infra/core/host/container-registry.bicep create mode 100644 infra/core/host/functions-node.bicep create mode 100644 infra/core/host/functions-python.bicep create mode 100644 infra/core/host/functions.bicep create mode 100644 infra/core/host/staticwebapp.bicep create mode 100644 infra/core/monitor/applicationinsights-dashboard.bicep create mode 100644 infra/core/monitor/applicationinsights.bicep create mode 100644 infra/core/monitor/loganalytics.bicep create mode 100644 infra/core/monitor/monitoring.bicep create mode 100644 infra/core/security/keyvault-access.bicep create mode 100644 infra/core/security/keyvault.bicep create mode 100644 infra/core/storage/storage-account.bicep create mode 100644 infra/main.bicep create mode 100644 infra/main.parameters.json create mode 100644 infra/resources.bicep diff --git a/.gitignore b/.gitignore index 1148ecd..2407635 100644 --- a/.gitignore +++ b/.gitignore @@ -257,3 +257,5 @@ pub/ #Ignore marker-file used to know which docker files we have. .eshopdocker_* + +.azure diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 0000000..da49708 --- /dev/null +++ b/azure.yaml @@ -0,0 +1,14 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +name: eShopOnWeb +services: + web: + project: ./src/Web + language: csharp + host: appservice +infra: + provider: "" + path: "" + module: "" +pipeline: + provider: "" diff --git a/infra/abbreviations.json b/infra/abbreviations.json new file mode 100644 index 0000000..a4fc9df --- /dev/null +++ b/infra/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/infra/app/dbCatalog.bicep b/infra/app/dbCatalog.bicep new file mode 100644 index 0000000..e320f63 --- /dev/null +++ b/infra/app/dbCatalog.bicep @@ -0,0 +1,25 @@ +param environmentName string +param location string = resourceGroup().location + +param databaseName string = 'CatalogDB' +param keyVaultName string + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +module sqlServer1 '../core/database/sqlserver1.bicep' = { + name: 'sqlServer1' + params: { + environmentName: environmentName + location: location + dbName: databaseName + keyVaultName: keyVaultName + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + } +} + +output sqlConnectionStringKey string = sqlServer1.outputs.sqlConnectionStringKey +output sqlDatabase1Name string = databaseName diff --git a/infra/app/dbIdentity.bicep b/infra/app/dbIdentity.bicep new file mode 100644 index 0000000..a9cb741 --- /dev/null +++ b/infra/app/dbIdentity.bicep @@ -0,0 +1,25 @@ +param environmentName string +param location string = resourceGroup().location + +param databaseName string = 'IdentityDB' +param keyVaultName string + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +module sqlServer2 '../core/database/sqlserver2.bicep' = { + name: 'sqlServer2' + params: { + environmentName: environmentName + location: location + dbName: databaseName + keyVaultName: keyVaultName + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + } +} + +output sqlConnectionStringKey string = sqlServer2.outputs.sqlConnectionStringKey +output sqlDatabase2Name string = databaseName diff --git a/infra/app/web.bicep b/infra/app/web.bicep new file mode 100644 index 0000000..828b282 --- /dev/null +++ b/infra/app/web.bicep @@ -0,0 +1,18 @@ +param environmentName string +param location string = resourceGroup().location +param appServicePlanId string + +param serviceName string = 'web' + +module web '../core/host/appservice-dotnet.bicep' = { + name: '${serviceName}-appservice-dotnet-module' + params: { + environmentName: environmentName + location: location + appServicePlanId: appServicePlanId + serviceName: serviceName + } +} + +output WEB_NAME string = web.outputs.name +output WEB_URI string = web.outputs.uri \ No newline at end of file diff --git a/infra/core/database/sqlserver1.bicep b/infra/core/database/sqlserver1.bicep new file mode 100644 index 0000000..671371c --- /dev/null +++ b/infra/core/database/sqlserver1.bicep @@ -0,0 +1,131 @@ +param environmentName string +param location string = resourceGroup().location + +param appUser string = 'appUser' +param dbName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param sqlConnectionStringKey string = 'AZURE-SQL-CATALOG-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { + name: '${abbrs.sqlServers}${resourceToken}-Catalog' + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: dbName + 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: 'script-${resourceToken}-Catalog' + 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: dbName + } + { + 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: sqlConnectionStringKey + properties: { + value: '${azureSqlConnectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var azureSqlConnectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output sqlConnectionStringKey string = sqlConnectionStringKey diff --git a/infra/core/database/sqlserver2.bicep b/infra/core/database/sqlserver2.bicep new file mode 100644 index 0000000..430f48f --- /dev/null +++ b/infra/core/database/sqlserver2.bicep @@ -0,0 +1,131 @@ +param environmentName string +param location string = resourceGroup().location + +param appUser string = 'appUser' +param dbName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param sqlConnectionStringKey string = 'AZURE-SQL-IDENTITY-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { + name: '${abbrs.sqlServers}${resourceToken}-Identity' + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: dbName + 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: 'script-${resourceToken}-Identity' + 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: dbName + } + { + 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: sqlConnectionStringKey + properties: { + value: '${azureSqlConnectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var azureSqlConnectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output sqlConnectionStringKey string = sqlConnectionStringKey diff --git a/infra/core/host/appservice-config-cosmos.bicep b/infra/core/host/appservice-config-cosmos.bicep new file mode 100644 index 0000000..65e91f9 --- /dev/null +++ b/infra/core/host/appservice-config-cosmos.bicep @@ -0,0 +1,18 @@ +param appServiceName string +param cosmosConnectionStringKey string = '' +param cosmosDatabaseName string = '' +param cosmosEndpoint string = '' + +module appServiceConfigCosmosSettings 'appservice-config-union.bicep' = { + name: '${appServiceName}-appservice-config-cosmos-settings' + params: { + appServiceName: appServiceName + configName: 'appsettings' + currentConfigProperties: list(resourceId('Microsoft.Web/sites/config', appServiceName, 'appsettings'), '2022-03-01').properties + additionalConfigProperties: { + AZURE_COSMOS_CONNECTION_STRING_KEY: cosmosConnectionStringKey + AZURE_COSMOS_DATABASE_NAME: cosmosDatabaseName + AZURE_COSMOS_ENDPOINT: cosmosEndpoint + } + } +} diff --git a/infra/core/host/appservice-config-logs.bicep b/infra/core/host/appservice-config-logs.bicep new file mode 100644 index 0000000..fde8940 --- /dev/null +++ b/infra/core/host/appservice-config-logs.bicep @@ -0,0 +1,11 @@ +param appServiceName string + +resource siteConfigLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: '${appServiceName}/logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } +} diff --git a/infra/core/host/appservice-config-sqlserver.bicep b/infra/core/host/appservice-config-sqlserver.bicep new file mode 100644 index 0000000..3d8c8d0 --- /dev/null +++ b/infra/core/host/appservice-config-sqlserver.bicep @@ -0,0 +1,14 @@ +param appServiceName string +param sqlConnectionStringKey string + +module appServiceConfigSqlServerSettings 'appservice-config-union.bicep' = { + name: '${appServiceName}-appservice-config-sqlserver-settings' + params: { + appServiceName: appServiceName + configName: 'appsettings' + currentConfigProperties: list(resourceId('Microsoft.Web/sites/config', appServiceName, 'appsettings'), '2022-03-01').properties + additionalConfigProperties: { + AZURE_SQL_CONNECTION_STRING_KEY: sqlConnectionStringKey + } + } +} diff --git a/infra/core/host/appservice-config-union.bicep b/infra/core/host/appservice-config-union.bicep new file mode 100644 index 0000000..3e0bcee --- /dev/null +++ b/infra/core/host/appservice-config-union.bicep @@ -0,0 +1,9 @@ +param additionalConfigProperties object +param appServiceName string +param configName string +param currentConfigProperties object + +resource siteConfigUnion 'Microsoft.Web/sites/config@2022-03-01' = { + name: '${appServiceName}/${configName}' + properties: union(currentConfigProperties, additionalConfigProperties) +} diff --git a/infra/core/host/appservice-dotnet.bicep b/infra/core/host/appservice-dotnet.bicep new file mode 100644 index 0000000..012a452 --- /dev/null +++ b/infra/core/host/appservice-dotnet.bicep @@ -0,0 +1,35 @@ +param environmentName string +param location string = resourceGroup().location + +param allowedOrigins array = [] +param appCommandLine string = '' +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param keyVaultName string = '' +param linuxFxVersion string = 'DOTNETCORE|6.0' +param managedIdentity bool = !(empty(keyVaultName)) +param scmDoBuildDuringDeployment bool = false +param serviceName string + +module appService 'appservice.bicep' = { + name: '${serviceName}-appservice-dotnet' + params: { + environmentName: environmentName + location: location + allowedOrigins: allowedOrigins + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: appSettings + keyVaultName: keyVaultName + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + serviceName: serviceName + } +} + +output identityPrincipalId string = appService.outputs.identityPrincipalId +output name string = appService.outputs.name +output uri string = appService.outputs.uri diff --git a/infra/core/host/appservice-node.bicep b/infra/core/host/appservice-node.bicep new file mode 100644 index 0000000..6f56d46 --- /dev/null +++ b/infra/core/host/appservice-node.bicep @@ -0,0 +1,35 @@ +param environmentName string +param location string = resourceGroup().location + +param allowedOrigins array = [] +param appCommandLine string = '' +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param keyVaultName string = '' +param linuxFxVersion string = 'NODE|16-lts' +param managedIdentity bool = !(empty(keyVaultName)) +param scmDoBuildDuringDeployment bool = false +param serviceName string + +module appService 'appservice.bicep' = { + name: '${serviceName}-appservice-node' + params: { + environmentName: environmentName + location: location + allowedOrigins: allowedOrigins + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: appSettings + keyVaultName: keyVaultName + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + serviceName: serviceName + } +} + +output identityPrincipalId string = appService.outputs.identityPrincipalId +output name string = appService.outputs.name +output uri string = appService.outputs.uri diff --git a/infra/core/host/appservice-python.bicep b/infra/core/host/appservice-python.bicep new file mode 100644 index 0000000..767c127 --- /dev/null +++ b/infra/core/host/appservice-python.bicep @@ -0,0 +1,35 @@ +param environmentName string +param location string = resourceGroup().location + +param allowedOrigins array = [] +param appCommandLine string = '' +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param keyVaultName string = '' +param linuxFxVersion string = 'PYTHON|3.8' +param managedIdentity bool = !(empty(keyVaultName)) +param scmDoBuildDuringDeployment bool = true +param serviceName string + +module appService 'appservice.bicep' = { + name: '${serviceName}-appservice-python' + params: { + environmentName: environmentName + location: location + allowedOrigins: allowedOrigins + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: appSettings + keyVaultName: keyVaultName + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + serviceName: serviceName + } +} + +output identityPrincipalId string = appService.outputs.identityPrincipalId +output name string = appService.outputs.name +output uri string = appService.outputs.uri diff --git a/infra/core/host/appservice.bicep b/infra/core/host/appservice.bicep new file mode 100644 index 0000000..3eece63 --- /dev/null +++ b/infra/core/host/appservice.bicep @@ -0,0 +1,100 @@ +param environmentName string +param location string = resourceGroup().location + +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param clientAffinityEnabled bool = false +param functionAppScaleLimit int = -1 +param keyVaultName string = '' +param kind string = 'app,linux' +param linuxFxVersion string = '' +param managedIdentity bool = !(empty(keyVaultName)) +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param serviceName string +param use32BitWorkerProcess bool = false + +var abbrs = loadJsonContent('../../abbreviations.json') +var tags = { 'azd-env-name': environmentName } +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) + +var prefix = contains(kind, 'function') ? abbrs.webSitesFunctions : abbrs.webSitesAppService + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: '${prefix}${serviceName}-${resourceToken}' + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + 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: managedIdentity ? { type: 'SystemAssigned' } : null + + resource appSettings 'config' = { + name: 'appsettings' + properties: union({ + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + }, + !(empty(applicationInsightsName)) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !(empty(keyVaultName)) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +module appSettingsUnion 'appservice-config-union.bicep' = if (!empty(appSettings)) { + name: '${serviceName}-app-settings-union' + params: { + appServiceName: appService.name + configName: 'appsettings' + currentConfigProperties: appService::appSettings.list().properties + additionalConfigProperties: appSettings + } +} + +module siteConfigLogs 'appservice-config-logs.bicep' = { + name: '${serviceName}-appservice-config-logs' + params: { + appServiceName: appService.name + } +} + +module keyVaultAccess '../security/keyvault-access.bicep' = if (!(empty(keyVaultName))) { + name: '${serviceName}-appservice-keyvault-access' + params: { + principalId: appService.identity.principalId + environmentName: environmentName + location: location + } +} + +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/infra/core/host/appserviceplan-functions.bicep b/infra/core/host/appserviceplan-functions.bicep new file mode 100644 index 0000000..6693e6b --- /dev/null +++ b/infra/core/host/appserviceplan-functions.bicep @@ -0,0 +1,21 @@ +param environmentName string +param location string = resourceGroup().location + +param sku object = { + name: 'Y1' + tier: 'Dynamic' + size: 'Y1' + family: 'Y' +} + +module appServicePlanFunctions 'appserviceplan.bicep' = { + name: 'appserviceplan-functions' + params: { + environmentName: environmentName + location: location + sku: sku + kind: 'functionapp' + } +} + +output appServicePlanId string = appServicePlanFunctions.outputs.appServicePlanId diff --git a/infra/core/host/appserviceplan-sites.bicep b/infra/core/host/appserviceplan-sites.bicep new file mode 100644 index 0000000..bd35137 --- /dev/null +++ b/infra/core/host/appserviceplan-sites.bicep @@ -0,0 +1,15 @@ +param environmentName string +param location string = resourceGroup().location + +param sku object = { name: 'B1' } + +module appServicePlanSites 'appserviceplan.bicep' = { + name: 'appserviceplan-sites' + params: { + environmentName: environmentName + location: location + sku: sku + } +} + +output appServicePlanId string = appServicePlanSites.outputs.appServicePlanId diff --git a/infra/core/host/appserviceplan.bicep b/infra/core/host/appserviceplan.bicep new file mode 100644 index 0000000..c72fcbd --- /dev/null +++ b/infra/core/host/appserviceplan.bicep @@ -0,0 +1,23 @@ +param environmentName string +param location string = resourceGroup().location + +param kind string = '' +param reserved bool = true +param sku object + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: '${abbrs.webServerFarms}${resourceToken}' + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output appServicePlanId string = appServicePlan.id diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep new file mode 100644 index 0000000..fdfcf67 --- /dev/null +++ b/infra/core/host/container-app.bicep @@ -0,0 +1,79 @@ +param environmentName string +param location string = resourceGroup().location + +param containerAppsEnvironmentName string = '' +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 +param serviceName string + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource app 'Microsoft.App/containerApps@2022-03-01' = { + name: '${abbrs.appContainerApps}${serviceName}-${resourceToken}' + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + identity: managedIdentity ? { type: 'SystemAssigned' } : null + 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: 'main' + env: env + } + ] + } + } +} + +module keyVaultAccess '../security/keyvault-access.bicep' = if (!(empty(keyVaultName))) { + name: '${serviceName}-appservice-keyvault-access' + params: { + environmentName: environmentName + location: location + keyVaultName: keyVaultName + principalId: app.identity.principalId + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { + name: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' +} + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' +} + +output identityPrincipalId string = managedIdentity ? app.identity.principalId : '' +output name string = app.name +output uri string = 'https://${app.properties.configuration.ingress.fqdn}' diff --git a/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep new file mode 100644 index 0000000..b1e0578 --- /dev/null +++ b/infra/core/host/container-apps-environment.bicep @@ -0,0 +1,30 @@ +param environmentName string +param location string = resourceGroup().location + +param containerAppsEnvironmentName string = '' +param logAnalyticsWorkspaceName string + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' = { + name: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = { + name: logAnalyticsWorkspaceName +} + +output containerAppsEnvironmentName string = containerAppsEnvironment.name diff --git a/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep new file mode 100644 index 0000000..a916c7f --- /dev/null +++ b/infra/core/host/container-apps.bicep @@ -0,0 +1,30 @@ +param environmentName string +param location string = resourceGroup().location + +param containerAppsEnvironmentName string = '' +param containerAppsGroupName string = 'app' +param containerRegistryName string = '' +param logAnalyticsWorkspaceName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${containerAppsGroupName}-container-apps-environment' + params: { + environmentName: environmentName + location: location + containerAppsEnvironmentName: containerAppsEnvironmentName + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${containerAppsGroupName}-container-registry' + params: { + environmentName: environmentName + location: location + containerRegistryName: containerRegistryName + } +} + +output containerAppsEnvironmentName string = containerAppsEnvironment.outputs.containerAppsEnvironmentName +output containerRegistryEndpoint string = containerRegistry.outputs.containerRegistryEndpoint +output containerRegistryName string = containerRegistry.outputs.containerRegistryName diff --git a/infra/core/host/container-registry.bicep b/infra/core/host/container-registry.bicep new file mode 100644 index 0000000..115da50 --- /dev/null +++ b/infra/core/host/container-registry.bicep @@ -0,0 +1,40 @@ +param environmentName string +param location string = resourceGroup().location + +param adminUserEnabled bool = true +param anonymousPullEnabled bool = false +param containerRegistryName string = '' +param dataEndpointEnabled bool = false +param encryption object = { + status: 'disabled' +} +param networkRuleBypassOptions string = 'AzureServices' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'Standard' +} +param zoneRedundancy string = 'Disabled' + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +output containerRegistryEndpoint string = containerRegistry.properties.loginServer +output containerRegistryName string = containerRegistry.name diff --git a/infra/core/host/functions-node.bicep b/infra/core/host/functions-node.bicep new file mode 100644 index 0000000..e4731f4 --- /dev/null +++ b/infra/core/host/functions-node.bicep @@ -0,0 +1,34 @@ +param environmentName string +param location string = resourceGroup().location + +param allowedOrigins array = [] +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param keyVaultName string = '' +param linuxFxVersion string = 'NODE|16' +param managedIdentity bool = !(empty(keyVaultName)) +param serviceName string +param storageAccountName string + +module functions 'functions.bicep' = { + name: '${serviceName}-functions-node' + params: { + environmentName: environmentName + location: location + allowedOrigins: allowedOrigins + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: appSettings + functionsWorkerRuntime: 'node' + keyVaultName: keyVaultName + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + serviceName: serviceName + storageAccountName: storageAccountName + } +} + +output identityPrincipalId string = functions.outputs.identityPrincipalId +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/infra/core/host/functions-python.bicep b/infra/core/host/functions-python.bicep new file mode 100644 index 0000000..2ff79bd --- /dev/null +++ b/infra/core/host/functions-python.bicep @@ -0,0 +1,34 @@ +param environmentName string +param location string = resourceGroup().location + +param allowedOrigins array = [] +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param keyVaultName string = '' +param linuxFxVersion string = 'PYTHON|3.8' +param managedIdentity bool = !(empty(keyVaultName)) +param serviceName string +param storageAccountName string + +module functions 'functions.bicep' = { + name: '${serviceName}-functions-python' + params: { + environmentName: environmentName + location: location + allowedOrigins: allowedOrigins + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: appSettings + functionsWorkerRuntime: 'python' + keyVaultName: keyVaultName + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + serviceName: serviceName + storageAccountName: storageAccountName + } +} + +output identityPrincipalId string = functions.outputs.identityPrincipalId +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/infra/core/host/functions.bicep b/infra/core/host/functions.bicep new file mode 100644 index 0000000..818485f --- /dev/null +++ b/infra/core/host/functions.bicep @@ -0,0 +1,58 @@ +param environmentName string +param location string = resourceGroup().location + +param allowedOrigins array = [] +param alwaysOn bool = false +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} +param clientAffinityEnabled bool = false +param functionAppScaleLimit int = 200 +param functionsExtensionVersion string = '~4' +param functionsWorkerRuntime string +param kind string = 'functionapp,linux' +param linuxFxVersion string = '' +param keyVaultName string = '' +param managedIdentity bool = !(empty(keyVaultName)) +param minimumElasticInstanceCount int = 0 +param numberOfWorkers int = 1 +param scmDoBuildDuringDeployment bool = true +param serviceName string +param storageAccountName string +param use32BitWorkerProcess bool = false + +module functions 'appservice.bicep' = { + name: '${serviceName}-functions' + params: { + environmentName: environmentName + location: location + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + 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: functionsExtensionVersion + FUNCTIONS_WORKER_RUNTIME: functionsWorkerRuntime + }) + clientAffinityEnabled: clientAffinityEnabled + functionAppScaleLimit: functionAppScaleLimit + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + serviceName: serviceName + 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/infra/core/host/staticwebapp.bicep b/infra/core/host/staticwebapp.bicep new file mode 100644 index 0000000..3537fa9 --- /dev/null +++ b/infra/core/host/staticwebapp.bicep @@ -0,0 +1,25 @@ +param environmentName string +param location string = resourceGroup().location + +param serviceName string +param sku object = { + name: 'Free' + tier: 'Free' +} + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: '${abbrs.webStaticSites}${serviceName}-${resourceToken}' + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/infra/core/monitor/applicationinsights-dashboard.bicep b/infra/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 0000000..11a6512 --- /dev/null +++ b/infra/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1238 @@ +param environmentName string +param location string = resourceGroup().location +param applicationInsightsName string + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: '${abbrs.portalDashboards}${resourceToken}' + 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/infra/core/monitor/applicationinsights.bicep b/infra/core/monitor/applicationinsights.bicep new file mode 100644 index 0000000..b9ce1fd --- /dev/null +++ b/infra/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +param environmentName string +param location string = resourceGroup().location +param logAnalyticsWorkspaceId string + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: '${abbrs.insightsComponents}${resourceToken}' + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { + name: 'application-insights-dashboard' + params: { + environmentName: environmentName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output applicationInsightsConnectionString string = applicationInsights.properties.ConnectionString +output applicationInsightsName string = applicationInsights.name diff --git a/infra/core/monitor/loganalytics.bicep b/infra/core/monitor/loganalytics.bicep new file mode 100644 index 0000000..a36912c --- /dev/null +++ b/infra/core/monitor/loganalytics.bicep @@ -0,0 +1,24 @@ +param environmentName string +param location string = resourceGroup().location + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output logAnalyticsWorkspaceId string = logAnalytics.id +output logAnalyticsWorkspaceName string = logAnalytics.name diff --git a/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep new file mode 100644 index 0000000..1c5ae3e --- /dev/null +++ b/infra/core/monitor/monitoring.bicep @@ -0,0 +1,24 @@ +param environmentName string +param location string = resourceGroup().location + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + environmentName: environmentName + location: location + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + environmentName: environmentName + location: location + logAnalyticsWorkspaceId: logAnalytics.outputs.logAnalyticsWorkspaceId + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.applicationInsightsConnectionString +output applicationInsightsName string = applicationInsights.outputs.applicationInsightsName +output logAnalyticsWorkspaceId string = logAnalytics.outputs.logAnalyticsWorkspaceId +output logAnalyticsWorkspaceName string = logAnalytics.outputs.logAnalyticsWorkspaceName diff --git a/infra/core/security/keyvault-access.bicep b/infra/core/security/keyvault-access.bicep new file mode 100644 index 0000000..30c00f4 --- /dev/null +++ b/infra/core/security/keyvault-access.bicep @@ -0,0 +1,25 @@ +param environmentName string +param location string = resourceGroup().location + +param keyVaultName string = '' +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: 'add' + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' +} diff --git a/infra/core/security/keyvault.bicep b/infra/core/security/keyvault.bicep new file mode 100644 index 0000000..96b8e84 --- /dev/null +++ b/infra/core/security/keyvault.bicep @@ -0,0 +1,29 @@ +param environmentName string +param location string = resourceGroup().location + +param keyVaultName string = '' +param principalId string = '' + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + 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 keyVaultEndpoint string = keyVault.properties.vaultUri +output keyVaultName string = keyVault.name diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep new file mode 100644 index 0000000..7d2eb91 --- /dev/null +++ b/infra/core/storage/storage-account.bicep @@ -0,0 +1,29 @@ +param environmentName string +param location string = resourceGroup().location + +param allowBlobPublicAccess bool = false +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param sku object = { name: 'Standard_LRS' } + +var abbrs = loadJsonContent('../../abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: '${abbrs.storageStorageAccounts}${resourceToken}' + location: location + tags: tags + kind: kind + sku: sku + properties: { + minimumTlsVersion: minimumTlsVersion + allowBlobPublicAccess: allowBlobPublicAccess + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Allow' + } + } +} + +output name string = storage.name diff --git a/infra/main.bicep b/infra/main.bicep new file mode 100644 index 0000000..a47a36f --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,46 @@ +targetScope = 'subscription' + +@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('Id of the user or app to assign application roles') +param principalId string = '' + +@secure() +@description('SQL Server administrator password') +param sqlAdminPassword string + +@secure() +@description('Application user password') +param appUserPassword string + +var abbrs = loadJsonContent('./abbreviations.json') +var tags = { 'azd-env-name': environmentName } + +resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: '${abbrs.resourcesResourceGroups}${environmentName}' + location: location + tags: tags +} + +module resources 'resources.bicep' = { + name: 'resources' + scope: rg + params: { + environmentName: environmentName + location: location + principalId: principalId + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + } +} + +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output REACT_APP_WEB_BASE_URL string = resources.outputs.WEB_URI diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 0000000..0ef1d97 --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,21 @@ +{ + "$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}" + }, + "sqlAdminPassword": { + "value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} sqlAdminPassword)" + }, + "appUserPassword": { + "value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} appUserPassword)" + } + } +} \ No newline at end of file diff --git a/infra/resources.bicep b/infra/resources.bicep new file mode 100644 index 0000000..0eafb44 --- /dev/null +++ b/infra/resources.bicep @@ -0,0 +1,82 @@ +param environmentName string +param location string = resourceGroup().location +param principalId string = '' + +@secure() +param sqlAdminPassword string + +@secure() +param appUserPassword string + +// The application frontend +module web './app/web.bicep' = { + name: 'web' + params: { + environmentName: environmentName + location: location + appServicePlanId: appServicePlan.outputs.appServicePlanId + } +} + +// The application database: Catalog +module sqlServer1 './app/dbCatalog.bicep' = { + name: 'sqlCatalog' + params: { + environmentName: environmentName + location: location + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + keyVaultName: keyVault.outputs.keyVaultName + } +} + +// The application database: Identity +module sqlServer2 './app/dbIdentity.bicep' = { + name: 'sqlIdentity' + params: { + environmentName: environmentName + location: location + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + keyVaultName: keyVault.outputs.keyVaultName + } +} + +// Configure web to use sqlCatalog +module apiSqlServerConfig1 './core/host/appservice-config-sqlserver.bicep' = { + name: 'web-sqlserver-config-1' + params: { + appServiceName: web.outputs.WEB_NAME + sqlConnectionStringKey: sqlServer1.outputs.sqlConnectionStringKey + } +} + +// Configure web to use sqlIdentity +module apiSqlServerConfig2 './core/host/appservice-config-sqlserver.bicep' = { + name: 'web-sqlserver-config-2' + params: { + appServiceName: web.outputs.WEB_NAME + sqlConnectionStringKey: sqlServer2.outputs.sqlConnectionStringKey + } +} + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + params: { + environmentName: environmentName + location: location + principalId: principalId + } +} + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan './core/host/appserviceplan-sites.bicep' = { + name: 'appserviceplan' + params: { + environmentName: environmentName + location: location + } +} + +output WEB_URI string = web.outputs.WEB_URI From c7831749337784e49ddff559a1987a12bfee4d2c Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 18 Nov 2022 10:23:31 +0800 Subject: [PATCH 02/22] Azdev-ify changes for latest /bicep/core --- src/Web/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Web/appsettings.json b/src/Web/appsettings.json index 70989a6..4f5c0ed 100644 --- a/src/Web/appsettings.json +++ b/src/Web/appsettings.json @@ -4,8 +4,8 @@ "webBase": "https://localhost:44315/" }, "ConnectionStrings": { - "CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;", - "IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;" + "CatalogConnection": "Server=sqlserver-catalog-01.database.windows.net; Database=Todo; User=appUser; Password=fACZr419yzJ19tA", + "IdentityConnection": "Server=sqlserver-identity-01.database.windows.net; Database=Todo; User=appUser; Password=fACZr419yzJ19tA" }, "CatalogBaseUrl": "", "Logging": { From 88a27783a1e0615fcee74d0852c5aa0b21c1a07d Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 18 Nov 2022 10:25:09 +0800 Subject: [PATCH 03/22] Azdev-ify changes for latest /bicep/core --- .gitignore | 1 + azure.yaml | 6 - infra/app/catalog-db.bicep | 31 +++++ infra/app/dbCatalog.bicep | 25 ---- infra/app/dbIdentity.bicep | 25 ---- infra/app/identity-db.bicep | 31 +++++ infra/app/web.bicep | 23 ++-- .../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 ++++ infra/core/database/sqlserver/sqlserver.bicep | 129 ++++++++++++++++++ .../database/{ => sqlserver}/sqlserver1.bicep | 30 ++-- .../database/{ => sqlserver}/sqlserver2.bicep | 30 ++-- .../core/host/appservice-config-cosmos.bicep | 18 --- infra/core/host/appservice-config-logs.bicep | 11 -- .../host/appservice-config-sqlserver.bicep | 14 -- infra/core/host/appservice-config-union.bicep | 9 -- infra/core/host/appservice-dotnet.bicep | 35 ----- infra/core/host/appservice-node.bicep | 35 ----- infra/core/host/appservice-python.bicep | 35 ----- infra/core/host/appservice.bicep | 89 ++++++------ .../core/host/appserviceplan-functions.bicep | 21 --- infra/core/host/appserviceplan-sites.bicep | 15 -- infra/core/host/appserviceplan.bicep | 11 +- infra/core/host/container-app.bicep | 42 +++--- .../host/container-apps-environment.bicep | 14 +- infra/core/host/container-apps.bicep | 22 +-- infra/core/host/container-registry.bicep | 16 +-- infra/core/host/functions-node.bicep | 34 ----- infra/core/host/functions-python.bicep | 34 ----- infra/core/host/functions.bicep | 62 ++++++--- infra/core/host/staticwebapp.bicep | 12 +- .../applicationinsights-dashboard.bicep | 11 +- infra/core/monitor/applicationinsights.bicep | 18 +-- infra/core/monitor/loganalytics.bicep | 13 +- infra/core/monitor/monitoring.bicep | 23 ++-- infra/core/security/keyvault-access.bicep | 10 +- infra/core/security/keyvault.bicep | 14 +- infra/core/storage/storage-account.bicep | 23 +++- infra/main.bicep | 84 +++++++++++- infra/resources.bicep | 82 ----------- 45 files changed, 762 insertions(+), 633 deletions(-) create mode 100644 infra/app/catalog-db.bicep delete mode 100644 infra/app/dbCatalog.bicep delete mode 100644 infra/app/dbIdentity.bicep create mode 100644 infra/app/identity-db.bicep create mode 100644 infra/core/database/cosmos/cosmos-account.bicep create mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 infra/core/database/sqlserver/sqlserver.bicep rename infra/core/database/{ => sqlserver}/sqlserver1.bicep (76%) rename infra/core/database/{ => sqlserver}/sqlserver2.bicep (76%) delete mode 100644 infra/core/host/appservice-config-cosmos.bicep delete mode 100644 infra/core/host/appservice-config-logs.bicep delete mode 100644 infra/core/host/appservice-config-sqlserver.bicep delete mode 100644 infra/core/host/appservice-config-union.bicep delete mode 100644 infra/core/host/appservice-dotnet.bicep delete mode 100644 infra/core/host/appservice-node.bicep delete mode 100644 infra/core/host/appservice-python.bicep delete mode 100644 infra/core/host/appserviceplan-functions.bicep delete mode 100644 infra/core/host/appserviceplan-sites.bicep delete mode 100644 infra/core/host/functions-node.bicep delete mode 100644 infra/core/host/functions-python.bicep delete mode 100644 infra/resources.bicep diff --git a/.gitignore b/.gitignore index 2407635..91a682b 100644 --- a/.gitignore +++ b/.gitignore @@ -257,5 +257,6 @@ pub/ #Ignore marker-file used to know which docker files we have. .eshopdocker_* +.devcontainer .azure diff --git a/azure.yaml b/azure.yaml index da49708..9590449 100644 --- a/azure.yaml +++ b/azure.yaml @@ -6,9 +6,3 @@ services: project: ./src/Web language: csharp host: appservice -infra: - provider: "" - path: "" - module: "" -pipeline: - provider: "" diff --git a/infra/app/catalog-db.bicep b/infra/app/catalog-db.bicep new file mode 100644 index 0000000..631f138 --- /dev/null +++ b/infra/app/catalog-db.bicep @@ -0,0 +1,31 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param databaseName string = 'CatalogDB' +param keyVaultName string + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword 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 sqlServer1 '../core/database/sqlserver/sqlserver1.bicep' = { + name: 'sqlServer01' + params: { + name: name + location: location + tags: tags + databaseName: actualDatabaseName + keyVaultName: keyVaultName + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + } +} + +output sqlCatalogConnectionStringKey string = sqlServer1.outputs.connectionStringKey +output sqlCatalogDatabase1Name string = sqlServer1.outputs.databaseName diff --git a/infra/app/dbCatalog.bicep b/infra/app/dbCatalog.bicep deleted file mode 100644 index e320f63..0000000 --- a/infra/app/dbCatalog.bicep +++ /dev/null @@ -1,25 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param databaseName string = 'CatalogDB' -param keyVaultName string - -@secure() -param sqlAdminPassword string -@secure() -param appUserPassword string - -module sqlServer1 '../core/database/sqlserver1.bicep' = { - name: 'sqlServer1' - params: { - environmentName: environmentName - location: location - dbName: databaseName - keyVaultName: keyVaultName - sqlAdminPassword: sqlAdminPassword - appUserPassword: appUserPassword - } -} - -output sqlConnectionStringKey string = sqlServer1.outputs.sqlConnectionStringKey -output sqlDatabase1Name string = databaseName diff --git a/infra/app/dbIdentity.bicep b/infra/app/dbIdentity.bicep deleted file mode 100644 index a9cb741..0000000 --- a/infra/app/dbIdentity.bicep +++ /dev/null @@ -1,25 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param databaseName string = 'IdentityDB' -param keyVaultName string - -@secure() -param sqlAdminPassword string -@secure() -param appUserPassword string - -module sqlServer2 '../core/database/sqlserver2.bicep' = { - name: 'sqlServer2' - params: { - environmentName: environmentName - location: location - dbName: databaseName - keyVaultName: keyVaultName - sqlAdminPassword: sqlAdminPassword - appUserPassword: appUserPassword - } -} - -output sqlConnectionStringKey string = sqlServer2.outputs.sqlConnectionStringKey -output sqlDatabase2Name string = databaseName diff --git a/infra/app/identity-db.bicep b/infra/app/identity-db.bicep new file mode 100644 index 0000000..1673c53 --- /dev/null +++ b/infra/app/identity-db.bicep @@ -0,0 +1,31 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param databaseName string = 'IdentityDB' +param keyVaultName string + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword 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 sqlServer2 '../core/database/sqlserver/sqlserver2.bicep' = { + name: 'sqlServer02' + params: { + name: name + location: location + tags: tags + databaseName: actualDatabaseName + keyVaultName: keyVaultName + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + } +} + +output sqlCatalogConnectionStringKey string = sqlServer2.outputs.connectionStringKey +output sqlCatalogDatabase1Name string = sqlServer2.outputs.databaseName diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 828b282..f33a276 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -1,18 +1,23 @@ -param environmentName string +param name string param location string = resourceGroup().location -param appServicePlanId string - +param tags object = {} param serviceName string = 'web' +param appCommandLine string = 'pm2 serve /home/site/wwwroot --no-daemon --spa' +param applicationInsightsName string = '' +param appServicePlanId string +param appSettings object = {} -module web '../core/host/appservice-dotnet.bicep' = { - name: '${serviceName}-appservice-dotnet-module' +module web '../core/host/appservice.bicep' = { + name: '${name}-deployment' params: { - environmentName: environmentName + name: name location: location appServicePlanId: appServicePlanId - serviceName: serviceName + runtimeName: 'dotnetcore' + runtimeVersion: '6.0' + tags: union(tags, { 'azd-service-name': serviceName }) + scmDoBuildDuringDeployment: false } } -output WEB_NAME string = web.outputs.name -output WEB_URI string = web.outputs.uri \ No newline at end of file +output REACT_APP_WEB_BASE_URL string = web.outputs.uri diff --git a/infra/core/database/cosmos/cosmos-account.bicep b/infra/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 0000000..6bc1f2e --- /dev/null +++ b/infra/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/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 0000000..bd2a2b5 --- /dev/null +++ b/infra/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/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 0000000..2c9688e --- /dev/null +++ b/infra/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/infra/core/database/cosmos/sql/cosmos-sql-account.bicep b/infra/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 0000000..e8b030f --- /dev/null +++ b/infra/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/infra/core/database/cosmos/sql/cosmos-sql-db.bicep b/infra/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 0000000..5a4de20 --- /dev/null +++ b/infra/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/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 0000000..6855edf --- /dev/null +++ b/infra/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/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 0000000..cfb4033 --- /dev/null +++ b/infra/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/infra/core/database/sqlserver/sqlserver.bicep b/infra/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 0000000..821a908 --- /dev/null +++ b/infra/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/infra/core/database/sqlserver1.bicep b/infra/core/database/sqlserver/sqlserver1.bicep similarity index 76% rename from infra/core/database/sqlserver1.bicep rename to infra/core/database/sqlserver/sqlserver1.bicep index 671371c..891aa45 100644 --- a/infra/core/database/sqlserver1.bicep +++ b/infra/core/database/sqlserver/sqlserver1.bicep @@ -1,23 +1,20 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} param appUser string = 'appUser' -param dbName string +param databaseName string param keyVaultName string param sqlAdmin string = 'sqlAdmin' -param sqlConnectionStringKey string = 'AZURE-SQL-CATALOG-CONNECTION-STRING' +param connectionStringKey string = 'AZURE-SQL-CATALOG-CONNECTION-STRING' @secure() param sqlAdminPassword string @secure() param appUserPassword string -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - -resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { - name: '${abbrs.sqlServers}${resourceToken}-Catalog' +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name location: location tags: tags properties: { @@ -29,7 +26,7 @@ resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { } resource database 'databases' = { - name: dbName + name: databaseName location: location } @@ -46,7 +43,7 @@ resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { } resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { - name: 'script-${resourceToken}-Catalog' + name: '${name}-deployment-script' location: location kind: 'AzureCLI' properties: { @@ -65,7 +62,7 @@ resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' } { name: 'DBNAME' - value: dbName + value: databaseName } { name: 'DBSERVER' @@ -117,9 +114,9 @@ resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: sqlConnectionStringKey + name: connectionStringKey properties: { - value: '${azureSqlConnectionString}; Password=${appUserPassword}' + value: '${connectionString}; Password=${appUserPassword}' } } @@ -127,5 +124,6 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { name: keyVaultName } -var azureSqlConnectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' -output sqlConnectionStringKey string = sqlConnectionStringKey +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/infra/core/database/sqlserver2.bicep b/infra/core/database/sqlserver/sqlserver2.bicep similarity index 76% rename from infra/core/database/sqlserver2.bicep rename to infra/core/database/sqlserver/sqlserver2.bicep index 430f48f..305c97b 100644 --- a/infra/core/database/sqlserver2.bicep +++ b/infra/core/database/sqlserver/sqlserver2.bicep @@ -1,23 +1,20 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} param appUser string = 'appUser' -param dbName string +param databaseName string param keyVaultName string param sqlAdmin string = 'sqlAdmin' -param sqlConnectionStringKey string = 'AZURE-SQL-IDENTITY-CONNECTION-STRING' +param connectionStringKey string = 'AZURE-SQL-IDENTITY-CONNECTION-STRING' @secure() param sqlAdminPassword string @secure() param appUserPassword string -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - -resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { - name: '${abbrs.sqlServers}${resourceToken}-Identity' +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name location: location tags: tags properties: { @@ -29,7 +26,7 @@ resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { } resource database 'databases' = { - name: dbName + name: databaseName location: location } @@ -46,7 +43,7 @@ resource sqlServer 'Microsoft.Sql/servers@2022-02-01-preview' = { } resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { - name: 'script-${resourceToken}-Identity' + name: '${name}-deployment-script' location: location kind: 'AzureCLI' properties: { @@ -65,7 +62,7 @@ resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' } { name: 'DBNAME' - value: dbName + value: databaseName } { name: 'DBSERVER' @@ -117,9 +114,9 @@ resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: sqlConnectionStringKey + name: connectionStringKey properties: { - value: '${azureSqlConnectionString}; Password=${appUserPassword}' + value: '${connectionString}; Password=${appUserPassword}' } } @@ -127,5 +124,6 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { name: keyVaultName } -var azureSqlConnectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' -output sqlConnectionStringKey string = sqlConnectionStringKey +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/infra/core/host/appservice-config-cosmos.bicep b/infra/core/host/appservice-config-cosmos.bicep deleted file mode 100644 index 65e91f9..0000000 --- a/infra/core/host/appservice-config-cosmos.bicep +++ /dev/null @@ -1,18 +0,0 @@ -param appServiceName string -param cosmosConnectionStringKey string = '' -param cosmosDatabaseName string = '' -param cosmosEndpoint string = '' - -module appServiceConfigCosmosSettings 'appservice-config-union.bicep' = { - name: '${appServiceName}-appservice-config-cosmos-settings' - params: { - appServiceName: appServiceName - configName: 'appsettings' - currentConfigProperties: list(resourceId('Microsoft.Web/sites/config', appServiceName, 'appsettings'), '2022-03-01').properties - additionalConfigProperties: { - AZURE_COSMOS_CONNECTION_STRING_KEY: cosmosConnectionStringKey - AZURE_COSMOS_DATABASE_NAME: cosmosDatabaseName - AZURE_COSMOS_ENDPOINT: cosmosEndpoint - } - } -} diff --git a/infra/core/host/appservice-config-logs.bicep b/infra/core/host/appservice-config-logs.bicep deleted file mode 100644 index fde8940..0000000 --- a/infra/core/host/appservice-config-logs.bicep +++ /dev/null @@ -1,11 +0,0 @@ -param appServiceName string - -resource siteConfigLogs 'Microsoft.Web/sites/config@2022-03-01' = { - name: '${appServiceName}/logs' - properties: { - applicationLogs: { fileSystem: { level: 'Verbose' } } - detailedErrorMessages: { enabled: true } - failedRequestsTracing: { enabled: true } - httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } - } -} diff --git a/infra/core/host/appservice-config-sqlserver.bicep b/infra/core/host/appservice-config-sqlserver.bicep deleted file mode 100644 index 3d8c8d0..0000000 --- a/infra/core/host/appservice-config-sqlserver.bicep +++ /dev/null @@ -1,14 +0,0 @@ -param appServiceName string -param sqlConnectionStringKey string - -module appServiceConfigSqlServerSettings 'appservice-config-union.bicep' = { - name: '${appServiceName}-appservice-config-sqlserver-settings' - params: { - appServiceName: appServiceName - configName: 'appsettings' - currentConfigProperties: list(resourceId('Microsoft.Web/sites/config', appServiceName, 'appsettings'), '2022-03-01').properties - additionalConfigProperties: { - AZURE_SQL_CONNECTION_STRING_KEY: sqlConnectionStringKey - } - } -} diff --git a/infra/core/host/appservice-config-union.bicep b/infra/core/host/appservice-config-union.bicep deleted file mode 100644 index 3e0bcee..0000000 --- a/infra/core/host/appservice-config-union.bicep +++ /dev/null @@ -1,9 +0,0 @@ -param additionalConfigProperties object -param appServiceName string -param configName string -param currentConfigProperties object - -resource siteConfigUnion 'Microsoft.Web/sites/config@2022-03-01' = { - name: '${appServiceName}/${configName}' - properties: union(currentConfigProperties, additionalConfigProperties) -} diff --git a/infra/core/host/appservice-dotnet.bicep b/infra/core/host/appservice-dotnet.bicep deleted file mode 100644 index 012a452..0000000 --- a/infra/core/host/appservice-dotnet.bicep +++ /dev/null @@ -1,35 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param allowedOrigins array = [] -param appCommandLine string = '' -param applicationInsightsName string = '' -param appServicePlanId string -param appSettings object = {} -param keyVaultName string = '' -param linuxFxVersion string = 'DOTNETCORE|6.0' -param managedIdentity bool = !(empty(keyVaultName)) -param scmDoBuildDuringDeployment bool = false -param serviceName string - -module appService 'appservice.bicep' = { - name: '${serviceName}-appservice-dotnet' - params: { - environmentName: environmentName - location: location - allowedOrigins: allowedOrigins - appCommandLine: appCommandLine - applicationInsightsName: applicationInsightsName - appServicePlanId: appServicePlanId - appSettings: appSettings - keyVaultName: keyVaultName - linuxFxVersion: linuxFxVersion - managedIdentity: managedIdentity - scmDoBuildDuringDeployment: scmDoBuildDuringDeployment - serviceName: serviceName - } -} - -output identityPrincipalId string = appService.outputs.identityPrincipalId -output name string = appService.outputs.name -output uri string = appService.outputs.uri diff --git a/infra/core/host/appservice-node.bicep b/infra/core/host/appservice-node.bicep deleted file mode 100644 index 6f56d46..0000000 --- a/infra/core/host/appservice-node.bicep +++ /dev/null @@ -1,35 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param allowedOrigins array = [] -param appCommandLine string = '' -param applicationInsightsName string = '' -param appServicePlanId string -param appSettings object = {} -param keyVaultName string = '' -param linuxFxVersion string = 'NODE|16-lts' -param managedIdentity bool = !(empty(keyVaultName)) -param scmDoBuildDuringDeployment bool = false -param serviceName string - -module appService 'appservice.bicep' = { - name: '${serviceName}-appservice-node' - params: { - environmentName: environmentName - location: location - allowedOrigins: allowedOrigins - appCommandLine: appCommandLine - applicationInsightsName: applicationInsightsName - appServicePlanId: appServicePlanId - appSettings: appSettings - keyVaultName: keyVaultName - linuxFxVersion: linuxFxVersion - managedIdentity: managedIdentity - scmDoBuildDuringDeployment: scmDoBuildDuringDeployment - serviceName: serviceName - } -} - -output identityPrincipalId string = appService.outputs.identityPrincipalId -output name string = appService.outputs.name -output uri string = appService.outputs.uri diff --git a/infra/core/host/appservice-python.bicep b/infra/core/host/appservice-python.bicep deleted file mode 100644 index 767c127..0000000 --- a/infra/core/host/appservice-python.bicep +++ /dev/null @@ -1,35 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param allowedOrigins array = [] -param appCommandLine string = '' -param applicationInsightsName string = '' -param appServicePlanId string -param appSettings object = {} -param keyVaultName string = '' -param linuxFxVersion string = 'PYTHON|3.8' -param managedIdentity bool = !(empty(keyVaultName)) -param scmDoBuildDuringDeployment bool = true -param serviceName string - -module appService 'appservice.bicep' = { - name: '${serviceName}-appservice-python' - params: { - environmentName: environmentName - location: location - allowedOrigins: allowedOrigins - appCommandLine: appCommandLine - applicationInsightsName: applicationInsightsName - appServicePlanId: appServicePlanId - appSettings: appSettings - keyVaultName: keyVaultName - linuxFxVersion: linuxFxVersion - managedIdentity: managedIdentity - scmDoBuildDuringDeployment: scmDoBuildDuringDeployment - serviceName: serviceName - } -} - -output identityPrincipalId string = appService.outputs.identityPrincipalId -output name string = appService.outputs.name -output uri string = appService.outputs.uri diff --git a/infra/core/host/appservice.bicep b/infra/core/host/appservice.bicep index 3eece63..62e34a6 100644 --- a/infra/core/host/appservice.bicep +++ b/infra/core/host/appservice.bicep @@ -1,34 +1,42 @@ -param environmentName string +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 applicationInsightsName string = '' -param appServicePlanId string param appSettings object = {} param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') param functionAppScaleLimit int = -1 -param keyVaultName string = '' -param kind string = 'app,linux' -param linuxFxVersion string = '' -param managedIdentity bool = !(empty(keyVaultName)) +param linuxFxVersion string = runtimeNameAndVersion param minimumElasticInstanceCount int = -1 param numberOfWorkers int = -1 param scmDoBuildDuringDeployment bool = false -param serviceName string param use32BitWorkerProcess bool = false -var abbrs = loadJsonContent('../../abbreviations.json') -var tags = { 'azd-env-name': environmentName } -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) - -var prefix = contains(kind, 'function') ? abbrs.webSitesFunctions : abbrs.webSitesAppService - resource appService 'Microsoft.Web/sites@2022-03-01' = { - name: '${prefix}${serviceName}-${resourceToken}' + name: name location: location - tags: union(tags, { 'azd-service-name': serviceName }) + tags: tags kind: kind properties: { serverFarmId: appServicePlanId @@ -49,41 +57,30 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = { httpsOnly: true } - identity: managedIdentity ? { type: 'SystemAssigned' } : null + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } - resource appSettings 'config' = { + resource configAppSettings 'config' = { name: 'appsettings' - properties: union({ + 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 } : {}) + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) } -} -module appSettingsUnion 'appservice-config-union.bicep' = if (!empty(appSettings)) { - name: '${serviceName}-app-settings-union' - params: { - appServiceName: appService.name - configName: 'appsettings' - currentConfigProperties: appService::appSettings.list().properties - additionalConfigProperties: appSettings - } -} - -module siteConfigLogs 'appservice-config-logs.bicep' = { - name: '${serviceName}-appservice-config-logs' - params: { - appServiceName: appService.name - } -} - -module keyVaultAccess '../security/keyvault-access.bicep' = if (!(empty(keyVaultName))) { - name: '${serviceName}-appservice-keyvault-access' - params: { - principalId: appService.identity.principalId - environmentName: environmentName - location: location + 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 + ] } } @@ -91,7 +88,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty( name: keyVaultName } -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!(empty(applicationInsightsName))) { +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { name: applicationInsightsName } diff --git a/infra/core/host/appserviceplan-functions.bicep b/infra/core/host/appserviceplan-functions.bicep deleted file mode 100644 index 6693e6b..0000000 --- a/infra/core/host/appserviceplan-functions.bicep +++ /dev/null @@ -1,21 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param sku object = { - name: 'Y1' - tier: 'Dynamic' - size: 'Y1' - family: 'Y' -} - -module appServicePlanFunctions 'appserviceplan.bicep' = { - name: 'appserviceplan-functions' - params: { - environmentName: environmentName - location: location - sku: sku - kind: 'functionapp' - } -} - -output appServicePlanId string = appServicePlanFunctions.outputs.appServicePlanId diff --git a/infra/core/host/appserviceplan-sites.bicep b/infra/core/host/appserviceplan-sites.bicep deleted file mode 100644 index bd35137..0000000 --- a/infra/core/host/appserviceplan-sites.bicep +++ /dev/null @@ -1,15 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param sku object = { name: 'B1' } - -module appServicePlanSites 'appserviceplan.bicep' = { - name: 'appserviceplan-sites' - params: { - environmentName: environmentName - location: location - sku: sku - } -} - -output appServicePlanId string = appServicePlanSites.outputs.appServicePlanId diff --git a/infra/core/host/appserviceplan.bicep b/infra/core/host/appserviceplan.bicep index c72fcbd..69c35d7 100644 --- a/infra/core/host/appserviceplan.bicep +++ b/infra/core/host/appserviceplan.bicep @@ -1,16 +1,13 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} param kind string = '' param reserved bool = true param sku object -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { - name: '${abbrs.webServerFarms}${resourceToken}' + name: name location: location tags: tags sku: sku @@ -20,4 +17,4 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { } } -output appServicePlanId string = appServicePlan.id +output id string = appServicePlan.id diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep index fdfcf67..dde1bab 100644 --- a/infra/core/host/container-app.bicep +++ b/infra/core/host/container-app.bicep @@ -1,25 +1,28 @@ -param environmentName string +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 managedIdentity bool = !empty(keyVaultName) param targetPort int = 80 -param serviceName string -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } +@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: '${abbrs.appContainerApps}${serviceName}-${resourceToken}' + name: name location: location - tags: union(tags, { 'azd-service-name': serviceName }) - identity: managedIdentity ? { type: 'SystemAssigned' } : null + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } properties: { managedEnvironmentId: containerAppsEnvironment.id configuration: { @@ -47,33 +50,28 @@ resource app 'Microsoft.App/containerApps@2022-03-01' = { containers: [ { image: imageName - name: 'main' + name: containerName env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } } ] } } } -module keyVaultAccess '../security/keyvault-access.bicep' = if (!(empty(keyVaultName))) { - name: '${serviceName}-appservice-keyvault-access' - params: { - environmentName: environmentName - location: location - keyVaultName: keyVaultName - principalId: app.identity.principalId - } -} - resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { - name: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + name: containerAppsEnvironmentName } // 2022-02-01-preview needed for anonymousPullEnabled resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { - name: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + 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/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep index b1e0578..2dd858c 100644 --- a/infra/core/host/container-apps-environment.bicep +++ b/infra/core/host/container-apps-environment.bicep @@ -1,15 +1,11 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} -param containerAppsEnvironmentName string = '' param logAnalyticsWorkspaceName string -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' = { - name: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + name: name location: location tags: tags properties: { @@ -23,8 +19,8 @@ resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' } } -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { name: logAnalyticsWorkspaceName } -output containerAppsEnvironmentName string = containerAppsEnvironment.name +output name string = containerAppsEnvironment.name diff --git a/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep index a916c7f..395af70 100644 --- a/infra/core/host/container-apps.bicep +++ b/infra/core/host/container-apps.bicep @@ -1,30 +1,30 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} param containerAppsEnvironmentName string = '' -param containerAppsGroupName string = 'app' param containerRegistryName string = '' param logAnalyticsWorkspaceName string = '' module containerAppsEnvironment 'container-apps-environment.bicep' = { - name: '${containerAppsGroupName}-container-apps-environment' + name: '${name}-container-apps-environment' params: { - environmentName: environmentName + name: containerAppsEnvironmentName location: location - containerAppsEnvironmentName: containerAppsEnvironmentName + tags: tags logAnalyticsWorkspaceName: logAnalyticsWorkspaceName } } module containerRegistry 'container-registry.bicep' = { - name: '${containerAppsGroupName}-container-registry' + name: '${name}-container-registry' params: { - environmentName: environmentName + name: containerRegistryName location: location - containerRegistryName: containerRegistryName + tags: tags } } -output containerAppsEnvironmentName string = containerAppsEnvironment.outputs.containerAppsEnvironmentName -output containerRegistryEndpoint string = containerRegistry.outputs.containerRegistryEndpoint -output containerRegistryName string = containerRegistry.outputs.containerRegistryName +output environmentName string = containerAppsEnvironment.outputs.name +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/infra/core/host/container-registry.bicep b/infra/core/host/container-registry.bicep index 115da50..01c3213 100644 --- a/infra/core/host/container-registry.bicep +++ b/infra/core/host/container-registry.bicep @@ -1,9 +1,9 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} param adminUserEnabled bool = true param anonymousPullEnabled bool = false -param containerRegistryName string = '' param dataEndpointEnabled bool = false param encryption object = { status: 'disabled' @@ -11,17 +11,13 @@ param encryption object = { param networkRuleBypassOptions string = 'AzureServices' param publicNetworkAccess string = 'Enabled' param sku object = { - name: 'Standard' + name: 'Basic' } param zoneRedundancy string = 'Disabled' -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - // 2022-02-01-preview needed for anonymousPullEnabled resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { - name: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + name: name location: location tags: tags sku: sku @@ -36,5 +32,5 @@ resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-pr } } -output containerRegistryEndpoint string = containerRegistry.properties.loginServer -output containerRegistryName string = containerRegistry.name +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/infra/core/host/functions-node.bicep b/infra/core/host/functions-node.bicep deleted file mode 100644 index e4731f4..0000000 --- a/infra/core/host/functions-node.bicep +++ /dev/null @@ -1,34 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param allowedOrigins array = [] -param applicationInsightsName string = '' -param appServicePlanId string -param appSettings object = {} -param keyVaultName string = '' -param linuxFxVersion string = 'NODE|16' -param managedIdentity bool = !(empty(keyVaultName)) -param serviceName string -param storageAccountName string - -module functions 'functions.bicep' = { - name: '${serviceName}-functions-node' - params: { - environmentName: environmentName - location: location - allowedOrigins: allowedOrigins - applicationInsightsName: applicationInsightsName - appServicePlanId: appServicePlanId - appSettings: appSettings - functionsWorkerRuntime: 'node' - keyVaultName: keyVaultName - linuxFxVersion: linuxFxVersion - managedIdentity: managedIdentity - serviceName: serviceName - storageAccountName: storageAccountName - } -} - -output identityPrincipalId string = functions.outputs.identityPrincipalId -output name string = functions.outputs.name -output uri string = functions.outputs.uri diff --git a/infra/core/host/functions-python.bicep b/infra/core/host/functions-python.bicep deleted file mode 100644 index 2ff79bd..0000000 --- a/infra/core/host/functions-python.bicep +++ /dev/null @@ -1,34 +0,0 @@ -param environmentName string -param location string = resourceGroup().location - -param allowedOrigins array = [] -param applicationInsightsName string = '' -param appServicePlanId string -param appSettings object = {} -param keyVaultName string = '' -param linuxFxVersion string = 'PYTHON|3.8' -param managedIdentity bool = !(empty(keyVaultName)) -param serviceName string -param storageAccountName string - -module functions 'functions.bicep' = { - name: '${serviceName}-functions-python' - params: { - environmentName: environmentName - location: location - allowedOrigins: allowedOrigins - applicationInsightsName: applicationInsightsName - appServicePlanId: appServicePlanId - appSettings: appSettings - functionsWorkerRuntime: 'python' - keyVaultName: keyVaultName - linuxFxVersion: linuxFxVersion - managedIdentity: managedIdentity - serviceName: serviceName - storageAccountName: storageAccountName - } -} - -output identityPrincipalId string = functions.outputs.identityPrincipalId -output name string = functions.outputs.name -output uri string = functions.outputs.uri diff --git a/infra/core/host/functions.bicep b/infra/core/host/functions.bicep index 818485f..28a581b 100644 --- a/infra/core/host/functions.bicep +++ b/infra/core/host/functions.bicep @@ -1,41 +1,63 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} -param allowedOrigins array = [] -param alwaysOn bool = false +// 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 functionAppScaleLimit int = 200 -param functionsExtensionVersion string = '~4' -param functionsWorkerRuntime string -param kind string = 'functionapp,linux' -param linuxFxVersion string = '' -param keyVaultName string = '' -param managedIdentity bool = !(empty(keyVaultName)) -param minimumElasticInstanceCount int = 0 -param numberOfWorkers int = 1 +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 serviceName string -param storageAccountName string param use32BitWorkerProcess bool = false module functions 'appservice.bicep' = { - name: '${serviceName}-functions' + name: '${name}-functions' params: { - environmentName: environmentName + 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: functionsExtensionVersion - FUNCTIONS_WORKER_RUNTIME: functionsWorkerRuntime + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName }) clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild functionAppScaleLimit: functionAppScaleLimit keyVaultName: keyVaultName kind: kind @@ -43,8 +65,10 @@ module functions 'appservice.bicep' = { managedIdentity: managedIdentity minimumElasticInstanceCount: minimumElasticInstanceCount numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion scmDoBuildDuringDeployment: scmDoBuildDuringDeployment - serviceName: serviceName use32BitWorkerProcess: use32BitWorkerProcess } } diff --git a/infra/core/host/staticwebapp.bicep b/infra/core/host/staticwebapp.bicep index 3537fa9..91c2d0d 100644 --- a/infra/core/host/staticwebapp.bicep +++ b/infra/core/host/staticwebapp.bicep @@ -1,20 +1,16 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} -param serviceName string param sku object = { name: 'Free' tier: 'Free' } -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - resource web 'Microsoft.Web/staticSites@2022-03-01' = { - name: '${abbrs.webStaticSites}${serviceName}-${resourceToken}' + name: name location: location - tags: union(tags, { 'azd-service-name': serviceName }) + tags: tags sku: sku properties: { provider: 'Custom' diff --git a/infra/core/monitor/applicationinsights-dashboard.bicep b/infra/core/monitor/applicationinsights-dashboard.bicep index 11a6512..b7af2c1 100644 --- a/infra/core/monitor/applicationinsights-dashboard.bicep +++ b/infra/core/monitor/applicationinsights-dashboard.bicep @@ -1,14 +1,11 @@ -param environmentName string -param location string = resourceGroup().location +param name string param applicationInsightsName string - -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } +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: '${abbrs.portalDashboards}${resourceToken}' + name: name location: location tags: tags properties: { diff --git a/infra/core/monitor/applicationinsights.bicep b/infra/core/monitor/applicationinsights.bicep index b9ce1fd..f76b292 100644 --- a/infra/core/monitor/applicationinsights.bicep +++ b/infra/core/monitor/applicationinsights.bicep @@ -1,13 +1,12 @@ -param environmentName string +param name string +param dashboardName string param location string = resourceGroup().location +param tags object = {} + param logAnalyticsWorkspaceId string -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { - name: '${abbrs.insightsComponents}${resourceToken}' + name: name location: location tags: tags kind: 'web' @@ -20,11 +19,12 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = { name: 'application-insights-dashboard' params: { - environmentName: environmentName + name: dashboardName location: location applicationInsightsName: applicationInsights.name } } -output applicationInsightsConnectionString string = applicationInsights.properties.ConnectionString -output applicationInsightsName string = applicationInsights.name +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/infra/core/monitor/loganalytics.bicep b/infra/core/monitor/loganalytics.bicep index a36912c..770544c 100644 --- a/infra/core/monitor/loganalytics.bicep +++ b/infra/core/monitor/loganalytics.bicep @@ -1,12 +1,9 @@ -param environmentName string +param name string param location string = resourceGroup().location - -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } +param tags object = {} resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { - name: '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + name: name location: location tags: tags properties: any({ @@ -20,5 +17,5 @@ resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-previ }) } -output logAnalyticsWorkspaceId string = logAnalytics.id -output logAnalyticsWorkspaceName string = logAnalytics.name +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep index 1c5ae3e..96ba11e 100644 --- a/infra/core/monitor/monitoring.bicep +++ b/infra/core/monitor/monitoring.bicep @@ -1,24 +1,31 @@ -param environmentName string +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string param location string = resourceGroup().location +param tags object = {} module logAnalytics 'loganalytics.bicep' = { name: 'loganalytics' params: { - environmentName: environmentName + name: logAnalyticsName location: location + tags: tags } } module applicationInsights 'applicationinsights.bicep' = { name: 'applicationinsights' params: { - environmentName: environmentName + name: applicationInsightsName location: location - logAnalyticsWorkspaceId: logAnalytics.outputs.logAnalyticsWorkspaceId + tags: tags + dashboardName: applicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id } } -output applicationInsightsConnectionString string = applicationInsights.outputs.applicationInsightsConnectionString -output applicationInsightsName string = applicationInsights.outputs.applicationInsightsName -output logAnalyticsWorkspaceId string = logAnalytics.outputs.logAnalyticsWorkspaceId -output logAnalyticsWorkspaceName string = logAnalytics.outputs.logAnalyticsWorkspaceName +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/infra/core/security/keyvault-access.bicep b/infra/core/security/keyvault-access.bicep index 30c00f4..96c9cf7 100644 --- a/infra/core/security/keyvault-access.bicep +++ b/infra/core/security/keyvault-access.bicep @@ -1,16 +1,12 @@ -param environmentName string -param location string = resourceGroup().location +param name string = 'add' param keyVaultName string = '' param permissions object = { secrets: [ 'get', 'list' ] } param principalId string -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) - resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { parent: keyVault - name: 'add' + name: name properties: { accessPolicies: [ { objectId: principalId @@ -21,5 +17,5 @@ resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-0 } resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + name: keyVaultName } diff --git a/infra/core/security/keyvault.bicep b/infra/core/security/keyvault.bicep index 96b8e84..0eb4a86 100644 --- a/infra/core/security/keyvault.bicep +++ b/infra/core/security/keyvault.bicep @@ -1,15 +1,11 @@ -param environmentName string +param name string param location string = resourceGroup().location +param tags object = {} -param keyVaultName string = '' param principalId string = '' -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { - name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + name: name location: location tags: tags properties: { @@ -25,5 +21,5 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { } } -output keyVaultEndpoint string = keyVault.properties.vaultUri -output keyVaultName string = keyVault.name +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep index 7d2eb91..a41972c 100644 --- a/infra/core/storage/storage-account.bicep +++ b/infra/core/storage/storage-account.bicep @@ -1,17 +1,15 @@ -param environmentName string +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' } -var abbrs = loadJsonContent('../../abbreviations.json') -var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) -var tags = { 'azd-env-name': environmentName } - -resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' = { - name: '${abbrs.storageStorageAccounts}${resourceToken}' +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name location: location tags: tags kind: kind @@ -24,6 +22,17 @@ resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' = { 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/infra/main.bicep b/infra/main.bicep index a47a36f..55f8ddd 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -9,6 +9,18 @@ param environmentName string @description('Primary location for all resources') param location string +// 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 resourceGroupName string = '' +param webServiceName string = '' +param sqlServer1Name string = 'sqlServer-catalog-01' +param sqlServer2Name string = 'sqlServer-identity-01' +param sqlDatabaseName string = '' +param appServicePlanName string = '' +param keyVaultName string = '' + @description('Id of the user or app to assign application roles') param principalId string = '' @@ -21,26 +33,86 @@ param sqlAdminPassword string param appUserPassword string var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } +// Organize resources in a resource group resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: '${abbrs.resourcesResourceGroups}${environmentName}' + name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}' location: location tags: tags } -module resources 'resources.bicep' = { - name: 'resources' +// The application frontend +module web './app/web.bicep' = { + name: 'web' scope: rg params: { - environmentName: environmentName + name: !empty(webServiceName) ? webServiceName : '${abbrs.webSitesAppService}web-${resourceToken}' location: location - principalId: principalId + tags: tags + appServicePlanId: appServicePlan.outputs.id + } +} + +// The application database: Catalog +module sqlServer1 './app/catalog-db.bicep' = { + name: 'sql-catalog' + scope: rg + params: { + name: !empty(sqlServer1Name) ? sqlServer1Name : '${abbrs.sqlServers}${resourceToken}' + databaseName: sqlDatabaseName + location: location + tags: tags sqlAdminPassword: sqlAdminPassword appUserPassword: appUserPassword + keyVaultName: keyVault.outputs.name + } +} + +// The application database: Identity +module sqlServer2 './app/identity-db.bicep' = { + name: 'sql-identity' + scope: rg + params: { + name: !empty(sqlServer2Name) ? sqlServer2Name : '${abbrs.sqlServers}${resourceToken}' + databaseName: sqlDatabaseName + location: location + tags: tags + sqlAdminPassword: sqlAdminPassword + appUserPassword: appUserPassword + keyVaultName: keyVault.outputs.name + } +} + + + +// Store secrets in a keyvault +module keyVault './core/security/keyvault.bicep' = { + name: 'keyvault' + scope: rg + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan './core/host/appserviceplan.bicep' = { + name: 'appserviceplan' + scope: rg + params: { + name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' + location: location + tags: tags + sku: { + name: 'B1' + } } } output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId -output REACT_APP_WEB_BASE_URL string = resources.outputs.WEB_URI diff --git a/infra/resources.bicep b/infra/resources.bicep deleted file mode 100644 index 0eafb44..0000000 --- a/infra/resources.bicep +++ /dev/null @@ -1,82 +0,0 @@ -param environmentName string -param location string = resourceGroup().location -param principalId string = '' - -@secure() -param sqlAdminPassword string - -@secure() -param appUserPassword string - -// The application frontend -module web './app/web.bicep' = { - name: 'web' - params: { - environmentName: environmentName - location: location - appServicePlanId: appServicePlan.outputs.appServicePlanId - } -} - -// The application database: Catalog -module sqlServer1 './app/dbCatalog.bicep' = { - name: 'sqlCatalog' - params: { - environmentName: environmentName - location: location - sqlAdminPassword: sqlAdminPassword - appUserPassword: appUserPassword - keyVaultName: keyVault.outputs.keyVaultName - } -} - -// The application database: Identity -module sqlServer2 './app/dbIdentity.bicep' = { - name: 'sqlIdentity' - params: { - environmentName: environmentName - location: location - sqlAdminPassword: sqlAdminPassword - appUserPassword: appUserPassword - keyVaultName: keyVault.outputs.keyVaultName - } -} - -// Configure web to use sqlCatalog -module apiSqlServerConfig1 './core/host/appservice-config-sqlserver.bicep' = { - name: 'web-sqlserver-config-1' - params: { - appServiceName: web.outputs.WEB_NAME - sqlConnectionStringKey: sqlServer1.outputs.sqlConnectionStringKey - } -} - -// Configure web to use sqlIdentity -module apiSqlServerConfig2 './core/host/appservice-config-sqlserver.bicep' = { - name: 'web-sqlserver-config-2' - params: { - appServiceName: web.outputs.WEB_NAME - sqlConnectionStringKey: sqlServer2.outputs.sqlConnectionStringKey - } -} - -// Store secrets in a keyvault -module keyVault './core/security/keyvault.bicep' = { - name: 'keyvault' - params: { - environmentName: environmentName - location: location - principalId: principalId - } -} - -// Create an App Service Plan to group applications under the same payment plan and SKU -module appServicePlan './core/host/appserviceplan-sites.bicep' = { - name: 'appserviceplan' - params: { - environmentName: environmentName - location: location - } -} - -output WEB_URI string = web.outputs.WEB_URI From b2a3137f02a3b7ef96f59fd4e55504602620b870 Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 18 Nov 2022 10:27:19 +0800 Subject: [PATCH 04/22] Remove some invalid changes --- src/Web/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Web/appsettings.json b/src/Web/appsettings.json index 4f5c0ed..70989a6 100644 --- a/src/Web/appsettings.json +++ b/src/Web/appsettings.json @@ -4,8 +4,8 @@ "webBase": "https://localhost:44315/" }, "ConnectionStrings": { - "CatalogConnection": "Server=sqlserver-catalog-01.database.windows.net; Database=Todo; User=appUser; Password=fACZr419yzJ19tA", - "IdentityConnection": "Server=sqlserver-identity-01.database.windows.net; Database=Todo; User=appUser; Password=fACZr419yzJ19tA" + "CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;", + "IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;" }, "CatalogBaseUrl": "", "Logging": { From 45ad9ed83f854e6abb8127cbfa7711025ec276d5 Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 18 Nov 2022 13:53:38 +0800 Subject: [PATCH 05/22] add scripts folder and rename sqlserver --- infra/app/catalog-db.bicep | 2 +- infra/app/identity-db.bicep | 2 +- .../{sqlserver1.bicep => sqlserver-catalog.bicep} | 0 .../{sqlserver2.bicep => sqlserver-identity.bicep} | 0 scripts/setup-database.ps1 | 9 +++++++++ scripts/setup-database.sh | 12 ++++++++++++ 6 files changed, 23 insertions(+), 2 deletions(-) rename infra/core/database/sqlserver/{sqlserver1.bicep => sqlserver-catalog.bicep} (100%) rename infra/core/database/sqlserver/{sqlserver2.bicep => sqlserver-identity.bicep} (100%) create mode 100644 scripts/setup-database.ps1 create mode 100644 scripts/setup-database.sh diff --git a/infra/app/catalog-db.bicep b/infra/app/catalog-db.bicep index 631f138..3d40c45 100644 --- a/infra/app/catalog-db.bicep +++ b/infra/app/catalog-db.bicep @@ -14,7 +14,7 @@ param appUserPassword string var defaultDatabaseName = 'Todo' var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName -module sqlServer1 '../core/database/sqlserver/sqlserver1.bicep' = { +module sqlServer1 '../core/database/sqlserver/sqlserver-catalog.bicep' = { name: 'sqlServer01' params: { name: name diff --git a/infra/app/identity-db.bicep b/infra/app/identity-db.bicep index 1673c53..9717032 100644 --- a/infra/app/identity-db.bicep +++ b/infra/app/identity-db.bicep @@ -14,7 +14,7 @@ param appUserPassword string var defaultDatabaseName = 'Todo' var actualDatabaseName = !empty(databaseName) ? databaseName : defaultDatabaseName -module sqlServer2 '../core/database/sqlserver/sqlserver2.bicep' = { +module sqlServer2 '../core/database/sqlserver/sqlserver-identity.bicep' = { name: 'sqlServer02' params: { name: name diff --git a/infra/core/database/sqlserver/sqlserver1.bicep b/infra/core/database/sqlserver/sqlserver-catalog.bicep similarity index 100% rename from infra/core/database/sqlserver/sqlserver1.bicep rename to infra/core/database/sqlserver/sqlserver-catalog.bicep diff --git a/infra/core/database/sqlserver/sqlserver2.bicep b/infra/core/database/sqlserver/sqlserver-identity.bicep similarity index 100% rename from infra/core/database/sqlserver/sqlserver2.bicep rename to infra/core/database/sqlserver/sqlserver-identity.bicep diff --git a/scripts/setup-database.ps1 b/scripts/setup-database.ps1 new file mode 100644 index 0000000..8969760 --- /dev/null +++ b/scripts/setup-database.ps1 @@ -0,0 +1,9 @@ +# Default to current directory +$FOLDER_PATH = [string](Get-Location) + +# Before setting up your database, make sure of two things: +# 1. Ensure your connection strings in appsettings.json point to a local SQL Server instance. +# 2. Ensure the tool EF was already installed. +dotnet tool update --global dotnet-ef +dotnet ef database update -c catalogcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj +dotnet ef database update -c appidentitydbcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj \ No newline at end of file diff --git a/scripts/setup-database.sh b/scripts/setup-database.sh new file mode 100644 index 0000000..abd4e60 --- /dev/null +++ b/scripts/setup-database.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Default to current directory +FOLDER_PATH=$(pwd) + + +# Before setting up your database, make sure of two things: +# 1. Ensure your connection strings in appsettings.json point to a local SQL Server instance. +# 2. Ensure the tool EF was already installed. +dotnet tool update --global dotnet-ef +dotnet ef database update -c catalogcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj +dotnet ef database update -c appidentitydbcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj \ No newline at end of file From 5b86898d61bb075c75c26ffd07db8f82787f11a8 Mon Sep 17 00:00:00 2001 From: Jon Gallant Date: Fri, 9 Dec 2022 16:03:04 -0800 Subject: [PATCH 06/22] Update bicep add azure.yaml scripts --- azure.yaml | 13 +- infra/app/catalog-db.bicep | 31 ----- infra/app/identity-db.bicep | 31 ----- infra/app/web.bicep | 23 ---- .../sqlserver/sqlserver-catalog.bicep | 129 ------------------ .../sqlserver/sqlserver-identity.bicep | 129 ------------------ infra/main.bicep | 30 ++-- 7 files changed, 24 insertions(+), 362 deletions(-) delete mode 100644 infra/app/catalog-db.bicep delete mode 100644 infra/app/identity-db.bicep delete mode 100644 infra/app/web.bicep delete mode 100644 infra/core/database/sqlserver/sqlserver-catalog.bicep delete mode 100644 infra/core/database/sqlserver/sqlserver-identity.bicep diff --git a/azure.yaml b/azure.yaml index 9590449..f36e91e 100644 --- a/azure.yaml +++ b/azure.yaml @@ -1,8 +1,11 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/wbreza/azure-dev/main/schemas/v1.0/azure.yaml.json name: eShopOnWeb services: - web: - project: ./src/Web - language: csharp - host: appservice + web: + project: ./src/Web + language: csharp + host: appservice +scripts: + postprovision: + path: './scripts/setup-database.ps1' \ No newline at end of file diff --git a/infra/app/catalog-db.bicep b/infra/app/catalog-db.bicep deleted file mode 100644 index 3d40c45..0000000 --- a/infra/app/catalog-db.bicep +++ /dev/null @@ -1,31 +0,0 @@ -param name string -param location string = resourceGroup().location -param tags object = {} - -param databaseName string = 'CatalogDB' -param keyVaultName string - -@secure() -param sqlAdminPassword string -@secure() -param appUserPassword 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 sqlServer1 '../core/database/sqlserver/sqlserver-catalog.bicep' = { - name: 'sqlServer01' - params: { - name: name - location: location - tags: tags - databaseName: actualDatabaseName - keyVaultName: keyVaultName - sqlAdminPassword: sqlAdminPassword - appUserPassword: appUserPassword - } -} - -output sqlCatalogConnectionStringKey string = sqlServer1.outputs.connectionStringKey -output sqlCatalogDatabase1Name string = sqlServer1.outputs.databaseName diff --git a/infra/app/identity-db.bicep b/infra/app/identity-db.bicep deleted file mode 100644 index 9717032..0000000 --- a/infra/app/identity-db.bicep +++ /dev/null @@ -1,31 +0,0 @@ -param name string -param location string = resourceGroup().location -param tags object = {} - -param databaseName string = 'IdentityDB' -param keyVaultName string - -@secure() -param sqlAdminPassword string -@secure() -param appUserPassword 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 sqlServer2 '../core/database/sqlserver/sqlserver-identity.bicep' = { - name: 'sqlServer02' - params: { - name: name - location: location - tags: tags - databaseName: actualDatabaseName - keyVaultName: keyVaultName - sqlAdminPassword: sqlAdminPassword - appUserPassword: appUserPassword - } -} - -output sqlCatalogConnectionStringKey string = sqlServer2.outputs.connectionStringKey -output sqlCatalogDatabase1Name string = sqlServer2.outputs.databaseName diff --git a/infra/app/web.bicep b/infra/app/web.bicep deleted file mode 100644 index f33a276..0000000 --- a/infra/app/web.bicep +++ /dev/null @@ -1,23 +0,0 @@ -param name string -param location string = resourceGroup().location -param tags object = {} -param serviceName string = 'web' -param appCommandLine string = 'pm2 serve /home/site/wwwroot --no-daemon --spa' -param applicationInsightsName string = '' -param appServicePlanId string -param appSettings object = {} - -module web '../core/host/appservice.bicep' = { - name: '${name}-deployment' - params: { - name: name - location: location - appServicePlanId: appServicePlanId - runtimeName: 'dotnetcore' - runtimeVersion: '6.0' - tags: union(tags, { 'azd-service-name': serviceName }) - scmDoBuildDuringDeployment: false - } -} - -output REACT_APP_WEB_BASE_URL string = web.outputs.uri diff --git a/infra/core/database/sqlserver/sqlserver-catalog.bicep b/infra/core/database/sqlserver/sqlserver-catalog.bicep deleted file mode 100644 index 891aa45..0000000 --- a/infra/core/database/sqlserver/sqlserver-catalog.bicep +++ /dev/null @@ -1,129 +0,0 @@ -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-CATALOG-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/infra/core/database/sqlserver/sqlserver-identity.bicep b/infra/core/database/sqlserver/sqlserver-identity.bicep deleted file mode 100644 index 305c97b..0000000 --- a/infra/core/database/sqlserver/sqlserver-identity.bicep +++ /dev/null @@ -1,129 +0,0 @@ -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-IDENTITY-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/infra/main.bicep b/infra/main.bicep index 55f8ddd..1d8bba8 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -15,9 +15,10 @@ param location string // } param resourceGroupName string = '' param webServiceName string = '' -param sqlServer1Name string = 'sqlServer-catalog-01' -param sqlServer2Name string = 'sqlServer-identity-01' -param sqlDatabaseName string = '' +param catalogDatabaseName string = '' +param catalogDatabaseServerName string = '' +param identityDatabaseName string = '' +param identityDatabaseServerName string = '' param appServicePlanName string = '' param keyVaultName string = '' @@ -44,49 +45,51 @@ resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { } // The application frontend -module web './app/web.bicep' = { +module web './core/host/appservice.bicep' = { name: 'web' scope: rg params: { name: !empty(webServiceName) ? webServiceName : '${abbrs.webSitesAppService}web-${resourceToken}' location: location - tags: tags appServicePlanId: appServicePlan.outputs.id + runtimeName: 'dotnetcore' + runtimeVersion: '6.0' + tags: union(tags, { 'azd-service-name': 'web' }) } } // The application database: Catalog -module sqlServer1 './app/catalog-db.bicep' = { +module catalogDb './core/database/sqlserver/sqlserver.bicep' = { name: 'sql-catalog' scope: rg params: { - name: !empty(sqlServer1Name) ? sqlServer1Name : '${abbrs.sqlServers}${resourceToken}' - databaseName: sqlDatabaseName + name: !empty(catalogDatabaseServerName) ? catalogDatabaseServerName : '${abbrs.sqlServers}catalog-${resourceToken}' + databaseName: catalogDatabaseName location: location tags: tags sqlAdminPassword: sqlAdminPassword appUserPassword: appUserPassword keyVaultName: keyVault.outputs.name + connectionStringKey: 'AZURE-SQL-CATALOG-CONNECTION-STRING' } } // The application database: Identity -module sqlServer2 './app/identity-db.bicep' = { +module identityDb './core/database/sqlserver/sqlserver.bicep' = { name: 'sql-identity' scope: rg params: { - name: !empty(sqlServer2Name) ? sqlServer2Name : '${abbrs.sqlServers}${resourceToken}' - databaseName: sqlDatabaseName + name: !empty(identityDatabaseServerName) ? identityDatabaseServerName : '${abbrs.sqlServers}identity-${resourceToken}' + databaseName: identityDatabaseName location: location tags: tags sqlAdminPassword: sqlAdminPassword appUserPassword: appUserPassword keyVaultName: keyVault.outputs.name + connectionStringKey: 'AZURE-SQL-IDENTITY-CONNECTION-STRING' } } - - // Store secrets in a keyvault module keyVault './core/security/keyvault.bicep' = { name: 'keyvault' @@ -99,7 +102,6 @@ module keyVault './core/security/keyvault.bicep' = { } } - // Create an App Service Plan to group applications under the same payment plan and SKU module appServicePlan './core/host/appserviceplan.bicep' = { name: 'appserviceplan' From 8697f4a082e4153aea1254b7217605864d089ae2 Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 10:42:06 +0800 Subject: [PATCH 07/22] add azd in codespace --- .devcontainer/devcontainer.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6264a06..f146232 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,7 +11,24 @@ "INSTALL_AZURE_CLI": "false" } }, - + "features": { + "ghcr.io/devcontainers/features/azure-cli:1": { + "version": "2.38" + }, + "ghcr.io/devcontainers/features/docker-from-docker:1": { + "version": "20.10" + }, + "ghcr.io/devcontainers/features/dotnet:1": { + "version": "6.0" + }, + "ghcr.io/devcontainers/features/github-cli:1": { + "version": "2" + }, + "ghcr.io/devcontainers/features/node:1": { + "version": "16", + "nodeGypDependencies": false + } + }, // Comment out to connect as root user. See https://aka.ms/vscode-remote/containers/non-root. // make sure this is the same as USERNAME above "remoteUser": "vscode", @@ -23,6 +40,7 @@ // Add the IDs of extensions you want installed when the container is created. "extensions": [ + "ms-azuretools.azure-dev", "ms-dotnettools.csharp", "formulahendry.dotnet-test-explorer", "ms-vscode.vscode-node-azure-pack", From a949d4b225c9c913eea5c9e8e80bed74653f32d8 Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 10:56:23 +0800 Subject: [PATCH 08/22] upgrade dotnet core 5.0 to 6.0 in order to install dotnet-ef --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d188b1d..f60cd45 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. #------------------------------------------------------------------------------------------------------------- -FROM mcr.microsoft.com/dotnet/sdk:5.0 +FROM mcr.microsoft.com/dotnet/sdk:6.0 # This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" # property in devcontainer.json to use it. On Linux, the container user's GID/UIDs From 7f40d2d115db4ee5e9be73b1d518e676ea3af2fe Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 11:14:42 +0800 Subject: [PATCH 09/22] install azd in codespace --- .devcontainer/Dockerfile | 7 +++++++ .devcontainer/devcontainer.json | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f60cd45..05e5acb 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -20,6 +20,7 @@ ENV NVM_DIR=/usr/local/share/nvm # [Optional] Install the Azure CLI ARG INSTALL_AZURE_CLI="false" +ARG INSTALL_AZURE_DEVELOPER_CLI="true" # Configure apt and install packages RUN apt-get update \ @@ -68,6 +69,12 @@ RUN apt-get update \ && apt-get install -y azure-cli; \ fi \ # + # [Optional] Install the Azure Developer CLI + && if [ "$INSTALL_AZURE_DEVELOPER_CLI" = "true" ]; then \ + apt-get update \ + && curl -fsSL https://aka.ms/install-azd.sh | bash \ + fi \ + # # Install EF Core dotnet tool && dotnet tool install dotnet-ef --tool-path /home/$USERNAME/.dotnet/tools \ && chown -R $USERNAME /home/$USERNAME/.dotnet \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f146232..3bee848 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,8 @@ "USERNAME": "vscode", "INSTALL_NODE": "false", "NODE_VERSION": "lts/*", - "INSTALL_AZURE_CLI": "false" + "INSTALL_AZURE_CLI": "false", + "INSTALL_AZURE_DEVELOPER_CLI": "true" } }, "features": { From 04b88ffbb84a7837c5d3f90b3727bd9dbeb75fff Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 11:25:12 +0800 Subject: [PATCH 10/22] install azd in codespace --- .devcontainer/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 05e5acb..1403065 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -71,8 +71,7 @@ RUN apt-get update \ # # [Optional] Install the Azure Developer CLI && if [ "$INSTALL_AZURE_DEVELOPER_CLI" = "true" ]; then \ - apt-get update \ - && curl -fsSL https://aka.ms/install-azd.sh | bash \ + curl -fsSL https://aka.ms/install-azd.sh | bash \ fi \ # # Install EF Core dotnet tool From 5951b6d3c1655d5db491e2a76c556072a3024188 Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 11:27:43 +0800 Subject: [PATCH 11/22] Remove install azd in codespace, check some errors --- .devcontainer/Dockerfile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 1403065..ad9b25e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -69,11 +69,6 @@ RUN apt-get update \ && apt-get install -y azure-cli; \ fi \ # - # [Optional] Install the Azure Developer CLI - && if [ "$INSTALL_AZURE_DEVELOPER_CLI" = "true" ]; then \ - curl -fsSL https://aka.ms/install-azd.sh | bash \ - fi \ - # # Install EF Core dotnet tool && dotnet tool install dotnet-ef --tool-path /home/$USERNAME/.dotnet/tools \ && chown -R $USERNAME /home/$USERNAME/.dotnet \ From c3479b9abcb22160f2dda85ca0b744c8ea4537e8 Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 11:33:57 +0800 Subject: [PATCH 12/22] Install azd in codespace, check some errors --- .devcontainer/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ad9b25e..279aebc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -23,7 +23,8 @@ ARG INSTALL_AZURE_CLI="false" ARG INSTALL_AZURE_DEVELOPER_CLI="true" # Configure apt and install packages -RUN apt-get update \ +RUN curl -fsSL https://aka.ms/install-azd.sh | bash \ + && apt-get update \ && export DEBIAN_FRONTEND=noninteractive \ && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \ # From e5c6f71155ba4a2c63f90e23bdad7783dfb6c7f5 Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 14:57:57 +0800 Subject: [PATCH 13/22] eliminate manually update the appsettings.json file --- infra/core/database/sqlserver/sqlserver.bicep | 1 + infra/main.bicep | 8 ++++++-- src/Infrastructure/Dependencies.cs | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/infra/core/database/sqlserver/sqlserver.bicep b/infra/core/database/sqlserver/sqlserver.bicep index 821a908..1c4c212 100644 --- a/infra/core/database/sqlserver/sqlserver.bicep +++ b/infra/core/database/sqlserver/sqlserver.bicep @@ -127,3 +127,4 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' output connectionStringKey string = connectionStringKey output databaseName string = sqlServer::database.name +output connectionString string = connectionString diff --git a/infra/main.bicep b/infra/main.bicep index 1d8bba8..535ded5 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -15,9 +15,9 @@ param location string // } param resourceGroupName string = '' param webServiceName string = '' -param catalogDatabaseName string = '' +param catalogDatabaseName string = 'catalogDatabase' param catalogDatabaseServerName string = '' -param identityDatabaseName string = '' +param identityDatabaseName string = 'identityDatabase' param identityDatabaseServerName string = '' param appServicePlanName string = '' param keyVaultName string = '' @@ -55,6 +55,10 @@ module web './core/host/appservice.bicep' = { runtimeName: 'dotnetcore' runtimeVersion: '6.0' tags: union(tags, { 'azd-service-name': 'web' }) + appSettings: { + CATALOG_CONNECTION_STRING_VALUE: '${catalogDb.outputs.connectionString}; Password=${appUserPassword}' + IDENTITY_CONNECTION_STRING_VALUE: '${identityDb.outputs.connectionString}; Password=${appUserPassword}' + } } } diff --git a/src/Infrastructure/Dependencies.cs b/src/Infrastructure/Dependencies.cs index d049a96..d399531 100644 --- a/src/Infrastructure/Dependencies.cs +++ b/src/Infrastructure/Dependencies.cs @@ -30,11 +30,11 @@ public static class Dependencies // Requires LocalDB which can be installed with SQL Server Express 2016 // https://www.microsoft.com/en-us/download/details.aspx?id=54284 services.AddDbContext(c => - c.UseSqlServer(configuration.GetConnectionString("CatalogConnection"))); + c.UseSqlServer(configuration["CATALOG_CONNECTION_STRING_VALUE"])); // Add Identity DbContext services.AddDbContext(options => - options.UseSqlServer(configuration.GetConnectionString("IdentityConnection"))); + options.UseSqlServer(configuration["IDENTITY_CONNECTION_STRING_VALUE"])); } } } From eba96d82af59eb69ab6a9bdfbacd5a37850cf37f Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 12 Dec 2022 15:44:56 +0800 Subject: [PATCH 14/22] remove some invalid changes --- azure.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/azure.yaml b/azure.yaml index f36e91e..c63402c 100644 --- a/azure.yaml +++ b/azure.yaml @@ -5,7 +5,4 @@ services: web: project: ./src/Web language: csharp - host: appservice -scripts: - postprovision: - path: './scripts/setup-database.ps1' \ No newline at end of file + host: appservice \ No newline at end of file From b4853e22d312cc787e985886a0e44b4312e783c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenjie=20Yu=EF=BC=88MSFT=EF=BC=89?= <81678720+zedy-wj@users.noreply.github.com> Date: Tue, 13 Dec 2022 10:25:12 +0800 Subject: [PATCH 15/22] fix indent --- .devcontainer/devcontainer.json | 47 +++++++++++++-------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3bee848..375e927 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,61 +1,50 @@ // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.112.0/containers/dotnetcore-3.1 { - "name": "eShopOnWeb", + "name": "eShopOnWeb", "build": { "dockerfile": "Dockerfile", "args": { "USERNAME": "vscode", "INSTALL_NODE": "false", "NODE_VERSION": "lts/*", - "INSTALL_AZURE_CLI": "false", - "INSTALL_AZURE_DEVELOPER_CLI": "true" + "INSTALL_AZURE_CLI": "false" } }, "features": { - "ghcr.io/devcontainers/features/azure-cli:1": { - "version": "2.38" - }, "ghcr.io/devcontainers/features/docker-from-docker:1": { "version": "20.10" }, - "ghcr.io/devcontainers/features/dotnet:1": { - "version": "6.0" - }, "ghcr.io/devcontainers/features/github-cli:1": { "version": "2" - }, - "ghcr.io/devcontainers/features/node:1": { - "version": "16", - "nodeGypDependencies": false } }, - // Comment out to connect as root user. See https://aka.ms/vscode-remote/containers/non-root. - // make sure this is the same as USERNAME above - "remoteUser": "vscode", + // Comment out to connect as root user. See https://aka.ms/vscode-remote/containers/non-root. + // make sure this is the same as USERNAME above + "remoteUser": "vscode", - // Set *default* container specific settings.json values on container create. - "settings": { - "terminal.integrated.shell.linux": "/bin/bash" - }, + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "ms-azuretools.azure-dev", + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-azuretools.azure-dev", "ms-dotnettools.csharp", "formulahendry.dotnet-test-explorer", "ms-vscode.vscode-node-azure-pack", "ms-kubernetes-tools.vscode-kubernetes-tools", "redhat.vscode-yaml" - ], + ], - // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [5000, 5001], + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [5000, 5001], - // [Optional] To reuse of your local HTTPS dev cert, first export it locally using this command: - // * Windows PowerShell: - // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // [Optional] To reuse of your local HTTPS dev cert, first export it locally using this command: + // * Windows PowerShell: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" // * macOS/Linux terminal: // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" // From a4f4d69cec252b2f095390603eb26c5fdadcae8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenjie=20Yu=EF=BC=88MSFT=EF=BC=89?= <81678720+zedy-wj@users.noreply.github.com> Date: Tue, 13 Dec 2022 10:30:30 +0800 Subject: [PATCH 16/22] Fix indent --- .devcontainer/devcontainer.json | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 375e927..e18a3aa 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -25,12 +25,12 @@ // Set *default* container specific settings.json values on container create. "settings": { - "terminal.integrated.shell.linux": "/bin/bash" + "terminal.integrated.shell.linux": "/bin/bash" }, // Add the IDs of extensions you want installed when the container is created. "extensions": [ - "ms-azuretools.azure-dev", + "ms-azuretools.azure-dev", "ms-dotnettools.csharp", "formulahendry.dotnet-test-explorer", "ms-vscode.vscode-node-azure-pack", @@ -45,20 +45,20 @@ // [Optional] To reuse of your local HTTPS dev cert, first export it locally using this command: // * Windows PowerShell: // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" - // * macOS/Linux terminal: - // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" - // - // Next, after running the command above, uncomment lines in the 'mounts' and 'remoteEnv' lines below, - // and open / rebuild the container so the settings take effect. - // - "mounts": [ - // "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind" - ], - "remoteEnv": { - // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere", - // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx", - } + // * macOS/Linux terminal: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // + // Next, after running the command above, uncomment lines in the 'mounts' and 'remoteEnv' lines below, + // and open / rebuild the container so the settings take effect. + // + "mounts": [ + // "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind" + ], + "remoteEnv": { + // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere", + // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx", + } - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "dotnet restore" + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "dotnet restore" } From 50a8268b2ec237ccd0acfe365be0824c5e9ef295 Mon Sep 17 00:00:00 2001 From: zedy Date: Tue, 13 Dec 2022 10:38:28 +0800 Subject: [PATCH 17/22] Remove invalid changes --- .devcontainer/Dockerfile | 1 - .../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 - infra/core/host/container-app.bicep | 77 - .../host/container-apps-environment.bicep | 26 - infra/core/host/container-apps.bicep | 30 - infra/core/host/container-registry.bicep | 36 - infra/core/host/functions.bicep | 82 -- infra/core/host/staticwebapp.bicep | 21 - .../applicationinsights-dashboard.bicep | 1235 ----------------- infra/core/monitor/applicationinsights.bicep | 30 - infra/core/monitor/loganalytics.bicep | 21 - infra/core/monitor/monitoring.bicep | 31 - infra/core/security/keyvault-access.bicep | 21 - infra/core/storage/storage-account.bicep | 38 - 20 files changed, 1906 deletions(-) delete mode 100644 infra/core/database/cosmos/cosmos-account.bicep delete mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep delete mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-account.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-db.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep delete mode 100644 infra/core/host/container-app.bicep delete mode 100644 infra/core/host/container-apps-environment.bicep delete mode 100644 infra/core/host/container-apps.bicep delete mode 100644 infra/core/host/container-registry.bicep delete mode 100644 infra/core/host/functions.bicep delete mode 100644 infra/core/host/staticwebapp.bicep delete mode 100644 infra/core/monitor/applicationinsights-dashboard.bicep delete mode 100644 infra/core/monitor/applicationinsights.bicep delete mode 100644 infra/core/monitor/loganalytics.bicep delete mode 100644 infra/core/monitor/monitoring.bicep delete mode 100644 infra/core/security/keyvault-access.bicep delete mode 100644 infra/core/storage/storage-account.bicep diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 279aebc..54d859f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -20,7 +20,6 @@ ENV NVM_DIR=/usr/local/share/nvm # [Optional] Install the Azure CLI ARG INSTALL_AZURE_CLI="false" -ARG INSTALL_AZURE_DEVELOPER_CLI="true" # Configure apt and install packages RUN curl -fsSL https://aka.ms/install-azd.sh | bash \ diff --git a/infra/core/database/cosmos/cosmos-account.bicep b/infra/core/database/cosmos/cosmos-account.bicep deleted file mode 100644 index 6bc1f2e..0000000 --- a/infra/core/database/cosmos/cosmos-account.bicep +++ /dev/null @@ -1,48 +0,0 @@ -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/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep deleted file mode 100644 index bd2a2b5..0000000 --- a/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep +++ /dev/null @@ -1,22 +0,0 @@ -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/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep deleted file mode 100644 index 2c9688e..0000000 --- a/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep +++ /dev/null @@ -1,46 +0,0 @@ -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/infra/core/database/cosmos/sql/cosmos-sql-account.bicep b/infra/core/database/cosmos/sql/cosmos-sql-account.bicep deleted file mode 100644 index e8b030f..0000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-account.bicep +++ /dev/null @@ -1,21 +0,0 @@ -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/infra/core/database/cosmos/sql/cosmos-sql-db.bicep b/infra/core/database/cosmos/sql/cosmos-sql-db.bicep deleted file mode 100644 index 5a4de20..0000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-db.bicep +++ /dev/null @@ -1,73 +0,0 @@ -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/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep deleted file mode 100644 index 6855edf..0000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep +++ /dev/null @@ -1,18 +0,0 @@ -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/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep deleted file mode 100644 index cfb4033..0000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep +++ /dev/null @@ -1,29 +0,0 @@ -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/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep deleted file mode 100644 index dde1bab..0000000 --- a/infra/core/host/container-app.bicep +++ /dev/null @@ -1,77 +0,0 @@ -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/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep deleted file mode 100644 index 2dd858c..0000000 --- a/infra/core/host/container-apps-environment.bicep +++ /dev/null @@ -1,26 +0,0 @@ -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/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep deleted file mode 100644 index 395af70..0000000 --- a/infra/core/host/container-apps.bicep +++ /dev/null @@ -1,30 +0,0 @@ -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/infra/core/host/container-registry.bicep b/infra/core/host/container-registry.bicep deleted file mode 100644 index 01c3213..0000000 --- a/infra/core/host/container-registry.bicep +++ /dev/null @@ -1,36 +0,0 @@ -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/infra/core/host/functions.bicep b/infra/core/host/functions.bicep deleted file mode 100644 index 28a581b..0000000 --- a/infra/core/host/functions.bicep +++ /dev/null @@ -1,82 +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) -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/infra/core/host/staticwebapp.bicep b/infra/core/host/staticwebapp.bicep deleted file mode 100644 index 91c2d0d..0000000 --- a/infra/core/host/staticwebapp.bicep +++ /dev/null @@ -1,21 +0,0 @@ -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/infra/core/monitor/applicationinsights-dashboard.bicep b/infra/core/monitor/applicationinsights-dashboard.bicep deleted file mode 100644 index b7af2c1..0000000 --- a/infra/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/infra/core/monitor/applicationinsights.bicep b/infra/core/monitor/applicationinsights.bicep deleted file mode 100644 index f76b292..0000000 --- a/infra/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/infra/core/monitor/loganalytics.bicep b/infra/core/monitor/loganalytics.bicep deleted file mode 100644 index 770544c..0000000 --- a/infra/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/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep deleted file mode 100644 index 96ba11e..0000000 --- a/infra/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/infra/core/security/keyvault-access.bicep b/infra/core/security/keyvault-access.bicep deleted file mode 100644 index 96c9cf7..0000000 --- a/infra/core/security/keyvault-access.bicep +++ /dev/null @@ -1,21 +0,0 @@ -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/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep deleted file mode 100644 index a41972c..0000000 --- a/infra/core/storage/storage-account.bicep +++ /dev/null @@ -1,38 +0,0 @@ -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 From a4427bcbf9735b654c12d190767b30d7206f3870 Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 14 Dec 2022 15:32:14 +0800 Subject: [PATCH 18/22] Pull the connection string from key vault --- .devcontainer/devcontainer.json | 3 +++ .vscode/extensions.json | 3 ++- infra/main.bicep | 14 +++++++++++-- src/Infrastructure/Dependencies.cs | 26 ++++++++++++++++++++++-- src/Infrastructure/Infrastructure.csproj | 2 ++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e18a3aa..046068a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,6 +12,9 @@ } }, "features": { + "ghcr.io/devcontainers/features/azure-cli:1": { + "version": "2.38" + }, "ghcr.io/devcontainers/features/docker-from-docker:1": { "version": "20.10" }, diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d0663c2..680470c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,7 @@ "formulahendry.dotnet-test-explorer", "ms-vscode.vscode-node-azure-pack", "ms-kubernetes-tools.vscode-kubernetes-tools", - "redhat.vscode-yaml" + "redhat.vscode-yaml", + "ms-azuretools.azure-dev" ] } \ No newline at end of file diff --git a/infra/main.bicep b/infra/main.bicep index 535ded5..bdf877a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -56,8 +56,9 @@ module web './core/host/appservice.bicep' = { runtimeVersion: '6.0' tags: union(tags, { 'azd-service-name': 'web' }) appSettings: { - CATALOG_CONNECTION_STRING_VALUE: '${catalogDb.outputs.connectionString}; Password=${appUserPassword}' - IDENTITY_CONNECTION_STRING_VALUE: '${identityDb.outputs.connectionString}; Password=${appUserPassword}' + CATALOG_CONNECTION_STRING_KEY: 'AZURE-SQL-CATALOG-CONNECTION-STRING' + IDENTITY_CONNECTION_STRING_KEY: 'AZURE-SQL-IDENTITY-CONNECTION-STRING' + KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint } } } @@ -120,5 +121,14 @@ module appServicePlan './core/host/appserviceplan.bicep' = { } } +// Data outputs +output AZURE_SQL_CATALOG_CONNECTION_STRING string = catalogDb.outputs.connectionStringKey +output AZURE_SQL_IDENTITY_CONNECTION_STRING string = identityDb.outputs.connectionStringKey +output AZURE_SQL_CATALOG_DATABASE_NAME string = catalogDb.outputs.databaseName +output AZURE_SQL_IDENTITY_DATABASE_NAME string = identityDb.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/src/Infrastructure/Dependencies.cs b/src/Infrastructure/Dependencies.cs index d399531..ac319d2 100644 --- a/src/Infrastructure/Dependencies.cs +++ b/src/Infrastructure/Dependencies.cs @@ -3,6 +3,9 @@ using Microsoft.eShopWeb.Infrastructure.Data; using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Azure.Security.KeyVault.Secrets; +using Azure.Identity; +using System; namespace Microsoft.eShopWeb.Infrastructure; @@ -11,6 +14,12 @@ public static class Dependencies public static void ConfigureServices(IConfiguration configuration, IServiceCollection services) { var useOnlyInMemoryDatabase = false; + string keyVaultUri = configuration["KEY_VAULT_ENDPOINT"]; + string catalogConnectionStringKey = configuration["AZURE-SQL-CATALOG-CONNECTION-STRING"]; + string identityConnectionStringKey = configuration["AZURE-SQL-IDENTITY-CONNECTION-STRING"]; + string catalogConnectionStringValue = GetSqlConnectString(keyVaultUri, catalogConnectionStringKey); + string identityConnectionStringValue = GetSqlConnectString(keyVaultUri, identityConnectionStringKey); + if (configuration["UseOnlyInMemoryDatabase"] != null) { useOnlyInMemoryDatabase = bool.Parse(configuration["UseOnlyInMemoryDatabase"]); @@ -30,11 +39,24 @@ public static class Dependencies // Requires LocalDB which can be installed with SQL Server Express 2016 // https://www.microsoft.com/en-us/download/details.aspx?id=54284 services.AddDbContext(c => - c.UseSqlServer(configuration["CATALOG_CONNECTION_STRING_VALUE"])); + c.UseSqlServer(catalogConnectionStringValue)); // Add Identity DbContext services.AddDbContext(options => - options.UseSqlServer(configuration["IDENTITY_CONNECTION_STRING_VALUE"])); + options.UseSqlServer(identityConnectionStringValue)); } } + + public static string GetSqlConnectString(string keyVaultUri, string connectionStringKey) + { + if (connectionStringKey == null) + { + return ""; + } + + var secretClient = new SecretClient(new Uri(keyVaultUri), new ClientSecretCredential("","","")); + KeyVaultSecret secret = secretClient.GetSecret(connectionStringKey); + string secretValue = secret.Value; + return secretValue; + } } diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index a0be8bb..4c7a052 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -8,6 +8,8 @@ + + From 9e821608a1071460796a6e2989bd713e860e75f7 Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 14 Dec 2022 15:58:45 +0800 Subject: [PATCH 19/22] fix some bug --- infra/main.bicep | 6 +++--- src/Infrastructure/Dependencies.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index bdf877a..d1187f7 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -56,9 +56,9 @@ module web './core/host/appservice.bicep' = { runtimeVersion: '6.0' tags: union(tags, { 'azd-service-name': 'web' }) appSettings: { - CATALOG_CONNECTION_STRING_KEY: 'AZURE-SQL-CATALOG-CONNECTION-STRING' - IDENTITY_CONNECTION_STRING_KEY: 'AZURE-SQL-IDENTITY-CONNECTION-STRING' - KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint + AZURE_CATALOG_CONNECTION_STRING_KEY: 'AZURE-SQL-CATALOG-CONNECTION-STRING' + AZURE_IDENTITY_CONNECTION_STRING_KEY: 'AZURE-SQL-IDENTITY-CONNECTION-STRING' + AZURE_KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint } } } diff --git a/src/Infrastructure/Dependencies.cs b/src/Infrastructure/Dependencies.cs index ac319d2..7218310 100644 --- a/src/Infrastructure/Dependencies.cs +++ b/src/Infrastructure/Dependencies.cs @@ -14,9 +14,9 @@ public static class Dependencies public static void ConfigureServices(IConfiguration configuration, IServiceCollection services) { var useOnlyInMemoryDatabase = false; - string keyVaultUri = configuration["KEY_VAULT_ENDPOINT"]; - string catalogConnectionStringKey = configuration["AZURE-SQL-CATALOG-CONNECTION-STRING"]; - string identityConnectionStringKey = configuration["AZURE-SQL-IDENTITY-CONNECTION-STRING"]; + string keyVaultUri = configuration["AZURE_KEY_VAULT_ENDPOINT"]; + string catalogConnectionStringKey = configuration["AZURE_CATALOG_CONNECTION_STRING_KEY"]; + string identityConnectionStringKey = configuration["AZURE_IDENTITY_CONNECTION_STRING_KEY"]; string catalogConnectionStringValue = GetSqlConnectString(keyVaultUri, catalogConnectionStringKey); string identityConnectionStringValue = GetSqlConnectString(keyVaultUri, identityConnectionStringKey); From d4950760f21ac2cc42449a73c7101fcf5f16eb12 Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 15 Dec 2022 14:29:40 +0800 Subject: [PATCH 20/22] Remove some invalid changes --- infra/core/security/keyvault-access.bicep | 21 +++++++++++++++++++ infra/main.bicep | 18 ++++++++++++---- src/Infrastructure/Dependencies.cs | 25 ++--------------------- src/Infrastructure/Infrastructure.csproj | 2 -- 4 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 infra/core/security/keyvault-access.bicep diff --git a/infra/core/security/keyvault-access.bicep b/infra/core/security/keyvault-access.bicep new file mode 100644 index 0000000..96c9cf7 --- /dev/null +++ b/infra/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/infra/main.bicep b/infra/main.bicep index d1187f7..b8c5ebb 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -52,17 +52,27 @@ module web './core/host/appservice.bicep' = { name: !empty(webServiceName) ? webServiceName : '${abbrs.webSitesAppService}web-${resourceToken}' location: location appServicePlanId: appServicePlan.outputs.id + keyVaultName: keyVault.outputs.name runtimeName: 'dotnetcore' runtimeVersion: '6.0' tags: union(tags, { 'azd-service-name': 'web' }) appSettings: { - AZURE_CATALOG_CONNECTION_STRING_KEY: 'AZURE-SQL-CATALOG-CONNECTION-STRING' - AZURE_IDENTITY_CONNECTION_STRING_KEY: 'AZURE-SQL-IDENTITY-CONNECTION-STRING' + AZURE_SQL_CATALOG_CONNECTION_STRING_KEY: 'AZURE-SQL-CATALOG-CONNECTION-STRING' + AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY: 'AZURE-SQL-IDENTITY-CONNECTION-STRING' AZURE_KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint } } } +module apiKeyVaultAccess './core/security/keyvault-access.bicep' = { + name: 'api-keyvault-access' + scope: rg + params: { + keyVaultName: keyVault.outputs.name + principalId: web.outputs.identityPrincipalId + } +} + // The application database: Catalog module catalogDb './core/database/sqlserver/sqlserver.bicep' = { name: 'sql-catalog' @@ -122,8 +132,8 @@ module appServicePlan './core/host/appserviceplan.bicep' = { } // Data outputs -output AZURE_SQL_CATALOG_CONNECTION_STRING string = catalogDb.outputs.connectionStringKey -output AZURE_SQL_IDENTITY_CONNECTION_STRING string = identityDb.outputs.connectionStringKey +output AZURE_SQL_CATALOG_CONNECTION_STRING_KEY string = catalogDb.outputs.connectionStringKey +output AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY string = identityDb.outputs.connectionStringKey output AZURE_SQL_CATALOG_DATABASE_NAME string = catalogDb.outputs.databaseName output AZURE_SQL_IDENTITY_DATABASE_NAME string = identityDb.outputs.databaseName diff --git a/src/Infrastructure/Dependencies.cs b/src/Infrastructure/Dependencies.cs index 7218310..954da48 100644 --- a/src/Infrastructure/Dependencies.cs +++ b/src/Infrastructure/Dependencies.cs @@ -3,9 +3,6 @@ using Microsoft.eShopWeb.Infrastructure.Data; using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Azure.Security.KeyVault.Secrets; -using Azure.Identity; -using System; namespace Microsoft.eShopWeb.Infrastructure; @@ -14,11 +11,6 @@ public static class Dependencies public static void ConfigureServices(IConfiguration configuration, IServiceCollection services) { var useOnlyInMemoryDatabase = false; - string keyVaultUri = configuration["AZURE_KEY_VAULT_ENDPOINT"]; - string catalogConnectionStringKey = configuration["AZURE_CATALOG_CONNECTION_STRING_KEY"]; - string identityConnectionStringKey = configuration["AZURE_IDENTITY_CONNECTION_STRING_KEY"]; - string catalogConnectionStringValue = GetSqlConnectString(keyVaultUri, catalogConnectionStringKey); - string identityConnectionStringValue = GetSqlConnectString(keyVaultUri, identityConnectionStringKey); if (configuration["UseOnlyInMemoryDatabase"] != null) { @@ -39,24 +31,11 @@ public static class Dependencies // Requires LocalDB which can be installed with SQL Server Express 2016 // https://www.microsoft.com/en-us/download/details.aspx?id=54284 services.AddDbContext(c => - c.UseSqlServer(catalogConnectionStringValue)); + c.UseSqlServer(configuration.GetConnectionString("CatalogConnection"))); // Add Identity DbContext services.AddDbContext(options => - options.UseSqlServer(identityConnectionStringValue)); + options.UseSqlServer(configuration.GetConnectionString("IdentityConnection"))); } } - - public static string GetSqlConnectString(string keyVaultUri, string connectionStringKey) - { - if (connectionStringKey == null) - { - return ""; - } - - var secretClient = new SecretClient(new Uri(keyVaultUri), new ClientSecretCredential("","","")); - KeyVaultSecret secret = secretClient.GetSecret(connectionStringKey); - string secretValue = secret.Value; - return secretValue; - } } diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index 4c7a052..a0be8bb 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -8,8 +8,6 @@ - - From 9a13af426b2f6a9514c4ecfd814c4c1c187c7eaa Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 15 Dec 2022 15:41:24 +0800 Subject: [PATCH 21/22] Fix format and update the method of pulling connection string from kv with dac --- src/Infrastructure/Dependencies.cs | 1 - src/Web/AzureDeveloperCliCredential.cs | 148 +++++++++++++++++++++++++ src/Web/Program.cs | 16 ++- src/Web/Web.csproj | 2 + 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/Web/AzureDeveloperCliCredential.cs diff --git a/src/Infrastructure/Dependencies.cs b/src/Infrastructure/Dependencies.cs index 954da48..d049a96 100644 --- a/src/Infrastructure/Dependencies.cs +++ b/src/Infrastructure/Dependencies.cs @@ -11,7 +11,6 @@ public static class Dependencies public static void ConfigureServices(IConfiguration configuration, IServiceCollection services) { var useOnlyInMemoryDatabase = false; - if (configuration["UseOnlyInMemoryDatabase"] != null) { useOnlyInMemoryDatabase = bool.Parse(configuration["UseOnlyInMemoryDatabase"]); diff --git a/src/Web/AzureDeveloperCliCredential.cs b/src/Web/AzureDeveloperCliCredential.cs new file mode 100644 index 0000000..406c7bf --- /dev/null +++ b/src/Web/AzureDeveloperCliCredential.cs @@ -0,0 +1,148 @@ +using Azure.Core; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.RegularExpressions; + +namespace Azure.Identity +{ + public class AzureDeveloperCliCredential : TokenCredential + { + internal const string AzdCliNotInstalled = $"Azure Developer CLI could not be found. {Troubleshoot}"; + internal const string AzdNotLogIn = "Please run 'azd login' from a command prompt to authenticate before using this credential."; + internal const string WinAzdCliError = "'azd is not recognized"; + internal const string AzdCliTimeoutError = "Azure Developer CLI authentication timed out."; + internal const string AzdCliFailedError = "Azure Developer CLI authentication failed due to an unknown error."; + internal const string Troubleshoot = "Please visit https://aka.ms/azure-dev for installation instructions and then, once installed, authenticate to your Azure account using 'azd login'."; + internal const string InteractiveLoginRequired = "Azure Developer CLI could not login. Interactive login is required."; + private const string RefreshTokeExpired = "The provided authorization code or refresh token has expired due to inactivity. Send a new interactive authorization request for this user and resource."; + + private static readonly string DefaultWorkingDirWindows = Environment.GetFolderPath(Environment.SpecialFolder.System); + private const string DefaultWorkingDirNonWindows = "/bin/"; + private static readonly string DefaultWorkingDir = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? DefaultWorkingDirWindows : DefaultWorkingDirNonWindows; + + private static readonly Regex AzdNotFoundPattern = new Regex("azd:(.*)not found"); + + public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken = default) + { + return RequestCliAccessTokenAsync(requestContext, cancellationToken) + .GetAwaiter() + .GetResult(); + } + + public override async ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken = default) + { + return await RequestCliAccessTokenAsync(requestContext, cancellationToken).ConfigureAwait(false); + } + + private async ValueTask RequestCliAccessTokenAsync(TokenRequestContext context, CancellationToken cancellationToken) + { + try + { + ProcessStartInfo processStartInfo = GetAzdCliProcessStartInfo(context.Scopes); + string output = await RunProcessAsync(processStartInfo); + + return DeserializeOutput(output); + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + throw new AuthenticationFailedException(AzdCliTimeoutError); + } + catch (InvalidOperationException exception) + { + bool isWinError = exception.Message.StartsWith(WinAzdCliError, StringComparison.CurrentCultureIgnoreCase); + bool isOtherOsError = AzdNotFoundPattern.IsMatch(exception.Message); + + if (isWinError || isOtherOsError) + { + throw new CredentialUnavailableException(AzdCliNotInstalled); + } + + bool isAADSTSError = exception.Message.Contains("AADSTS"); + bool isLoginError = exception.Message.IndexOf("azd login", StringComparison.OrdinalIgnoreCase) != -1; + + if (isLoginError && !isAADSTSError) + { + throw new CredentialUnavailableException(AzdNotLogIn); + } + + bool isRefreshTokenFailedError = exception.Message.IndexOf(AzdCliFailedError, StringComparison.OrdinalIgnoreCase) != -1 && + exception.Message.IndexOf(RefreshTokeExpired, StringComparison.OrdinalIgnoreCase) != -1 || + exception.Message.IndexOf("CLIInternalError", StringComparison.OrdinalIgnoreCase) != -1; + + if (isRefreshTokenFailedError) + { + throw new CredentialUnavailableException(InteractiveLoginRequired); + } + + throw new AuthenticationFailedException($"{AzdCliFailedError} {Troubleshoot} {exception.Message}"); + } + catch (Exception ex) + { + throw new CredentialUnavailableException($"{AzdCliFailedError} {Troubleshoot} {ex.Message}"); + } + } + + private async ValueTask RunProcessAsync(ProcessStartInfo processStartInfo, CancellationToken cancellationToken = default) + { + var process = Process.Start(processStartInfo); + if (process == null) + { + throw new CredentialUnavailableException(AzdCliFailedError); + } + + await process.WaitForExitAsync(cancellationToken); + + if (process.ExitCode != 0) + { + var errorMessage = process.StandardError.ReadToEnd(); + throw new InvalidOperationException(errorMessage); + } + + return process.StandardOutput.ReadToEnd(); + } + + private ProcessStartInfo GetAzdCliProcessStartInfo(string[] scopes) + { + string scopeArgs = string.Join(" ", scopes.Select(scope => string.Format($"--scope {scope}"))); + string command = $"azd auth token --output json {scopeArgs}"; + + string fileName; + string argument; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe"); + argument = $"/c \"{command}\""; + } + else + { + fileName = "/bin/sh"; + argument = $"-c \"{command}\""; + } + + return new ProcessStartInfo + { + FileName = fileName, + Arguments = argument, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + ErrorDialog = false, + CreateNoWindow = true, + WorkingDirectory = DefaultWorkingDir, + }; + } + + private static AccessToken DeserializeOutput(string output) + { + using JsonDocument document = JsonDocument.Parse(output); + + JsonElement root = document.RootElement; + string accessToken = root.GetProperty("token").GetString(); + DateTimeOffset expiresOn = root.GetProperty("expiresOn").GetDateTimeOffset(); + + return new AccessToken(accessToken, expiresOn); + } + } +} diff --git a/src/Web/Program.cs b/src/Web/Program.cs index e1181e2..a998b28 100644 --- a/src/Web/Program.cs +++ b/src/Web/Program.cs @@ -1,5 +1,6 @@ using System.Net.Mime; using Ardalis.ListStartupServices; +using Azure.Identity; using BlazorAdmin; using BlazorAdmin.Services; using Blazored.LocalStorage; @@ -8,6 +9,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.EntityFrameworkCore; using Microsoft.eShopWeb; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.Infrastructure.Data; @@ -21,7 +23,19 @@ var builder = WebApplication.CreateBuilder(args); builder.Logging.AddConsole(); -Microsoft.eShopWeb.Infrastructure.Dependencies.ConfigureServices(builder.Configuration, builder.Services); +// Configure SQL Server +var credential = new ChainedTokenCredential(new AzureDeveloperCliCredential(), new DefaultAzureCredential()); +builder.Configuration.AddAzureKeyVault(new Uri(builder.Configuration["AZURE_KEY_VAULT_ENDPOINT"]), credential); +builder.Services.AddDbContext(c => +{ + var connectionString = builder.Configuration[builder.Configuration["AZURE_SQL_CATALOG_CONNECTION_STRING_KEY"]]; + c.UseSqlServer(connectionString, sqlOptions => sqlOptions.EnableRetryOnFailure()); +}); +builder.Services.AddDbContext(options => +{ + var connectionString = builder.Configuration[builder.Configuration["AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY"]]; + options.UseSqlServer(connectionString, sqlOptions => sqlOptions.EnableRetryOnFailure()); +}); builder.Services.AddCookieSettings(); diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index 1d06eb0..6ec695d 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -17,6 +17,8 @@ + + From fc3f7b27469c1370cc9a14f99c3aa37c4eaf33a3 Mon Sep 17 00:00:00 2001 From: zedy Date: Tue, 31 Jan 2023 10:50:21 +0800 Subject: [PATCH 22/22] Remove invalid files --- scripts/setup-database.ps1 | 9 --------- scripts/setup-database.sh | 12 ------------ 2 files changed, 21 deletions(-) delete mode 100644 scripts/setup-database.ps1 delete mode 100644 scripts/setup-database.sh diff --git a/scripts/setup-database.ps1 b/scripts/setup-database.ps1 deleted file mode 100644 index 8969760..0000000 --- a/scripts/setup-database.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -# Default to current directory -$FOLDER_PATH = [string](Get-Location) - -# Before setting up your database, make sure of two things: -# 1. Ensure your connection strings in appsettings.json point to a local SQL Server instance. -# 2. Ensure the tool EF was already installed. -dotnet tool update --global dotnet-ef -dotnet ef database update -c catalogcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj -dotnet ef database update -c appidentitydbcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj \ No newline at end of file diff --git a/scripts/setup-database.sh b/scripts/setup-database.sh deleted file mode 100644 index abd4e60..0000000 --- a/scripts/setup-database.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# Default to current directory -FOLDER_PATH=$(pwd) - - -# Before setting up your database, make sure of two things: -# 1. Ensure your connection strings in appsettings.json point to a local SQL Server instance. -# 2. Ensure the tool EF was already installed. -dotnet tool update --global dotnet-ef -dotnet ef database update -c catalogcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj -dotnet ef database update -c appidentitydbcontext -p $FOLDER_PATH/src/Infrastructure/Infrastructure.csproj -s $FOLDER_PATH/src/Web/Web.csproj \ No newline at end of file