From 2312b656f9e2057270c684321e4a31c57907469e Mon Sep 17 00:00:00 2001 From: asleboon Date: Thu, 23 Jan 2025 13:38:16 +0100 Subject: [PATCH] feat: infra - vnet, private endpoint and custom domain --- infra/container-api-prod.bicep | 93 ++++++++++++++++++++---- infra/container-frontend-prod.bicep | 64 +++++++++++++--- infra/container-frontend-test.bicep | 5 ++ infra/main-prod.bicep | 16 ++++ infra/main-test.bicep | 16 ++++ infra/modules/containerAppFrontend.bicep | 4 +- infra/modules/containerEnv.bicep | 10 +++ infra/modules/sqlServer.bicep | 56 +++++++++++++- 8 files changed, 234 insertions(+), 30 deletions(-) diff --git a/infra/container-api-prod.bicep b/infra/container-api-prod.bicep index 422963ca..d22f59ff 100644 --- a/infra/container-api-prod.bicep +++ b/infra/container-api-prod.bicep @@ -8,7 +8,7 @@ param managedIdentityName string = 'bds-prod-managedidentity' param keyVaultName string = 'bds-prod-keyvault' @description('The name of the container environment.') -param containerName string = 'bds-prod-containerenv-api' +param containerEnvName string = 'bds-prod-containerenv-api' @description('The name of the container app.') param containerAppName string = 'bds-prod-containerapp-api' @@ -16,6 +16,9 @@ param containerAppName string = 'bds-prod-containerapp-api' @description('The name of the Log Analytics workspace.') param logAnalyticsWorkspaceName string = 'bds-prod-loganalytics' +@description('The name of the VNet.') +param vnetName string = 'bds-prod-vnet' + @description('The name of the ACR login server.') param acrServer string @@ -30,28 +33,86 @@ param acrPassword string param containerImage string module containerEnv 'modules/containerEnv.bicep' = { - name: containerName + name: containerEnvName params: { - containerAppEnvironmentName: containerName + vnetName: vnetName + containerAppEnvironmentName: containerEnvName location: location logAnalyticsWorkspaceName: logAnalyticsWorkspaceName } } -module containerApp 'modules/containerApp.bicep' = { +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = { + name: managedIdentityName +} + +resource containerApp 'Microsoft.App/containerApps@2023-08-01-preview' = { name: containerAppName - params: { - location: location - managedIdentityName: managedIdentityName - keyVaultName: keyVaultName - containerAppName: containerAppName - containerAppEnvironmentId: containerEnv.outputs.id - acrLoginServer: acrServer - acrUsername: acrUsername - acrPassword: acrPassword - containerImage: containerImage - targetPort: 5001 + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + managedEnvironmentId: containerEnv.outputs.id + configuration: { + activeRevisionsMode: 'Multiple' + ingress: { + external: true + targetPort: 5001 + allowInsecure: false + customDomains: [ + { + bindingType: 'SniEnabled' + certificateId: 'surveyapi.bouvetapps.io-bds-prod-250120104816' + name: 'surveyapi.bouvetapps.io' + } + ] + } + registries: [ + { + server: acrServer + passwordSecretRef: 'container-registry-password' + username: acrUsername + } + ] + secrets: [ + { + name: 'container-registry-password' + value: acrPassword + } + { + name: 'sql-server-connection-string' // github workflow will fail if this does not exist. + keyVaultUrl: 'https://${keyVaultName}.vault.azure.net/secrets/sql-server-connection-string' + identity: managedIdentity.id + } + ] + } + template: { + containers: [ + { + name: containerAppName + image: '${acrServer}/backend-image:${containerImage}' + resources: { + cpu: json('0.5') + memory: '1Gi' + } + env: [ + { + name: 'ConnectionString' + secretRef: 'sql-server-connection-string' + } + ] + } + ] + scale: { + minReplicas: 0 + maxReplicas: 2 + } + } } } -output fqdn string = containerApp.outputs.fqdn +output fqdn string = containerApp.properties.configuration.ingress.fqdn diff --git a/infra/container-frontend-prod.bicep b/infra/container-frontend-prod.bicep index 86e1e3bf..c73fbe5d 100644 --- a/infra/container-frontend-prod.bicep +++ b/infra/container-frontend-prod.bicep @@ -16,6 +16,9 @@ param acrServer string @description('The name of the ACR username.') param acrUsername string +@description('The name of the VNet.') +param vnetName string = 'bds-prod-vnet' + @secure() @description('The name of the ACR password.') param acrPassword string @@ -26,24 +29,63 @@ param containerImage string module containerEnv 'modules/containerEnv.bicep' = { name: containerName params: { + vnetName: vnetName containerAppEnvironmentName: containerName location: location logAnalyticsWorkspaceName: logAnalyticsWorkspaceName } } -module containerApp 'modules/containerAppFrontend.bicep' = { +resource containerApp 'Microsoft.App/containerApps@2023-08-01-preview' = { name: containerAppName - params: { - location: location - containerAppName: containerAppName - containerAppEnvironmentId: containerEnv.outputs.id - acrLoginServer: acrServer - acrUsername: acrUsername - acrPassword: acrPassword - containerImage: containerImage - targetPort: 3000 + location: location + properties: { + managedEnvironmentId: containerEnv.outputs.id + configuration: { + activeRevisionsMode: 'Multiple' + ingress: { + external: true + targetPort: 3000 + allowInsecure: false + customDomains: [ + { + bindingType: 'SniEnabled' + certificateId: 'survey.bouvetapps.io-bds-prod-250120105916' + name: 'survey.bouvetapps.io' + } + ] + } + secrets: [ + { + name: 'container-registry-password' + value: acrPassword + } + ] + registries: [ + { + server: acrServer + passwordSecretRef: 'container-registry-password' + username: acrUsername + } + ] + } + template: { + containers: [ + { + name: containerAppName + image: containerImage + resources: { + cpu: json('0.5') + memory: '1Gi' + } + } + ] + scale: { + minReplicas: 0 + maxReplicas: 2 + } + } } } -output fqdn string = containerApp.outputs.fqdn +output fqdn string = containerApp.properties.configuration.ingress.fqdn diff --git a/infra/container-frontend-test.bicep b/infra/container-frontend-test.bicep index 5d724f38..f9b09e3b 100644 --- a/infra/container-frontend-test.bicep +++ b/infra/container-frontend-test.bicep @@ -10,12 +10,16 @@ param containerAppName string = 'bds-test-containerapp-frontend' @description('The name of the Log Analytics workspace.') param logAnalyticsWorkspaceName string = 'bds-test-loganalytics' +@description('The name of the VNet.') +param vnetName string = 'bds-test-vnet' + @description('The name of the ACR login server.') param acrServer string @description('The name of the ACR username.') param acrUsername string + @secure() @description('The name of the ACR password.') param acrPassword string @@ -26,6 +30,7 @@ param containerImage string module containerEnv 'modules/containerEnv.bicep' = { name: containerName params: { + vnetName: vnetName containerAppEnvironmentName: containerName location: location logAnalyticsWorkspaceName: logAnalyticsWorkspaceName diff --git a/infra/main-prod.bicep b/infra/main-prod.bicep index 7f34fc25..e0484c68 100644 --- a/infra/main-prod.bicep +++ b/infra/main-prod.bicep @@ -19,6 +19,18 @@ param sqlDBName string = 'bds-prod-sqldb' @description('The username for the SQL Server.') param sqlServerUsername string = 'bdsadmin' +@description('The name of the VNet') +param vnetName string = 'bds-prod-vnet' + +@description('The name of the subnet') +param subnetName string = 'bds-prod-subnet' + +@description('The name of the private endpoint') +param sqlPrivateEndpointName string = 'bds-prod-privateendpoint-sqlserver' + +@description('The name of the private endpoint connection') +param sqlServerConnectionName string = 'bds-prod-sqlserver-connection' + @secure() @description('The administrator password used for the sql server instance created.') param sqlServerPassword string @@ -43,6 +55,10 @@ module keyVault 'modules/keyVault.bicep' = { module sqlServer 'modules/sqlServer.bicep' = { name: serverName params: { + vnetName: vnetName + subnetName: subnetName + sqlPrivateEndpointName: sqlPrivateEndpointName + sqlServerConnectionName: sqlServerConnectionName serverName: serverName sqlDBName: sqlDBName location: location diff --git a/infra/main-test.bicep b/infra/main-test.bicep index ce3d9edf..bb57a8e1 100644 --- a/infra/main-test.bicep +++ b/infra/main-test.bicep @@ -19,6 +19,18 @@ param sqlDBName string = 'bds-test-sqldb' @description('The username for the SQL Server.') param sqlServerUsername string = 'bdsadmin' +@description('The name of the VNet') +param vnetName string = 'bds-test-vnet' + +@description('The name of the subnet') +param subnetName string = 'bds-test-subnet' + +@description('The name of the private endpoint') +param sqlPrivateEndpointName string = 'bds-test-privateendpoint-sqlserver' + +@description('The name of the private endpoint connection') +param sqlServerConnectionName string = 'bds-test-sqlserver-connection' + @secure() @description('The administrator password used for the sql server instance created.') param sqlServerPassword string @@ -43,6 +55,10 @@ module keyVault 'modules/keyVault.bicep' = { module sqlServer 'modules/sqlServer.bicep' = { name: serverName params: { + vnetName: vnetName + subnetName: subnetName + sqlPrivateEndpointName: sqlPrivateEndpointName + sqlServerConnectionName: sqlServerConnectionName serverName: serverName sqlDBName: sqlDBName location: location diff --git a/infra/modules/containerAppFrontend.bicep b/infra/modules/containerAppFrontend.bicep index f8c77f3a..998e8adf 100644 --- a/infra/modules/containerAppFrontend.bicep +++ b/infra/modules/containerAppFrontend.bicep @@ -61,8 +61,8 @@ resource containerApp 'Microsoft.App/containerApps@2023-08-01-preview' = { } ] scale: { - minReplicas: 1 - maxReplicas: 3 + minReplicas: 0 + maxReplicas: 2 } } } diff --git a/infra/modules/containerEnv.bicep b/infra/modules/containerEnv.bicep index e50326dc..6124922b 100644 --- a/infra/modules/containerEnv.bicep +++ b/infra/modules/containerEnv.bicep @@ -7,6 +7,13 @@ param location string @description('Log analytics workspace name') param logAnalyticsWorkspaceName string +@description('The name of the VNet') +param vnetName string + +resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' existing = { + name: vnetName +} + resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location @@ -21,6 +28,9 @@ resource env 'Microsoft.App/managedEnvironments@2023-08-01-preview' = { name: containerAppEnvironmentName location: location properties: { + vnetConfiguration: { + infrastructureSubnetId: vnet.properties.subnets[0].id + } appLogsConfiguration: { destination: 'log-analytics' logAnalyticsConfiguration: { diff --git a/infra/modules/sqlServer.bicep b/infra/modules/sqlServer.bicep index b120ff35..494f188a 100644 --- a/infra/modules/sqlServer.bicep +++ b/infra/modules/sqlServer.bicep @@ -7,6 +7,18 @@ param sqlDBName string @description('The location for the SQL Server.') param location string +@description('The name of the VNet') +param vnetName string + +@description('The name of the subnet') +param subnetName string + +@description('The name of the private endpoint') +param sqlPrivateEndpointName string + +@description('The name of the private endpoint connection') +param sqlServerConnectionName string + @description('The administrator login for the SQL Server.') param administratorLogin string @@ -33,8 +45,50 @@ resource sqlDB 'Microsoft.Sql/servers/databases@2022-05-01-preview' = { capacity: 1 family: 'Gen5' } - properties:{ + properties: { autoPauseDelay: 60 minCapacity: 1 } } + +resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: vnetName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.0.0.0/24' // 256 ip addresses in total + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: '10.0.1.0/27' // subnet with 32 addresses + } + } + ] + } +} + +resource sqlPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { + name: sqlPrivateEndpointName + location: location + properties: { + subnet: { + id: vnet.properties.subnets[0].id + } + privateLinkServiceConnections: [ + { + name: sqlServerConnectionName + properties: { + privateLinkServiceConnectionState: { + status: 'Approved' + description: 'Private endpoint for SQL server' + } + privateLinkServiceId: sqlServer.id + } + } + ] + } +}