From 2e50a80f6100597462f73b2a96cccd311b39bd65 Mon Sep 17 00:00:00 2001 From: ahmeds Date: Tue, 13 Jul 2021 14:53:47 +0200 Subject: [PATCH 01/49] cleaned up windows vm create and custom script --- deployment/bicep/main.bicep | 100 ++++++++++++++++++ deployment/bicep/parameters.json | 25 +++++ deployment/bicep/shared/createvmwindows.bicep | 100 ++++++++++++++++++ deployment/bicep/shared/shared.bicep | 57 ++++++++++ deployment/bicep/shared/vm-nic.bicep | 35 ++++++ 5 files changed, 317 insertions(+) create mode 100644 deployment/bicep/main.bicep create mode 100644 deployment/bicep/parameters.json create mode 100644 deployment/bicep/shared/createvmwindows.bicep create mode 100644 deployment/bicep/shared/shared.bicep create mode 100644 deployment/bicep/shared/vm-nic.bicep diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep new file mode 100644 index 00000000..3a2a2e57 --- /dev/null +++ b/deployment/bicep/main.bicep @@ -0,0 +1,100 @@ +targetScope='subscription' +param workloadName string +param location string = deployment().location +@description('The-- environment for which the deployment is being executed') +@allowed([ + 'dev' + 'uat' + 'prod' + 'dr' +]) +param environment string + +// parameters for azure devops agent +param vmazdevopsUsername string +param vmazdevopsPassword string +param azureDevOpsAccount string +param personalAccessToken string + +// Variables +var resourceSuffix = '${workloadName}-${environment}-${location}-001' +var vmSuffix=environment +// RG Names Declaration +var sharedResourceGroupName = 'rg-shared-${resourceSuffix}' +var aseResourceGroupName = 'rg-ase-${resourceSuffix}' +// Create resources name using these objects and pass it as a params in module +var sharedResourceGroupResources = { + 'appInsightsName':'appin-${resourceSuffix}' + 'logAnalyticsWorkspaceName': 'logananalyticsws-${resourceSuffix}' + 'environmentName': environment + 'resourceSuffix' : resourceSuffix + 'vmSuffix' : vmSuffix +} + + + +resource aseResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: aseResourceGroupName + location: location +} + + + +// shared resource group + + +// for testing -- need a subnet + +var NetworkResourceGroupName = 'rg-network-${resourceSuffix}' + +resource networkRg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: NetworkResourceGroupName + location: location +} + +module vnet_generic './vnettest/vnetWithOutBastian.bicep' = { + name: 'vnet' + scope: resourceGroup(networkRg.name) + params: { + namePrefix: 'test-vnet' + } +} + +var subnetId=vnet_generic.outputs.subnetId + +// end testing subnet + + +resource sharedRG 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: sharedResourceGroupName + location: location +} + + +module shared './shared/shared.bicep' = { + name: 'sharedresources' + scope: resourceGroup(sharedRG.name) + params: { + location: location + sharedResourceGroupResources : sharedResourceGroupResources + subnetId: subnetId + vmazdevopsPassword:vmazdevopsPassword + vmazdevopsUsername: vmazdevopsUsername + personalAccessToken: personalAccessToken + azureDevOpsAccount: azureDevOpsAccount + resourceGroupName: sharedRG.name + } +} + +module ase 'ase.bicep' = { + dependsOn: [ + shared + ] + scope: resourceGroup(aseResourceGroup.name) + name: 'aseresources' + params: { + location: location + workloadName: workloadName + environment: environment + } +} diff --git a/deployment/bicep/parameters.json b/deployment/bicep/parameters.json new file mode 100644 index 00000000..7cd37471 --- /dev/null +++ b/deployment/bicep/parameters.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmazdevopsUsername": { + "value": "test_username" + }, + "vmazdevopsPassword": { + "value": "$1test_password" + }, + "azureDevOpsAccount": { + "value": "https://dev.azure.com/ORGNAME" + }, + "personalAccessToken": { + "value": "PUTINTOKEN" + }, + "workloadName" :{ + "value": "demoWL" + }, + "environment" :{ + "value": "dev" + } + + } +} \ No newline at end of file diff --git a/deployment/bicep/shared/createvmwindows.bicep b/deployment/bicep/shared/createvmwindows.bicep new file mode 100644 index 00000000..15a0e217 --- /dev/null +++ b/deployment/bicep/shared/createvmwindows.bicep @@ -0,0 +1,100 @@ + +param location string = resourceGroup().location +param subnetId string +param osDiskType string = 'Standard_LRS' +param vmSize string = 'Standard_D4_v3' +param username string +param password string +param windowsOSVersion string = '2016-Datacenter' + +param vmName string +param deployAgent bool=false + +@description('The Azure DevOps account name]') +param azureDevOpsAccount string='' + +@description('The personal access token to connect to Azure DevOps') +@secure() +param personalAccessToken string='' + +@description('The Azure DevOps build agent pool for this build agent to join. Use \'Default\' if you don\'t have a separate pool.') +param poolName string = 'Default' + +@description('Enable autologon to run the build agent in interactive mode that can sustain machine reboots.
Set this to true if the agents will be used to run UI tests.') +param enableAutologon bool = false + +@description('The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated.') +param artifactsLocation string = 'https://raw.githubusercontent.com/ahmedsza/azdevopsagent/main/setupagent.ps1' + + + +var azureDevOpsAgentName = 'agent-${vmName}' + +// Bring in the nic +module nic './vm-nic.bicep' = { + name: '${vmName}-nic' + params: { + subnetId: subnetId + vmName: vmName + } +} + +// Create the vm +resource vm 'Microsoft.Compute/virtualMachines@2019-07-01' = { + name: vmName + location: location + zones: [ + '1' + ] + properties: { + hardwareProfile: { + vmSize: vmSize + } + storageProfile: { + osDisk: { + createOption: 'FromImage' + managedDisk: { + storageAccountType: osDiskType + } + } + imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: windowsOSVersion + version: 'latest' + } + } + osProfile: { + computerName: vmName + adminUsername: username + adminPassword: password + } + networkProfile: { + networkInterfaces: [ + { + id: nic.outputs.nicId + } + ] + } + } +} + +resource vm_CustomScript 'Microsoft.Compute/virtualMachines/extensions@2018-06-01' = if (deployAgent) { + parent: vm + name: 'CustomScript' + location: location + properties: { + publisher: 'Microsoft.Compute' + type: 'CustomScriptExtension' + typeHandlerVersion: '1.10' + settings: { + fileUris: [ + artifactsLocation + ] + commandToExecute: 'powershell.exe -ExecutionPolicy Unrestricted -Command ./setupagent.ps1 -url ${azureDevOpsAccount} -pat ${personalAccessToken} -agent ${azureDevOpsAgentName} -pool ${poolName} -runAsAutoLogon ${enableAutologon} -vmAdminUserName ${username} -vmAdminPassword ${password}' + } + } +} + + +output id string = vm.id diff --git a/deployment/bicep/shared/shared.bicep b/deployment/bicep/shared/shared.bicep new file mode 100644 index 00000000..66d91800 --- /dev/null +++ b/deployment/bicep/shared/shared.bicep @@ -0,0 +1,57 @@ +targetScope='resourceGroup' +param location string +param sharedResourceGroupResources object + +param subnetId string +param vmazdevopsUsername string +param vmazdevopsPassword string +param azureDevOpsAccount string + + +param personalAccessToken string +param resourceGroupName string + + +//param environment string +//param namePrefix string = 'not set' + + +module appInsights './azmon.bicep' = { + name: 'azmon' + scope: resourceGroup(resourceGroupName) + params: { + location: location + sharedResourceGroupResources : sharedResourceGroupResources + + } +} +output appInsightsConnectionString string = appInsights.outputs.appInsightsConnectionString + + +module vm_devopswinvm './createvmwindows.bicep' = { + name: 'azdevopsvm' + scope: resourceGroup(resourceGroupName) + params: { + subnetId: subnetId + username: vmazdevopsUsername + password: vmazdevopsPassword + vmName: 'azdevops-${sharedResourceGroupResources.vmSuffix}' + azureDevOpsAccount: azureDevOpsAccount + personalAccessToken: personalAccessToken + deployAgent: false + } +} + +module vm_jumpboxwinvm './createvmwindows.bicep' = { + name: 'jumpboxwinvm' + scope: resourceGroup(resourceGroupName) + params: { + subnetId: subnetId + username: vmazdevopsUsername + password: vmazdevopsPassword + vmName: 'jumpbox-${sharedResourceGroupResources.vmSuffix}' + } +} + +output devopsAgentvmName string = vm_devopswinvm.name +output jumpBoxvmName string = vm_jumpboxwinvm.name diff --git a/deployment/bicep/shared/vm-nic.bicep b/deployment/bicep/shared/vm-nic.bicep new file mode 100644 index 00000000..cc75f3f2 --- /dev/null +++ b/deployment/bicep/shared/vm-nic.bicep @@ -0,0 +1,35 @@ + +param location string = resourceGroup().location +param subnetId string +param privateIPAddress string = '10.0.0.4' + +param vmName string + +resource nic 'Microsoft.Network/networkInterfaces@2020-08-01' = { + name: vmName + location: location + properties: { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + privateIPAddress: privateIPAddress + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: subnetId + } + primary: true + privateIPAddressVersion: 'IPv4' + } + } + ] + dnsSettings: { + dnsServers: [] + } + enableAcceleratedNetworking: false + enableIPForwarding: false + } +} + + +output nicId string = nic.id From d6047f27c17b22bbb10966cd37b0a2cecbc575e1 Mon Sep 17 00:00:00 2001 From: ahmeds Date: Fri, 16 Jul 2021 09:27:16 +0200 Subject: [PATCH 02/49] refactored keyvault --- deployment/bicep/shared/shared.bicep | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/deployment/bicep/shared/shared.bicep b/deployment/bicep/shared/shared.bicep index 66d91800..f7be6a47 100644 --- a/deployment/bicep/shared/shared.bicep +++ b/deployment/bicep/shared/shared.bicep @@ -53,5 +53,38 @@ module vm_jumpboxwinvm './createvmwindows.bicep' = { } } +resource key_vault 'Microsoft.KeyVault/vaults@2019-09-01' = { + name: sharedResourceGroupResources.keyVaultName + location: location + properties: { + tenantId: subscription().tenantId + sku: { + family: 'A' + name: 'standard' + } + accessPolicies: [ + // { + // tenantId: 'string' + // objectId: 'string' + // applicationId: 'string' + // permissions: { + // keys: [ + // 'string' + // ] + // secrets: [ + // 'string' + // ] + // certificates: [ + // 'string' + // ] + // storage: [ + // 'string' + // ] + // } + // } + ] + } +} + output devopsAgentvmName string = vm_devopswinvm.name output jumpBoxvmName string = vm_jumpboxwinvm.name From b91c2e7a47e3e648883761276e090e7891661235 Mon Sep 17 00:00:00 2001 From: ahmeds Date: Fri, 16 Jul 2021 09:38:51 +0200 Subject: [PATCH 03/49] subnet id for networking module and shared --- deployment/bicep/main.bicep | 19 ++- deployment/bicep/networking.bicep | 199 +++++++++++++++++++++++++++ deployment/bicep/shared/shared.bicep | 7 +- 3 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 deployment/bicep/networking.bicep diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep index 3a2a2e57..4c273d9e 100644 --- a/deployment/bicep/main.bicep +++ b/deployment/bicep/main.bicep @@ -71,13 +71,28 @@ resource sharedRG 'Microsoft.Resources/resourceGroups@2021-04-01' = { } -module shared './shared/shared.bicep' = { + +module networking 'networking.bicep' = { + name: 'networkingresources' + scope: resourceGroup(networkingRG.name) + params: { + workloadName: workloadName + environment: environment + } +} + +var jumpboxSubnetId= networking.outputs.jumpBoxSubnetId +var agentSubnetId=networking.outputs.devOpsSubnetId +module shared './shared/shared.bicep' = { dependsOn: [ + networking + ] name: 'sharedresources' scope: resourceGroup(sharedRG.name) params: { location: location sharedResourceGroupResources : sharedResourceGroupResources - subnetId: subnetId + jumpboxSubnetId: jumpboxSubnetId + agentSubnetId: agentSubnetId vmazdevopsPassword:vmazdevopsPassword vmazdevopsUsername: vmazdevopsUsername personalAccessToken: personalAccessToken diff --git a/deployment/bicep/networking.bicep b/deployment/bicep/networking.bicep new file mode 100644 index 00000000..dcebd150 --- /dev/null +++ b/deployment/bicep/networking.bicep @@ -0,0 +1,199 @@ +// Parameters +@description('A short name for the workload being deployed') +param workloadName string + +@description('The environment for which the deployment is being executed') +@allowed([ + 'dev' + 'uat' + 'prod' + 'dr' +]) +param environment string + +param hubVNetNameAddressPrefix string = '10.0.0.0/16' +param spokeVNetNameAddressPrefix string = '10.1.0.0/16' + +param bastionAddressPrefix string = '10.0.1.0/24' +param devOpsNameAddressPrefix string = '10.0.2.0/24' +param jumpBoxAddressPrefix string = '10.0.3.0/24' + +param aseAddressPrefix string = '10.1.1.0/24' + +// Variables +var owner = 'ASE Const Set' +var location = resourceGroup().location + +var hubVNetName = 'vnet-hub-${workloadName}-${environment}-${location}' +var spokeVNetName = 'vnet-spoke-${workloadName}-${environment}-${location}-001' + +var bastionSubnetName = 'snet-bast-${workloadName}-${environment}-${location}' +var devOpsSubnetName = 'snet-devops-${workloadName}-${environment}-${location}' +var jumpBoxSubnetName = 'snet-jbox-${workloadName}-${environment}-${location}-001' + +var aseSubnetName = 'snet-ase-${workloadName}-${environment}-${location}-001' + + +// Resources - VNet - SubNets +resource vnetHub 'Microsoft.Network/virtualNetworks@2021-02-01' = { + name: hubVNetName + location: location + tags: { + Owner: owner + // CostCenter: costCenter + } + properties: { + addressSpace: { + addressPrefixes: [ + hubVNetNameAddressPrefix + ] + } + enableVmProtection: false + enableDdosProtection: false + // subnets: [ + // { + // name: bastionSubnetName + // properties: { + // addressPrefix: bastionAddressPrefix + // } + // } + // { + // name: devOpsSubnetName + // properties: { + // addressPrefix: devOpsNameAddressPrefix + // } + // } + // { + // name: jumpBoxSubnetName + // properties: { + // addressPrefix: jumpBoxAddressPrefix + // } + // } + // ] + } +} + +resource bastionSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = { + name: bastionSubnetName + parent: vnetHub + properties: { + addressPrefix: bastionAddressPrefix + } + dependsOn:[ + vnetHub + ] +} +resource devOpsSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = { + name: devOpsSubnetName + parent: vnetHub + properties: { + addressPrefix: devOpsNameAddressPrefix + } + dependsOn:[ + vnetHub + bastionSubnet + ] +} +resource jumpBoxSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = { + name: jumpBoxSubnetName + parent: vnetHub + properties: { + addressPrefix: jumpBoxAddressPrefix + } + dependsOn:[ + vnetHub + bastionSubnet + devOpsSubnet + ] +} + +resource vnetSpoke 'Microsoft.Network/virtualNetworks@2021-02-01' = { + name: spokeVNetName + location: resourceGroup().location + tags: { + Owner: owner + // CostCenter: costCenter + } + properties: { + addressSpace: { + addressPrefixes: [ + spokeVNetNameAddressPrefix + ] + } + enableVmProtection: false + enableDdosProtection: false + // subnets: [ + // { + // name: aseSubnetName + // properties: { + // addressPrefix: aseAddressPrefix + // } + // } + // ] + } +} + +resource aseSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = { + name: aseSubnetName + parent: vnetSpoke + properties: { + addressPrefix: aseAddressPrefix + } + dependsOn:[ + vnetSpoke + ] +} + +// Peering +resource vnetHubPeer 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-02-01' = { + name: '${vnetHub.name}/${vnetHub.name}-${vnetSpoke.name}' + properties: { + allowVirtualNetworkAccess: true + allowForwardedTraffic: false + allowGatewayTransit: false + useRemoteGateways: false + remoteVirtualNetwork: { + id: vnetSpoke.id + } + } + dependsOn:[ + vnetHub + vnetSpoke + ] +} + +resource vnetSpokePeer 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-02-01' = { + name: '${vnetSpoke.name}/${vnetSpoke.name}-${vnetHub.name}' + properties: { + allowVirtualNetworkAccess: true + allowForwardedTraffic: false + allowGatewayTransit: false + useRemoteGateways: false + remoteVirtualNetwork: { + id: vnetHub.id + } + } + dependsOn:[ + vnetHub + vnetSpoke + ] +} + + +// Output section +output hubVNetName string = hubVNetName +output spokeVNetName string = spokeVNetName + +output hubVNetId string = vnetHub.id +output spokeVNetId string = vnetSpoke.id + +output devOpsSubnetId string=devOpsSubnet.id +output jumpBoxSubnetId string=jumpBoxSubnet.id + +output bastionSubnetName string = bastionSubnetName +output devOpsSubnetName string = devOpsSubnetName +output jumpBoxSubnetName string = jumpBoxSubnetName +output aseSubnetName string = aseSubnetName + + + diff --git a/deployment/bicep/shared/shared.bicep b/deployment/bicep/shared/shared.bicep index f7be6a47..ee386a90 100644 --- a/deployment/bicep/shared/shared.bicep +++ b/deployment/bicep/shared/shared.bicep @@ -2,7 +2,8 @@ targetScope='resourceGroup' param location string param sharedResourceGroupResources object -param subnetId string +param jumpboxSubnetId string +param agentSubnetId string param vmazdevopsUsername string param vmazdevopsPassword string param azureDevOpsAccount string @@ -32,7 +33,7 @@ module vm_devopswinvm './createvmwindows.bicep' = { name: 'azdevopsvm' scope: resourceGroup(resourceGroupName) params: { - subnetId: subnetId + subnetId: agentSubnetId username: vmazdevopsUsername password: vmazdevopsPassword vmName: 'azdevops-${sharedResourceGroupResources.vmSuffix}' @@ -46,7 +47,7 @@ module vm_jumpboxwinvm './createvmwindows.bicep' = { name: 'jumpboxwinvm' scope: resourceGroup(resourceGroupName) params: { - subnetId: subnetId + subnetId: jumpboxSubnetId username: vmazdevopsUsername password: vmazdevopsPassword vmName: 'jumpbox-${sharedResourceGroupResources.vmSuffix}' From 9b82340020841c2caf8aa45307c13137731abda5 Mon Sep 17 00:00:00 2001 From: ahmeds Date: Fri, 16 Jul 2021 11:12:12 +0200 Subject: [PATCH 04/49] fixed issues --- deployment/bicep/main.bicep | 24 +++++++++++++++--------- deployment/bicep/parameters.json | 4 ++-- deployment/bicep/testpscript.ps1 | 10 ++++++++++ 3 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 deployment/bicep/testpscript.ps1 diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep index 4c273d9e..4f9be1a0 100644 --- a/deployment/bicep/main.bicep +++ b/deployment/bicep/main.bicep @@ -40,17 +40,8 @@ resource aseResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { -// shared resource group -// for testing -- need a subnet - -var NetworkResourceGroupName = 'rg-network-${resourceSuffix}' - -resource networkRg 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: NetworkResourceGroupName - location: location -} module vnet_generic './vnettest/vnetWithOutBastian.bicep' = { name: 'vnet' @@ -113,3 +104,18 @@ module ase 'ase.bicep' = { environment: environment } } +// module ase 'ase.bicep' = { +// dependsOn: [ +// networking +// shared +// ] +// scope: resourceGroup(aseResourceGroup.name) +// name: 'aseresources' +// params: { +// location: location +// workloadName: workloadName +// environment: environment +// aseSubnetName: networking.outputs.aseSubnetName +// aseSubnetId: '${networking.outputs.spokeVNetId}/subnets/${networking.outputs.aseSubnetName}' +// } +//} diff --git a/deployment/bicep/parameters.json b/deployment/bicep/parameters.json index 7cd37471..0e7d3aea 100644 --- a/deployment/bicep/parameters.json +++ b/deployment/bicep/parameters.json @@ -9,10 +9,10 @@ "value": "$1test_password" }, "azureDevOpsAccount": { - "value": "https://dev.azure.com/ORGNAME" + "value": "https://dev.azure.com/ahmeds" }, "personalAccessToken": { - "value": "PUTINTOKEN" + "value": "lbjxhqqxyovi5g6psgvcmncrlnv72o55fcfsqhwg33xaaurapykq" }, "workloadName" :{ "value": "demoWL" diff --git a/deployment/bicep/testpscript.ps1 b/deployment/bicep/testpscript.ps1 new file mode 100644 index 00000000..53c5ca47 --- /dev/null +++ b/deployment/bicep/testpscript.ps1 @@ -0,0 +1,10 @@ +$RESOURCE_GROUP = "tesatase" +$LOCATION = "westeurope" +$BICEP_FILE="main.bicep" + +# delete a deployment +az deployment sub delete --name testasedeployment + +# deploy the bicep file directly + +az deployment sub create --name testasedeployment --template-file $BICEP_FILE --parameters parameters.json --location $LOCATION -o json From 3b6ef7cc72653890ec9bdf0b6a93a952e2be230c Mon Sep 17 00:00:00 2001 From: ahmeds Date: Fri, 16 Jul 2021 11:13:25 +0200 Subject: [PATCH 05/49] uncomment ase --- deployment/bicep/main.bicep | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep index 4f9be1a0..d58d74b1 100644 --- a/deployment/bicep/main.bicep +++ b/deployment/bicep/main.bicep @@ -94,6 +94,7 @@ module shared './shared/shared.bicep' = { dependsOn: [ module ase 'ase.bicep' = { dependsOn: [ + networking shared ] scope: resourceGroup(aseResourceGroup.name) @@ -102,20 +103,7 @@ module ase 'ase.bicep' = { location: location workloadName: workloadName environment: environment + aseSubnetName: networking.outputs.aseSubnetName + aseSubnetId: '${networking.outputs.spokeVNetId}/subnets/${networking.outputs.aseSubnetName}' } } -// module ase 'ase.bicep' = { -// dependsOn: [ -// networking -// shared -// ] -// scope: resourceGroup(aseResourceGroup.name) -// name: 'aseresources' -// params: { -// location: location -// workloadName: workloadName -// environment: environment -// aseSubnetName: networking.outputs.aseSubnetName -// aseSubnetId: '${networking.outputs.spokeVNetId}/subnets/${networking.outputs.aseSubnetName}' -// } -//} From 5122681804060b9b462f409ca10b0fbae2543cb0 Mon Sep 17 00:00:00 2001 From: ahmeds Date: Fri, 16 Jul 2021 16:04:08 +0200 Subject: [PATCH 06/49] comment out ase --- deployment/bicep/main.bicep | 30 +++++++++++++++--------------- deployment/bicep/parameters.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep index d58d74b1..7bf5edf6 100644 --- a/deployment/bicep/main.bicep +++ b/deployment/bicep/main.bicep @@ -92,18 +92,18 @@ module shared './shared/shared.bicep' = { dependsOn: [ } } -module ase 'ase.bicep' = { - dependsOn: [ - networking - shared - ] - scope: resourceGroup(aseResourceGroup.name) - name: 'aseresources' - params: { - location: location - workloadName: workloadName - environment: environment - aseSubnetName: networking.outputs.aseSubnetName - aseSubnetId: '${networking.outputs.spokeVNetId}/subnets/${networking.outputs.aseSubnetName}' - } -} +// module ase 'ase.bicep' = { +// dependsOn: [ +// networking +// shared +// ] +// scope: resourceGroup(aseResourceGroup.name) +// name: 'aseresources' +// params: { +// location: location +// workloadName: workloadName +// environment: environment +// aseSubnetName: networking.outputs.aseSubnetName +// aseSubnetId: '${networking.outputs.spokeVNetId}/subnets/${networking.outputs.aseSubnetName}' +// } +// } diff --git a/deployment/bicep/parameters.json b/deployment/bicep/parameters.json index 0e7d3aea..ea5c69d4 100644 --- a/deployment/bicep/parameters.json +++ b/deployment/bicep/parameters.json @@ -15,7 +15,7 @@ "value": "lbjxhqqxyovi5g6psgvcmncrlnv72o55fcfsqhwg33xaaurapykq" }, "workloadName" :{ - "value": "demoWL" + "value": "ahmsWL" }, "environment" :{ "value": "dev" From 297c39f71f3debbef8deaaaf8f7e1ec543284003 Mon Sep 17 00:00:00 2001 From: ahmeds Date: Mon, 19 Jul 2021 08:00:04 +0200 Subject: [PATCH 07/49] deployagent fix --- deployment/bicep/shared/shared.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/bicep/shared/shared.bicep b/deployment/bicep/shared/shared.bicep index ee386a90..293fc3b8 100644 --- a/deployment/bicep/shared/shared.bicep +++ b/deployment/bicep/shared/shared.bicep @@ -39,7 +39,7 @@ module vm_devopswinvm './createvmwindows.bicep' = { vmName: 'azdevops-${sharedResourceGroupResources.vmSuffix}' azureDevOpsAccount: azureDevOpsAccount personalAccessToken: personalAccessToken - deployAgent: false + deployAgent: true } } From e912f03262f66fab60b6c44ca7ef0773a61e9ba5 Mon Sep 17 00:00:00 2001 From: Nabeel Prior Date: Mon, 26 Jul 2021 09:46:08 +0200 Subject: [PATCH 08/49] cosmetic changes and ase commented out --- deployment/bicep/main.bicep | 25 +++++++++++++------ deployment/bicep/shared/createvmwindows.bicep | 1 - 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep index 7bf5edf6..4eccdf32 100644 --- a/deployment/bicep/main.bicep +++ b/deployment/bicep/main.bicep @@ -1,7 +1,7 @@ targetScope='subscription' param workloadName string -param location string = deployment().location -@description('The-- environment for which the deployment is being executed') +var location = deployment().location +@description('The environment for which the deployment is being executed') @allowed([ 'dev' 'uat' @@ -24,15 +24,24 @@ var sharedResourceGroupName = 'rg-shared-${resourceSuffix}' var aseResourceGroupName = 'rg-ase-${resourceSuffix}' // Create resources name using these objects and pass it as a params in module var sharedResourceGroupResources = { - 'appInsightsName':'appin-${resourceSuffix}' - 'logAnalyticsWorkspaceName': 'logananalyticsws-${resourceSuffix}' - 'environmentName': environment - 'resourceSuffix' : resourceSuffix - 'vmSuffix' : vmSuffix + 'appInsightsName':'appi-${resourceSuffix}' + 'logAnalyticsWorkspaceName': 'log-${resourceSuffix}' + 'environmentName': environment + 'resourceSuffix' : resourceSuffix + 'vmSuffix' : vmSuffix + 'keyVaultName':'kv-${workloadName}-${environment}' // Must be between 3-24 alphanumeric characters } +resource networkingRG 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: networkingResourceGroupName + location: location +} + + + + resource aseResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: aseResourceGroupName location: location @@ -104,6 +113,6 @@ module shared './shared/shared.bicep' = { dependsOn: [ // workloadName: workloadName // environment: environment // aseSubnetName: networking.outputs.aseSubnetName -// aseSubnetId: '${networking.outputs.spokeVNetId}/subnets/${networking.outputs.aseSubnetName}' +// aseSubnetId: networking.outputs.aseSubnetid // } // } diff --git a/deployment/bicep/shared/createvmwindows.bicep b/deployment/bicep/shared/createvmwindows.bicep index 15a0e217..f5dfb0e1 100644 --- a/deployment/bicep/shared/createvmwindows.bicep +++ b/deployment/bicep/shared/createvmwindows.bicep @@ -6,7 +6,6 @@ param vmSize string = 'Standard_D4_v3' param username string param password string param windowsOSVersion string = '2016-Datacenter' - param vmName string param deployAgent bool=false From 6f8d044d9495ccd37720d12ec47807718a5ebaa3 Mon Sep 17 00:00:00 2001 From: Kunal Babre Date: Wed, 15 Nov 2023 22:05:12 -0800 Subject: [PATCH 09/49] Add files via upload From 9955c9272465f34d4793bd4eb1d55bcdc233156b Mon Sep 17 00:00:00 2001 From: Kunal Babre Date: Fri, 15 Dec 2023 19:15:13 -0800 Subject: [PATCH 10/49] Add files via upload From 42c7e34754938a1557b7d08d4206d13b59266f9e Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:31:31 -0600 Subject: [PATCH 11/49] testing bicep cicd - added new oidc client id for read-only access, testing what-if flag --- .github/workflows/scenario2.bicep.yml | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 .github/workflows/scenario2.bicep.yml diff --git a/.github/workflows/scenario2.bicep.yml b/.github/workflows/scenario2.bicep.yml new file mode 100644 index 00000000..bf3a05db --- /dev/null +++ b/.github/workflows/scenario2.bicep.yml @@ -0,0 +1,129 @@ +name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' + +on: + workflow_dispatch: + + push: + branches: + - main + paths: + - '.github/workflows/bicep.scenario2.yml' + - 'scenarios/secure-baseline-multitenant/bicep/**' + - '!scenarios/secure-baseline-multitenant/**.md' + + pull_request: + branches: + - main + paths: + - '.github/workflows/bicep.scenario2.yml' + - 'scenarios/secure-baseline-multitenant/bicep/**' + - '!scenarios/secure-baseline-multitenant/**.md' + +permissions: + id-token: write + contents: read + +env: + modulePath: 'scenarios/secure-baseline-multitenant/bicep' + +jobs: + validate_bicep: + name: "Validate Bicep files" + runs-on: ubuntu-latest + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Validate that bicep builds + run: az bicep build -f main.bicep + working-directory: ${{ env.modulePath }} + + # Log into Azure via OIDC + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} + + # Deploy Bicep file + - name: deployment-what-if + uses: azure/arm-deploy@v1 + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + template: ${{ env.modulePath }}/main.bicep + parameters: ${{ env.modulePath }}/main.parameters.jsonc + failOnStdErr: false + additionalArguments: "--what-if" + + + build-and-deploy: + timeout-minutes: 360 + name: "Deploy Bicep templates" + needs: validate_bicep + runs-on: ubuntu-latest + environment: production + steps: + # Checkout code + - name: Checkout the code + uses: actions/checkout@main + + # - name: Variable substitution + # uses: microsoft/variable-substitution@v1 + # with: + # files: ${{ env.modulePath }}/config.yml + # env: + # ACCOUNT_NAME: ${{ secrets.AZURE_SUBSCRIPTION }} + + # - name: Install yq to parse yaml file + # run: | + # sudo wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.5.0/yq_linux_amd64 + # sudo chmod +x /usr/local/bin/yq + + # - name: Parse config.yaml as output to GitHub Actions matrix + # run: | + # echo "config=$(yq e ${{ env.modulePath }}/config.yml -j -I=0)" >> $GITHUB_ENV + + # - name: Write deployment information to log + # run: | + # echo "Deploying to ${{ fromJson(env.config).AZURE_LOCATION }} with name prefix ${{ fromJson(env.config).RESOURCE_NAME_PREFIX }}" + + # Log into Azure via OIDC + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} + + # Deploy Bicep file + - name: deploy + uses: azure/arm-deploy@v1 + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + template: ${{ env.modulePath }}/main.bicep + parameters: ${{ env.modulePath }}/main.parameters.jsonc + failOnStdErr: false + + + - name: Run Preflight Validation + working-directory: ${{ env.modulePath }} + run: | + az deployment sub validate \ + --location ${{ fromJson(env.config).AZURE_LOCATION }} \ + --parameters --template-file main.bicep + + # Deploy Bicep file, need to point parameters to the main.parameters.json location + - name: deploy + uses: azure/arm-deploy@v1 + with: + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + scope: subscription + region: ${{ fromJson(env.config).AZURE_LOCATION }} + deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" + template: ${{ env.modulePath }}/main.bicep + parameters: > + workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} + vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} + accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} + createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} \ No newline at end of file From 2dc7390ca6cfeb85a16a7422c2b2d3683d5793f7 Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:38:58 -0600 Subject: [PATCH 12/49] Adding region into the bicep yml, env var and to the what-if arm-deploy action --- .github/workflows/scenario1.bicep.yml | 152 ++++++++++++++++++-------- .github/workflows/scenario2.bicep.yml | 129 ---------------------- 2 files changed, 106 insertions(+), 175 deletions(-) delete mode 100644 .github/workflows/scenario2.bicep.yml diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 2b9c40d9..a1b746c8 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -1,13 +1,5 @@ name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' -# This workflow tests the ASEv3 Secure Baseline Multi-Tenant scenario deployment. -# This will use the default parameter file (main.parameters.jsonc) with an overridden -# SKU to deploy ASEv3 - -concurrency: - group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' - cancel-in-progress: false - on: workflow_dispatch: @@ -15,20 +7,16 @@ on: branches: - main paths: - - '.github/workflows/_template.bicep.yml' - - '.github/workflows/scenario1.bicep.yml' - - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - - 'scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc' + - '.github/workflows/bicep.scenario2.yml' + - 'scenarios/secure-baseline-multitenant/bicep/**' - '!scenarios/secure-baseline-multitenant/**.md' pull_request: branches: - main paths: - - '.github/workflows/_template.bicep.yml' - - '.github/workflows/scenario1.bicep.yml' - - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - - 'scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc' + - '.github/workflows/bicep.scenario2.yml' + - 'scenarios/secure-baseline-multitenant/bicep/**' - '!scenarios/secure-baseline-multitenant/**.md' permissions: @@ -36,38 +24,110 @@ permissions: contents: read env: - deployStackName: 'Scenario1-ASEv3-Secure-Baseline-MultiTenant' - deploymentPath: 'scenarios/secure-baseline-multitenant/bicep/main.bicep' - scenarioName: 'ase-multitenant' + modulePath: 'scenarios/secure-baseline-multitenant/bicep' region: 'westus2' - # webAppPlanSKU is the only parameter that is overridden for ASEv3 - webAppPlanSku: 'ASE_I3V2_AZ' jobs: - prepare-environment: - name: 'Prepare CICD Environment for Bicep Workflow' + validate_bicep: + name: "Validate Bicep files" + runs-on: ubuntu-latest + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Validate that bicep builds + run: az bicep build -f main.bicep + working-directory: ${{ env.modulePath }} + + # Log into Azure via OIDC + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} + + # Deploy Bicep file + - name: deployment-what-if + uses: azure/arm-deploy@v1 + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + region: ${{ env.region }} + template: ${{ env.modulePath }}/main.bicep + parameters: ${{ env.modulePath }}/main.parameters.jsonc + failOnStdErr: false + additionalArguments: "--what-if" + + + build-and-deploy: + if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' + + timeout-minutes: 360 + name: "Deploy Bicep templates" + needs: validate_bicep runs-on: ubuntu-latest + environment: production steps: - - name: Checkout the code - uses: actions/checkout@main - outputs: - deployStackName: ${{ env.deployStackName }} - region: ${{ env.region }} - modulePath: ${{ env.deploymentPath }} - bicepParamPath: ${{ env.deploymentPath }}/main.parameters.jsonc - bicepAdditionalParams: -p webAppPlanSku=${{ env.webAppPlanSku }} --deny-settings-mode 'none' - - call-workflow-passing-data: - name: 'Bicep CICD' - needs: - - prepare-environment - uses: ./.github/workflows/.template.bicep.yml - with: - deployStackName: ${{ needs.prepare-environment.outputs.deployStackName }} - region: ${{ needs.prepare-environment.outputs.region }} - modulePath: ${{ needs.prepare-environment.outputs.modulePath }} - bicepParamPath: ${{ needs.prepare-environment.outputs.bicepParamPath }} - bicepAdditionalParams: ${{ needs.prepare-environment.outputs.bicepAdditionalParams }} - # Ensure this value is a boolean - destroy: ${{ github.event.inputs.destroy == 'true' }} - secrets: inherit + # Checkout code + - name: Checkout the code + uses: actions/checkout@main + + # - name: Variable substitution + # uses: microsoft/variable-substitution@v1 + # with: + # files: ${{ env.modulePath }}/config.yml + # env: + # ACCOUNT_NAME: ${{ secrets.AZURE_SUBSCRIPTION }} + + # - name: Install yq to parse yaml file + # run: | + # sudo wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.5.0/yq_linux_amd64 + # sudo chmod +x /usr/local/bin/yq + + # - name: Parse config.yaml as output to GitHub Actions matrix + # run: | + # echo "config=$(yq e ${{ env.modulePath }}/config.yml -j -I=0)" >> $GITHUB_ENV + + # - name: Write deployment information to log + # run: | + # echo "Deploying to ${{ fromJson(env.config).AZURE_LOCATION }} with name prefix ${{ fromJson(env.config).RESOURCE_NAME_PREFIX }}" + + # Log into Azure via OIDC + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} + + # Deploy Bicep file + - name: deploy + uses: azure/arm-deploy@v1 + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + template: ${{ env.modulePath }}/main.bicep + parameters: ${{ env.modulePath }}/main.parameters.jsonc + failOnStdErr: false + + + - name: Run Preflight Validation + working-directory: ${{ env.modulePath }} + run: | + az deployment sub validate \ + --location ${{ fromJson(env.config).AZURE_LOCATION }} \ + --parameters --template-file main.bicep + + # Deploy Bicep file, need to point parameters to the main.parameters.json location + - name: deploy + uses: azure/arm-deploy@v1 + with: + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + scope: subscription + region: ${{ fromJson(env.config).AZURE_LOCATION }} + deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" + template: ${{ env.modulePath }}/main.bicep + parameters: > + workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} + vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} + accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} + createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} diff --git a/.github/workflows/scenario2.bicep.yml b/.github/workflows/scenario2.bicep.yml deleted file mode 100644 index bf3a05db..00000000 --- a/.github/workflows/scenario2.bicep.yml +++ /dev/null @@ -1,129 +0,0 @@ -name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' - -on: - workflow_dispatch: - - push: - branches: - - main - paths: - - '.github/workflows/bicep.scenario2.yml' - - 'scenarios/secure-baseline-multitenant/bicep/**' - - '!scenarios/secure-baseline-multitenant/**.md' - - pull_request: - branches: - - main - paths: - - '.github/workflows/bicep.scenario2.yml' - - 'scenarios/secure-baseline-multitenant/bicep/**' - - '!scenarios/secure-baseline-multitenant/**.md' - -permissions: - id-token: write - contents: read - -env: - modulePath: 'scenarios/secure-baseline-multitenant/bicep' - -jobs: - validate_bicep: - name: "Validate Bicep files" - runs-on: ubuntu-latest - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - - name: Validate that bicep builds - run: az bicep build -f main.bicep - working-directory: ${{ env.modulePath }} - - # Log into Azure via OIDC - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - - # Deploy Bicep file - - name: deployment-what-if - uses: azure/arm-deploy@v1 - with: - scope: subscription - subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} - template: ${{ env.modulePath }}/main.bicep - parameters: ${{ env.modulePath }}/main.parameters.jsonc - failOnStdErr: false - additionalArguments: "--what-if" - - - build-and-deploy: - timeout-minutes: 360 - name: "Deploy Bicep templates" - needs: validate_bicep - runs-on: ubuntu-latest - environment: production - steps: - # Checkout code - - name: Checkout the code - uses: actions/checkout@main - - # - name: Variable substitution - # uses: microsoft/variable-substitution@v1 - # with: - # files: ${{ env.modulePath }}/config.yml - # env: - # ACCOUNT_NAME: ${{ secrets.AZURE_SUBSCRIPTION }} - - # - name: Install yq to parse yaml file - # run: | - # sudo wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.5.0/yq_linux_amd64 - # sudo chmod +x /usr/local/bin/yq - - # - name: Parse config.yaml as output to GitHub Actions matrix - # run: | - # echo "config=$(yq e ${{ env.modulePath }}/config.yml -j -I=0)" >> $GITHUB_ENV - - # - name: Write deployment information to log - # run: | - # echo "Deploying to ${{ fromJson(env.config).AZURE_LOCATION }} with name prefix ${{ fromJson(env.config).RESOURCE_NAME_PREFIX }}" - - # Log into Azure via OIDC - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - - # Deploy Bicep file - - name: deploy - uses: azure/arm-deploy@v1 - with: - scope: subscription - subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} - template: ${{ env.modulePath }}/main.bicep - parameters: ${{ env.modulePath }}/main.parameters.jsonc - failOnStdErr: false - - - - name: Run Preflight Validation - working-directory: ${{ env.modulePath }} - run: | - az deployment sub validate \ - --location ${{ fromJson(env.config).AZURE_LOCATION }} \ - --parameters --template-file main.bicep - - # Deploy Bicep file, need to point parameters to the main.parameters.json location - - name: deploy - uses: azure/arm-deploy@v1 - with: - subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} - scope: subscription - region: ${{ fromJson(env.config).AZURE_LOCATION }} - deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" - template: ${{ env.modulePath }}/main.bicep - parameters: > - workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} - vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} - accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} - createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} \ No newline at end of file From 300ffa431f173aa9704913413fd2de7168466a49 Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:40:18 -0600 Subject: [PATCH 13/49] testing pipeline, adding puysh trigger for this branch --- .github/workflows/scenario1.bicep.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index a1b746c8..4b0ed251 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -6,6 +6,7 @@ on: push: branches: - main + - refactor/iac-cicd paths: - '.github/workflows/bicep.scenario2.yml' - 'scenarios/secure-baseline-multitenant/bicep/**' From d3c66b243f2f74d5694b1c17fea39119ecce0252 Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:42:11 -0600 Subject: [PATCH 14/49] testing pipeline, adding puysh trigger for this branch --- .github/workflows/scenario1.bicep.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 4b0ed251..2a534ad4 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -8,7 +8,7 @@ on: - main - refactor/iac-cicd paths: - - '.github/workflows/bicep.scenario2.yml' + - '.github/workflows/scenario1.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**' - '!scenarios/secure-baseline-multitenant/**.md' @@ -16,7 +16,7 @@ on: branches: - main paths: - - '.github/workflows/bicep.scenario2.yml' + - '.github/workflows/scenario1.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**' - '!scenarios/secure-baseline-multitenant/**.md' From edea540e4064376f6a4ff97746828229d14fd4f6 Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:08:06 -0600 Subject: [PATCH 15/49] testing with prod id as the what-if scenario requires same level of permissions --- .github/workflows/scenario1.bicep.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 2a534ad4..c5dc6419 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -32,6 +32,7 @@ jobs: validate_bicep: name: "Validate Bicep files" runs-on: ubuntu-latest + environment: production steps: - name: Checkout the code uses: actions/checkout@v4 From c5c059b24fe09f112db5bc15988c45e83bca61ee Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:43:06 -0600 Subject: [PATCH 16/49] testing with prod id as the what-if scenario requires same level of permissions --- .github/workflows/scenario1.bicep.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index c5dc6419..979a103d 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -48,8 +48,8 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - # Deploy Bicep file - - name: deployment-what-if + # Validate Bicep File + - name: deployment-validation uses: azure/arm-deploy@v1 with: scope: subscription @@ -59,6 +59,7 @@ jobs: parameters: ${{ env.modulePath }}/main.parameters.jsonc failOnStdErr: false additionalArguments: "--what-if" + deploymentMode: Validate build-and-deploy: From 84f2477f758fb5be43a4c234403aeeab7722d28b Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:27:21 -0600 Subject: [PATCH 17/49] testing the deployment --- .github/workflows/scenario1.bicep.yml | 109 +++++++++++++++----------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 979a103d..6140d2ed 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -1,5 +1,27 @@ name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' +######################################################### +# Concurrency allows to run 1 cycle at a time +# If worflow is running, 2nd one will automatically go in pending state +# if concurrency is enabled +# If 1st running, 2nd in pending and 3rd is triggered then 2nd which was +# in pending will be cancelled and only 3rd (latest) will run +# +# If this is enabled it will cancel current running and start latest +# cancel-in-progress: true +# +# When a concurrent job or workflow is queued, +# if another job or workflow using the same concurrency group in the repository +# is in progress, the queued job or workflow will be pending. +# +# Any previously pending job or workflow in the concurrency group will be canceled. +# To also cancel any currently running job or workflow in the same concurrency group, +# specify cancel-in-progress: true. +############################################################ +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: false + on: workflow_dispatch: @@ -29,10 +51,17 @@ env: region: 'westus2' jobs: - validate_bicep: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run Bicep linter + run: az bicep build --file deploy/main.bicep + working-directory: ${{ env.modulePath }} + + validate: name: "Validate Bicep files" runs-on: ubuntu-latest - environment: production steps: - name: Checkout the code uses: actions/checkout@v4 @@ -63,38 +92,18 @@ jobs: build-and-deploy: - if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' + # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' timeout-minutes: 360 name: "Deploy Bicep templates" - needs: validate_bicep + needs: [lint, validate] runs-on: ubuntu-latest - environment: production + environment: Production steps: # Checkout code - name: Checkout the code uses: actions/checkout@main - # - name: Variable substitution - # uses: microsoft/variable-substitution@v1 - # with: - # files: ${{ env.modulePath }}/config.yml - # env: - # ACCOUNT_NAME: ${{ secrets.AZURE_SUBSCRIPTION }} - - # - name: Install yq to parse yaml file - # run: | - # sudo wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.5.0/yq_linux_amd64 - # sudo chmod +x /usr/local/bin/yq - - # - name: Parse config.yaml as output to GitHub Actions matrix - # run: | - # echo "config=$(yq e ${{ env.modulePath }}/config.yml -j -I=0)" >> $GITHUB_ENV - - # - name: Write deployment information to log - # run: | - # echo "Deploying to ${{ fromJson(env.config).AZURE_LOCATION }} with name prefix ${{ fromJson(env.config).RESOURCE_NAME_PREFIX }}" - # Log into Azure via OIDC - uses: azure/login@v1 with: @@ -102,35 +111,47 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - # Deploy Bicep file - - name: deploy + # Validate Bicep File + - name: what-if uses: azure/arm-deploy@v1 with: scope: subscription subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + region: ${{ env.region }} template: ${{ env.modulePath }}/main.bicep parameters: ${{ env.modulePath }}/main.parameters.jsonc failOnStdErr: false + additionalArguments: "--what-if" - - - name: Run Preflight Validation - working-directory: ${{ env.modulePath }} - run: | - az deployment sub validate \ - --location ${{ fromJson(env.config).AZURE_LOCATION }} \ - --parameters --template-file main.bicep - - # Deploy Bicep file, need to point parameters to the main.parameters.json location + # Deploy Bicep file - name: deploy uses: azure/arm-deploy@v1 with: - subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} scope: subscription - region: ${{ fromJson(env.config).AZURE_LOCATION }} - deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + region: ${{ env.region }} template: ${{ env.modulePath }}/main.bicep - parameters: > - workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} - vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} - accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} - createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} + parameters: ${{ env.modulePath }}/main.parameters.jsonc + failOnStdErr: false + + # - name: Run Preflight Validation + # working-directory: ${{ env.modulePath }} + # run: | + # az deployment sub validate \ + # --location ${{ fromJson(env.config).AZURE_LOCATION }} \ + # --parameters --template-file main.bicep + + # Deploy Bicep file, need to point parameters to the main.parameters.json location + # - name: deploy + # uses: azure/arm-deploy@v1 + # with: + # subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} + # scope: subscription + # region: ${{ fromJson(env.config).AZURE_LOCATION }} + # deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" + # template: ${{ env.modulePath }}/main.bicep + # parameters: > + # workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} + # vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} + # accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} + # createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} \ No newline at end of file From fe42bf9a6d4e1f0df9da595ab78911fe255070f6 Mon Sep 17 00:00:00 2001 From: JinLee794 <94473824+JinLee794@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:30:36 -0600 Subject: [PATCH 18/49] testing the deployment --- .github/workflows/scenario1.bicep.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 6140d2ed..ad972014 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run Bicep linter - run: az bicep build --file deploy/main.bicep + run: az bicep build --file main.bicep working-directory: ${{ env.modulePath }} validate: @@ -66,10 +66,6 @@ jobs: - name: Checkout the code uses: actions/checkout@v4 - - name: Validate that bicep builds - run: az bicep build -f main.bicep - working-directory: ${{ env.modulePath }} - # Log into Azure via OIDC - uses: azure/login@v1 with: From c24b57ac50f6f699db00b10de1fed8a7f5a2b3fa Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Wed, 27 Dec 2023 20:38:38 -0600 Subject: [PATCH 19/49] testing cicd bicep templates --- .github/workflows/.template.bicep.yml | 40 ++-- .github/workflows/ase-multitenant.bicep.yml | 177 ++++++++++++++++++ .github/workflows/scenario1.bicep.yml | 153 --------------- .../bicep/parameters/appsvc.parameters.json | 88 +++++++++ .../ase-multitenant.parameters.jsonc | 121 ++++++++++++ .../ase-zone-redundant.parameters.jsonc | 129 +++++++++++++ 6 files changed, 529 insertions(+), 179 deletions(-) create mode 100644 .github/workflows/ase-multitenant.bicep.yml delete mode 100644 .github/workflows/scenario1.bicep.yml create mode 100644 scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json create mode 100644 scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc create mode 100644 scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc diff --git a/.github/workflows/.template.bicep.yml b/.github/workflows/.template.bicep.yml index 076bf5a1..59b9bbe1 100644 --- a/.github/workflows/.template.bicep.yml +++ b/.github/workflows/.template.bicep.yml @@ -13,20 +13,10 @@ on: description: 'Path to the Bicep module' required: true default: 'scenarios/secure-baseline-multitenant/bicep' - deployStackName: - type: string - description: 'Name of the subscription scoped stack to deploy' - required: false - default: 'secure-baseline-multitenant' bicepParamPath: type: string description: 'Path to the Bicep variables' required: true - bicepAdditionalParams: - type: string - description: 'Optional parameters to pass to Bicep in string format' - required: false - default: --deny-settings-mode 'none' destroy: type: boolean description: 'Destroy resources?' @@ -46,21 +36,20 @@ jobs: uses: actions/checkout@v4 # Log into Azure via OIDC - - uses: azure/login@v2 + - uses: azure/login@v1 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - name: Run Bicep linter - run: az bicep build --file ${{ inputs.modulePath }} - # working-directory: ${{ inputs.modulePath }} + run: az bicep build --file main.bicep + working-directory: ${{ inputs.modulePath }} - # TODO: Buildout PSRule policies - # - name: Run PSRule analysis - # uses: microsoft/ps-rule@v2.9.0 - # with: - # modules: PSRule.Rules.Azure + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.9.0 + with: + modules: PSRule.Rules.Azure deploy: if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' @@ -74,7 +63,7 @@ jobs: uses: actions/checkout@main # Log into Azure via OIDC - - uses: azure/login@v2 + - uses: azure/login@v1 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -82,21 +71,20 @@ jobs: - name: Deploy Bicep Scenario run: | + stackName=$(basename ${{ inputs.bicepParamPath }} ) + # If Destroy flag is set, delete the stack if ${{ inputs.destroy }}; then - echo "Destroying stack ${{ inputs.deployStackName }}" - - # Possible flags for delete: --delete-all, --delete-resource-groups, --delete-resources - az stack delete --name ${{ inputs.deployStackName }} --delete-all --yes - + echo "Destroying stack $stackName" + az stack delete --name $stackName --yes exit 0 # Exit successfully fi - az stack sub create --name ${{ inputs.deployStackName }} \ + az stack sub create --name $stackName \ --location ${{ inputs.region }} \ --template-file ${{ inputs.modulePath }} \ --parameters ${{ inputs.bicepParamPath }} \ - ${{ inputs.bicepAdditionalParams }} + --deny-settings-mode 'none' # Potential Deny Settings # ----------------------------- diff --git a/.github/workflows/ase-multitenant.bicep.yml b/.github/workflows/ase-multitenant.bicep.yml new file mode 100644 index 00000000..ce7720d1 --- /dev/null +++ b/.github/workflows/ase-multitenant.bicep.yml @@ -0,0 +1,177 @@ +name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' + +######################################################### +# Concurrency allows to run 1 cycle at a time +# If worflow is running, 2nd one will automatically go in pending state +# if concurrency is enabled +# If 1st running, 2nd in pending and 3rd is triggered then 2nd which was +# in pending will be cancelled and only 3rd (latest) will run +# +# If this is enabled it will cancel current running and start latest +# cancel-in-progress: true +# +# When a concurrent job or workflow is queued, +# if another job or workflow using the same concurrency group in the repository +# is in progress, the queued job or workflow will be pending. +# +# Any previously pending job or workflow in the concurrency group will be canceled. +# To also cancel any currently running job or workflow in the same concurrency group, +# specify cancel-in-progress: true. +############################################################ +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: false + +on: + workflow_dispatch: + + push: + branches: + - main + - refactor/iac-cicd + paths: + - '.github/workflows/scenario1.bicep.yml' + - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' + - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' + - '!scenarios/secure-baseline-multitenant/**.md' + + pull_request: + branches: + - main + paths: + - '.github/workflows/scenario1.bicep.yml' + - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' + - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' + - '!scenarios/secure-baseline-multitenant/**.md' + +permissions: + id-token: write + contents: read + +env: + deploymentPath: 'scenarios/secure-baseline-multitenant/bicep/main.bicep' + scenarioName: 'ase-multitenant' + region: 'westus2' + +jobs: + prepare-environment: + runs-on: ubuntu-latest + steps: + - name: Checkout the code + uses: actions/checkout@main + outputs: + region: ${{ env.region }} + modulePath: ${{ env.deploymentPath }} + bicepParamPath: ${{ env.deploymentPath }}/parameters/${{ env.scenarioName }}.parameters.jsonc + + call-workflow-passing-data: + name: 'Bicep CICD' + needs: + - prepare-environment + uses: ./.github/workflows/.template.bicep.yml + with: + region: ${{ needs.prepare-environment.outputs.region }} + modulePath: ${{ needs.prepare-environment.outputs.modulePath }} + bicepParamPath: ${{ needs.prepare-environment.outputs.bicepParamPath }} + # Ensure this value is a boolean + destroy: ${{ github.event.inputs.destroy == 'true' }} + secrets: inherit + +# jobs: +# validate: +# name: "Validate Bicep files" +# runs-on: ubuntu-latest +# steps: +# - name: Checkout the code +# uses: actions/checkout@v4 + +# # Log into Azure via OIDC +# - uses: azure/login@v1 +# with: +# client-id: ${{ secrets.AZURE_CLIENT_ID }} +# tenant-id: ${{ secrets.AZURE_TENANT_ID }} +# subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} + +# - name: Run Bicep linter +# run: az bicep build --file main.bicep +# working-directory: ${{ env.modulePath }} + +# # Validate Bicep File +# # Note: Requires same level of permissions to write to resources (no just read only) +# # - name: deployment-validation +# # uses: azure/arm-deploy@v1 +# # with: +# # scope: subscription +# # subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} +# # region: ${{ env.region }} +# # template: ${{ env.modulePath }}/main.bicep +# # parameters: ${{ env.modulePath }}/main.parameters.jsonc +# # failOnStdErr: false +# # additionalArguments: "--what-if" +# # deploymentMode: Validate + + +# build-and-deploy: +# # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' + +# timeout-minutes: 360 +# name: "Deploy Bicep templates" +# needs: [lint, validate] +# runs-on: ubuntu-latest +# environment: Production +# steps: +# # Checkout code +# - name: Checkout the code +# uses: actions/checkout@main + +# # Log into Azure via OIDC +# - uses: azure/login@v1 +# with: +# client-id: ${{ secrets.AZURE_CLIENT_ID }} +# tenant-id: ${{ secrets.AZURE_TENANT_ID }} +# subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} + +# # Validate Bicep File +# - name: what-if +# uses: azure/arm-deploy@v1 +# with: +# scope: subscription +# subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} +# region: ${{ env.region }} +# template: ${{ env.modulePath }}/main.bicep +# parameters: ${{ env.modulePath }}/main.parameters.jsonc +# failOnStdErr: false +# additionalArguments: "--what-if" + +# # Deploy Bicep file +# - name: deploy +# uses: azure/arm-deploy@v1 +# with: +# scope: subscription +# subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} +# region: ${{ env.region }} +# template: ${{ env.modulePath }}/main.bicep +# parameters: ${{ env.modulePath }}/main.parameters.jsonc +# failOnStdErr: false + +# # - name: Run Preflight Validation +# # working-directory: ${{ env.modulePath }} +# # run: | +# # az deployment sub validate \ +# # --location ${{ fromJson(env.config).AZURE_LOCATION }} \ +# # --parameters --template-file main.bicep + +# # Deploy Bicep file, need to point parameters to the main.parameters.json location +# # - name: deploy +# # uses: azure/arm-deploy@v1 +# # with: +# # subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} +# # scope: subscription +# # region: ${{ fromJson(env.config).AZURE_LOCATION }} +# # deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" +# # template: ${{ env.modulePath }}/main.bicep +# # parameters: > +# # workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} +# # vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} +# # accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} +# # createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} \ No newline at end of file diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml deleted file mode 100644 index ad972014..00000000 --- a/.github/workflows/scenario1.bicep.yml +++ /dev/null @@ -1,153 +0,0 @@ -name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' - -######################################################### -# Concurrency allows to run 1 cycle at a time -# If worflow is running, 2nd one will automatically go in pending state -# if concurrency is enabled -# If 1st running, 2nd in pending and 3rd is triggered then 2nd which was -# in pending will be cancelled and only 3rd (latest) will run -# -# If this is enabled it will cancel current running and start latest -# cancel-in-progress: true -# -# When a concurrent job or workflow is queued, -# if another job or workflow using the same concurrency group in the repository -# is in progress, the queued job or workflow will be pending. -# -# Any previously pending job or workflow in the concurrency group will be canceled. -# To also cancel any currently running job or workflow in the same concurrency group, -# specify cancel-in-progress: true. -############################################################ -concurrency: - group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' - cancel-in-progress: false - -on: - workflow_dispatch: - - push: - branches: - - main - - refactor/iac-cicd - paths: - - '.github/workflows/scenario1.bicep.yml' - - 'scenarios/secure-baseline-multitenant/bicep/**' - - '!scenarios/secure-baseline-multitenant/**.md' - - pull_request: - branches: - - main - paths: - - '.github/workflows/scenario1.bicep.yml' - - 'scenarios/secure-baseline-multitenant/bicep/**' - - '!scenarios/secure-baseline-multitenant/**.md' - -permissions: - id-token: write - contents: read - -env: - modulePath: 'scenarios/secure-baseline-multitenant/bicep' - region: 'westus2' - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run Bicep linter - run: az bicep build --file main.bicep - working-directory: ${{ env.modulePath }} - - validate: - name: "Validate Bicep files" - runs-on: ubuntu-latest - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - # Log into Azure via OIDC - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - - # Validate Bicep File - - name: deployment-validation - uses: azure/arm-deploy@v1 - with: - scope: subscription - subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} - region: ${{ env.region }} - template: ${{ env.modulePath }}/main.bicep - parameters: ${{ env.modulePath }}/main.parameters.jsonc - failOnStdErr: false - additionalArguments: "--what-if" - deploymentMode: Validate - - - build-and-deploy: - # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - - timeout-minutes: 360 - name: "Deploy Bicep templates" - needs: [lint, validate] - runs-on: ubuntu-latest - environment: Production - steps: - # Checkout code - - name: Checkout the code - uses: actions/checkout@main - - # Log into Azure via OIDC - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - - # Validate Bicep File - - name: what-if - uses: azure/arm-deploy@v1 - with: - scope: subscription - subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} - region: ${{ env.region }} - template: ${{ env.modulePath }}/main.bicep - parameters: ${{ env.modulePath }}/main.parameters.jsonc - failOnStdErr: false - additionalArguments: "--what-if" - - # Deploy Bicep file - - name: deploy - uses: azure/arm-deploy@v1 - with: - scope: subscription - subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} - region: ${{ env.region }} - template: ${{ env.modulePath }}/main.bicep - parameters: ${{ env.modulePath }}/main.parameters.jsonc - failOnStdErr: false - - # - name: Run Preflight Validation - # working-directory: ${{ env.modulePath }} - # run: | - # az deployment sub validate \ - # --location ${{ fromJson(env.config).AZURE_LOCATION }} \ - # --parameters --template-file main.bicep - - # Deploy Bicep file, need to point parameters to the main.parameters.json location - # - name: deploy - # uses: azure/arm-deploy@v1 - # with: - # subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} - # scope: subscription - # region: ${{ fromJson(env.config).AZURE_LOCATION }} - # deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" - # template: ${{ env.modulePath }}/main.bicep - # parameters: > - # workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} - # vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} - # accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} - # createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json b/scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json new file mode 100644 index 00000000..780731b1 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json @@ -0,0 +1,88 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "workloadName" : { + "value": "appsvclza1" + }, + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "deployAseV3": { + "value": false + }, + "vnetHubResourceId": { + "value": "" + }, + "firewallInternalIp": { + "value": "" + }, + "vnetHubAddressSpace": { + "value": "10.242.0.0/20" + }, + "enableEgressLockdown" : { + "value": true + }, + "deployRedis": { + "value": false + }, + "deployAzureSql": { + "value": false + }, + "deployAppConfig": { + "value": false + }, + "deployJumpHost": { + "value": false + }, + "deployOpenAi": { + "value": false + }, + "autoApproveAfdPrivateEndpoint": { + "value": true + }, + "subnetHubFirewallAddressSpace": { + "value": "10.242.0.0/26" + }, + "subnetHubBastionAddressSpace": { + "value": "10.242.0.64/26" + }, + "vnetSpokeAddressSpace": { + "value": "10.240.0.0/20" + }, + "subnetSpokeAppSvcAddressSpace": { + "value": "10.240.0.0/26" + }, + "subnetSpokeDevOpsAddressSpace": { + "value": "10.240.10.128/26" + }, + "subnetSpokePrivateEndpointAddressSpace": { + "value": "10.240.11.0/24" + }, + "webAppPlanSku": { + "value": "S1" + }, + "webAppBaseOs" : + { + "value": "Windows" + }, + "adminUsername": { + "value": "azureuser" + }, + "adminPassword": { + "value": "zxNMasKL12()" + }, + "resourceTags": { + "value": { + "deployment": "bicep" + } + }, + "sqlServerAdministrators": { + "value": { + "login": "Azure AD SQL Admins", + "sid": "xxx-xxxx-xxxx-xxxx", + "tenantId": "xxx-xxxx-xxxx-xxxx" + } + } + } +} diff --git a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc new file mode 100644 index 00000000..f87d79a8 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc @@ -0,0 +1,121 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // max length: 10. Suffix that will be used to name the resources in a pattern like - + "workloadName": { + "value": "ase-zones" + // "value": "merge-mul" + }, + //Required. The name of the environmentName (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long. + "environmentName": { + "value": "dev" + }, + //Optional, default is false. Set to true if you want to deploy ASE v3 instead of Multitenant App Service Plan. + "deployAseV3": { + "value": true + }, + //If empty, then a new hub will be deployed. If given, no new hub will be created and we create the peering between spoke and and existing hub vnet + "vnetHubResourceId": { + "value": "" + }, + //If vnetHubResourceId empty, this value is irrelevant. If vnetHubResourceId has value and we need UDR, then the internal IP of the azFW needs to be set. Otherwise no UDR will be created + "firewallInternalIp": { + "value": "10.242.0.4" + }, + "vnetHubAddressSpace": { + "value": "10.242.0.0/20" + }, + // Feature Flags + // set to true if you want to intercept all outbound traffic with azure firewall + "enableEgressLockdown": { + "value": true + }, + // set to true if you want to a redis cache + "deployRedis": { + "value": false + }, + // set to true if you want to deploy a azure SQL server and default database + "deployAzureSql": { + "value": false + }, + // set to true if you want to deploy application configuration + "deployAppConfig": { + "value": false + }, + // set to true if you want to deploy a jumpbox/devops VM + "deployJumpHost": { + "value": false + }, + // Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing , OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites + "deployOpenAi": { + "value": false + }, + // set to true if you want to auto approve the Private Endpoint of the AFD Premium + "autoApproveAfdPrivateEndpoint": { + "value": false + }, + // CIDR of the subnet that will host the azure Firewall + "subnetHubFirewallAddressSpace": { + "value": "10.242.0.0/26" + }, + // CIDR of the subnet that will host the Bastion Service + "subnetHubBastionAddressSpace": { + "value": "10.242.0.64/26" + }, + //CIDR of the spoke vnet that will hold the app services plan and the rest supporting services (and their private endpoints) + "vnetSpokeAddressSpace": { + "value": "10.240.0.0/20" + }, + //CIDR of the subnet that will hold the app services plan. ATTENTION: ASEv3 needs a /24 network + "subnetSpokeAppSvcAddressSpace": { + "value": "10.240.0.0/26" + }, + // //CIDRof the subnet that will hold the private link for the AFD Premium + // "subnetSpokeAfdIngressAddressSpace": { + // "value": "10.240.0.64/26" + // }, + //CIDR of the subnet that will hold devOps agents etc + "subnetSpokeDevOpsAddressSpace": { + "value": "10.240.10.128/26" + }, + //CIDR of the subnet that will hold the private endpoints of the supporting services + "subnetSpokePrivateEndpointAddressSpace": { + "value": "10.240.11.0/24" + }, + // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deploying at least three instances in three Availability Zones. EP* is only for functions' + // select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ', 'EP1', 'EP2', 'EP3', 'ASE_I1V2_AZ', 'ASE_I2V2_AZ', 'ASE_I3V2_AZ', 'ASE_I1V2', 'ASE_I2V2', 'ASE_I3V2' + "webAppPlanSku": { + "value": "ASE_I3V2" + }, + // two options: Windows or Linux + "webAppBaseOs": { + "value": "Windows" + }, + // admin username of the VM agent deployed in the Spoke + "adminUsername": { + "value": "azureuser" + }, + // admin password of the VM agent deployed in the Spoke + "adminPassword": { + "value": "strongpassword" + }, + //Resource tags that we might need to add to all resources (i.e. Environment, Cost center, application name etc) + "resourceTags": { + "value": { + "deployment": "bicep" + } + }, + // The Azure Active Directory (AAD) administrator group used for SQL Server authentication + "sqlServerAdministrators": { + "value": { + // Azure AD group where your Azure administrators are members + "login": "Azure AD SQL Admins", + // Azure AD object ID of the group + "sid": "xxx-xxxx-xxxx-xxxx", + // Azure AD tenant ID where the group is located + "tenantId": "xxxx-xxxxxx-xxxxx-xxxxx-xxx" + } + } + } +} \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc new file mode 100644 index 00000000..2ff9bc3e --- /dev/null +++ b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc @@ -0,0 +1,129 @@ +// Scenario: ASE Zone Redundant Multitenant deployment +// ----------------------------------------------------------------- +// Zone redundancy for the app service plan/environment is controlled by the `webappPlanSku` +// parameter by appending an `AZ` to the suffix like so: +// P3V3 (non zonal redundant) -> P3V3_AZ (zonal redundant) +// ASE_I3V2 (non zonal redundant) -> ASE_I3V2_AZ (zonal redundant) +// Module that controls this behavior can be found here [../../modules/app-service.module.bicep] +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deploying at least three instances in three Availability Zones. EP* is only for functions' + // select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ', 'EP1', 'EP2', 'EP3', 'ASE_I1V2_AZ', 'ASE_I2V2_AZ', 'ASE_I3V2_AZ', 'ASE_I1V2', 'ASE_I2V2', 'ASE_I3V2' + "webAppPlanSku": { + "value": "ASE_I3V2_AZ" + }, + // max length: 10. Suffix that will be used to name the resources in a pattern like - + "workloadName": { + "value": "ase-zones" + // "value": "merge-mul" + }, + //Required. The name of the environmentName (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long. + "environmentName": { + "value": "dev" + }, + //Optional, default is false. Set to true if you want to deploy ASE v3 instead of Multitenant App Service Plan. + "deployAseV3": { + "value": true + }, + //If empty, then a new hub will be deployed. If given, no new hub will be created and we create the peering between spoke and and existing hub vnet + "vnetHubResourceId": { + "value": "" + }, + //If vnetHubResourceId empty, this value is irrelevant. If vnetHubResourceId has value and we need UDR, then the internal IP of the azFW needs to be set. Otherwise no UDR will be created + "firewallInternalIp": { + "value": "10.242.0.4" + }, + "vnetHubAddressSpace": { + "value": "10.242.0.0/20" + }, + // Feature Flags + // set to true if you want to intercept all outbound traffic with azure firewall + "enableEgressLockdown": { + "value": true + }, + // set to true if you want to a redis cache + "deployRedis": { + "value": false + }, + // set to true if you want to deploy a azure SQL server and default database + "deployAzureSql": { + "value": false + }, + // set to true if you want to deploy application configuration + "deployAppConfig": { + "value": false + }, + // set to true if you want to deploy a jumpbox/devops VM + "deployJumpHost": { + "value": false + }, + // Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing , OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites + "deployOpenAi": { + "value": false + }, + // set to true if you want to auto approve the Private Endpoint of the AFD Premium + "autoApproveAfdPrivateEndpoint": { + "value": false + }, + // CIDR of the subnet that will host the azure Firewall + "subnetHubFirewallAddressSpace": { + "value": "10.242.0.0/26" + }, + // CIDR of the subnet that will host the Bastion Service + "subnetHubBastionAddressSpace": { + "value": "10.242.0.64/26" + }, + //CIDR of the spoke vnet that will hold the app services plan and the rest supporting services (and their private endpoints) + "vnetSpokeAddressSpace": { + "value": "10.240.0.0/20" + }, + //CIDR of the subnet that will hold the app services plan. ATTENTION: ASEv3 needs a /24 network + "subnetSpokeAppSvcAddressSpace": { + "value": "10.240.0.0/26" + }, + // //CIDRof the subnet that will hold the private link for the AFD Premium + // "subnetSpokeAfdIngressAddressSpace": { + // "value": "10.240.0.64/26" + // }, + //CIDR of the subnet that will hold devOps agents etc + "subnetSpokeDevOpsAddressSpace": { + "value": "10.240.10.128/26" + }, + //CIDR of the subnet that will hold the private endpoints of the supporting services + "subnetSpokePrivateEndpointAddressSpace": { + "value": "10.240.11.0/24" + }, + + // two options: Windows or Linux + "webAppBaseOs": { + "value": "Windows" + }, + // admin username of the VM agent deployed in the Spoke + "adminUsername": { + "value": "azureuser" + }, + // admin password of the VM agent deployed in the Spoke + "adminPassword": { + "value": "strongpassword" + }, + //Resource tags that we might need to add to all resources (i.e. Environment, Cost center, application name etc) + "resourceTags": { + "value": { + "deployment": "bicep" + } + }, + // The Azure Active Directory (AAD) administrator group used for SQL Server authentication + "sqlServerAdministrators": { + "value": { + // Azure AD group where your Azure administrators are members + "login": "Azure AD SQL Admins", + // Azure AD object ID of the group + "sid": "xxx-xxxx-xxxx-xxxx", + // Azure AD tenant ID where the group is located + "tenantId": "xxxx-xxxxxx-xxxxx-xxxxx-xxx" + } + } + } +} \ No newline at end of file From 950a53749a097978e3c36c0b4a1814c48e480fdd Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Thu, 28 Dec 2023 12:04:48 -0600 Subject: [PATCH 20/49] testing cicd --- .github/workflows/.template.bicep.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/.template.bicep.yml b/.github/workflows/.template.bicep.yml index 59b9bbe1..cd0fc325 100644 --- a/.github/workflows/.template.bicep.yml +++ b/.github/workflows/.template.bicep.yml @@ -76,7 +76,10 @@ jobs: # If Destroy flag is set, delete the stack if ${{ inputs.destroy }}; then echo "Destroying stack $stackName" - az stack delete --name $stackName --yes + + # Possible flags for delete: --delete-all, --delete-resource-groups, --delete-resources + az stack delete --name $stackName --delete-all --yes + exit 0 # Exit successfully fi From 207e0f1f81e43d03d6e038356ea97340e07f2650 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Thu, 28 Dec 2023 12:29:45 -0600 Subject: [PATCH 21/49] testing cicd --- .github/workflows/.template.bicep.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/.template.bicep.yml b/.github/workflows/.template.bicep.yml index cd0fc325..a47b8f0a 100644 --- a/.github/workflows/.template.bicep.yml +++ b/.github/workflows/.template.bicep.yml @@ -43,8 +43,8 @@ jobs: subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - name: Run Bicep linter - run: az bicep build --file main.bicep - working-directory: ${{ inputs.modulePath }} + run: az bicep build --file ${{ inputs.modulePath }} + # working-directory: ${{ inputs.modulePath }} - name: Run PSRule analysis uses: microsoft/ps-rule@v2.9.0 @@ -79,7 +79,7 @@ jobs: # Possible flags for delete: --delete-all, --delete-resource-groups, --delete-resources az stack delete --name $stackName --delete-all --yes - + exit 0 # Exit successfully fi From 2354ea9f90ef8a25089f7bf64a4e8522e27a61d7 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Thu, 28 Dec 2023 12:49:54 -0600 Subject: [PATCH 22/49] testing cicd --- .github/workflows/ase-multitenant.bicep.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ase-multitenant.bicep.yml b/.github/workflows/ase-multitenant.bicep.yml index ce7720d1..28b8eba7 100644 --- a/.github/workflows/ase-multitenant.bicep.yml +++ b/.github/workflows/ase-multitenant.bicep.yml @@ -30,7 +30,7 @@ on: - main - refactor/iac-cicd paths: - - '.github/workflows/scenario1.bicep.yml' + - '.github/workflows/ase-multitenant.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' - '!scenarios/secure-baseline-multitenant/**.md' @@ -39,7 +39,7 @@ on: branches: - main paths: - - '.github/workflows/scenario1.bicep.yml' + - '.github/workflows/ase-multitenant.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' - '!scenarios/secure-baseline-multitenant/**.md' From 5a2d280a24323701e42a70e621f72a3ccbe89e0a Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Thu, 28 Dec 2023 13:12:26 -0600 Subject: [PATCH 23/49] disabling psrule for now --- .github/workflows/.template.bicep.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/.template.bicep.yml b/.github/workflows/.template.bicep.yml index a47b8f0a..a9ed0a0c 100644 --- a/.github/workflows/.template.bicep.yml +++ b/.github/workflows/.template.bicep.yml @@ -46,10 +46,11 @@ jobs: run: az bicep build --file ${{ inputs.modulePath }} # working-directory: ${{ inputs.modulePath }} - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.9.0 - with: - modules: PSRule.Rules.Azure + # TODO: Buildout PSRule policies, review and refactor accordingly. Disabling for now. + # - name: Run PSRule analysis + # uses: microsoft/ps-rule@v2.9.0 + # with: + # modules: PSRule.Rules.Azure deploy: if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' From bc63effe6f5c7bf152c37677d2b19176e5b06b4f Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Thu, 28 Dec 2023 13:13:32 -0600 Subject: [PATCH 24/49] disabling psrule for now --- .github/workflows/ase-multitenant.bicep.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ase-multitenant.bicep.yml b/.github/workflows/ase-multitenant.bicep.yml index 28b8eba7..0b9dcb70 100644 --- a/.github/workflows/ase-multitenant.bicep.yml +++ b/.github/workflows/ase-multitenant.bicep.yml @@ -30,6 +30,7 @@ on: - main - refactor/iac-cicd paths: + - '.github/workflows/_template.bicep.yml' - '.github/workflows/ase-multitenant.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' @@ -39,6 +40,7 @@ on: branches: - main paths: + - '.github/workflows/_template.bicep.yml' - '.github/workflows/ase-multitenant.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' From f8538b5a75a3be345f953c80516d44deb20e1798 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 2 Jan 2024 09:37:38 -0600 Subject: [PATCH 25/49] consolidating tf scenario 1 workflows into a single cicd pipeline --- ...titenant.bicep.yml => scenario1.bicep.yml} | 3 +- .github/workflows/scenario1.terraform.yml | 80 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) rename .github/workflows/{ase-multitenant.bicep.yml => scenario1.bicep.yml} (98%) diff --git a/.github/workflows/ase-multitenant.bicep.yml b/.github/workflows/scenario1.bicep.yml similarity index 98% rename from .github/workflows/ase-multitenant.bicep.yml rename to .github/workflows/scenario1.bicep.yml index 0b9dcb70..be398457 100644 --- a/.github/workflows/ase-multitenant.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -31,7 +31,7 @@ on: - refactor/iac-cicd paths: - '.github/workflows/_template.bicep.yml' - - '.github/workflows/ase-multitenant.bicep.yml' + - '.github/workflows/scenario1.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' - '!scenarios/secure-baseline-multitenant/**.md' @@ -57,6 +57,7 @@ env: jobs: prepare-environment: + name: 'Prepare CICD Environment for Bicep Workflow' runs-on: ubuntu-latest steps: - name: Checkout the code diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index 0acf68af..c9b0922b 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -1,3 +1,83 @@ +name: 'Scenario 1: Terraform HUB Multi-tenant Secure Baseline' + +on: + workflow_dispatch: + inputs: + destroy: + description: 'Destroy resources?' + required: true + type: boolean + default: false + + push: + branches: + - main + paths: + - '.github/workflows/scenario1.terraform.yml' + - '.github/workflows/_template.terraform.yml' + - 'scenarios/secure-baseline-multitenant/terraform/hub/**' + - '!scenarios/secure-baseline-multitenant/terraform/hub/**.md' + + pull_request: + branches: + - main + paths: + - '.github/workflows/scenario1.terraform.yml' + - '.github/workflows/_template.terraform.yml' + - 'scenarios/secure-baseline-multitenant/terraform/**' + - '!scenarios/secure-baseline-multitenant/terraform/**.md' + +permissions: + id-token: write + contents: read + pull-requests: write + +env: + modulePath: 'scenarios/secure-baseline-multitenant/terraform/' + terraformVersion: 1.5.2 # must be greater than or equal to 1.2 for OIDC + backendStateKey: 'scenario1.hub.tfstate' + tfvarPath: 'parameters/ase-multitenant.parameters.tfvars' + +jobs: + prepare-environment: + runs-on: ubuntu-latest + steps: + - name: Checkout the code + uses: actions/checkout@main + outputs: + modulePath: ${{ env.modulePath }}/hub + terraformVersion: ${{ env.terraformVersion }} + backendStateKey: ${{ env.backendStateKey }} + tfvarPath: ${{ env.tfvarPath }} + + terraform-deploy-hub: + name: 'Terraform CICD (Hub Multi-tenant Secure Baseline)' + needs: + - prepare-environment + uses: ./.github/workflows/.template.terraform.yml + with: + modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/spoke + terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} + backendStateKey: ${{ needs.prepare-environment.outputs.backendStateKey }} + tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} + # Ensure this value is a boolean + destroy: ${{ github.event.inputs.destroy == 'true' }} + secrets: inherit + + terraform-deploy-spoke: + name: 'Terraform CICD (Spoke Multi-tenant Secure Baseline)' + needs: + - prepare-environment + - terraform-deploy-hub + uses: ./.github/workflows/.template.terraform.yml + with: + modulePath: ${{ needs.prepare-environment.outputs.modulePath }} + terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} + backendStateKey: ${{ needs.prepare-environment.outputs.backendStateKey }} + tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} + # Ensure this value is a boolean + destroy: ${{ github.event.inputs.destroy == 'true' }} + secrets: inherit name: 'Scenario 1: Terraform Multi-Tenant ASEv3 Secure Baseline' concurrency: From 6c0fc524b45533c9ba97380739a296afcf960e4a Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 2 Jan 2024 09:38:37 -0600 Subject: [PATCH 26/49] consolidating tf scenario 1 workflows into a single cicd pipeline --- .github/workflows/scenario1.terraform.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index c9b0922b..a606be88 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -12,6 +12,7 @@ on: push: branches: - main + - refactor/iac-cicd paths: - '.github/workflows/scenario1.terraform.yml' - '.github/workflows/_template.terraform.yml' From c4bd4c165814ebe40fd9bb0ff9512c835b421ec5 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 2 Jan 2024 09:40:00 -0600 Subject: [PATCH 27/49] consolidating tf scenario 1 workflows into a single cicd pipeline --- .github/workflows/scenario1.terraform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index a606be88..63bd62a5 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -34,7 +34,7 @@ permissions: pull-requests: write env: - modulePath: 'scenarios/secure-baseline-multitenant/terraform/' + modulePath: 'scenarios/secure-baseline-multitenant/terraform' terraformVersion: 1.5.2 # must be greater than or equal to 1.2 for OIDC backendStateKey: 'scenario1.hub.tfstate' tfvarPath: 'parameters/ase-multitenant.parameters.tfvars' From 458b14a73363bc656c45bdc5384e7f34ed0fea33 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 2 Jan 2024 09:40:43 -0600 Subject: [PATCH 28/49] consolidating tf scenario 1 workflows into a single cicd pipeline --- .github/workflows/scenario1.terraform.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index 63bd62a5..43ac023d 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -46,7 +46,7 @@ jobs: - name: Checkout the code uses: actions/checkout@main outputs: - modulePath: ${{ env.modulePath }}/hub + modulePath: ${{ env.modulePath }} terraformVersion: ${{ env.terraformVersion }} backendStateKey: ${{ env.backendStateKey }} tfvarPath: ${{ env.tfvarPath }} @@ -57,7 +57,7 @@ jobs: - prepare-environment uses: ./.github/workflows/.template.terraform.yml with: - modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/spoke + modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/hub terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} backendStateKey: ${{ needs.prepare-environment.outputs.backendStateKey }} tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} @@ -72,7 +72,7 @@ jobs: - terraform-deploy-hub uses: ./.github/workflows/.template.terraform.yml with: - modulePath: ${{ needs.prepare-environment.outputs.modulePath }} + modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/spoke terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} backendStateKey: ${{ needs.prepare-environment.outputs.backendStateKey }} tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} From 074eec0cc8a5520182bd98e82bf47d23c51f1ac7 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Wed, 10 Jan 2024 10:15:40 -0600 Subject: [PATCH 29/49] renaming consolidated scenario 1 tf pipeline --- .github/workflows/scenario1.bicep.yml | 2 +- .github/workflows/scenario1.terraform.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index be398457..694299c9 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -41,7 +41,7 @@ on: - main paths: - '.github/workflows/_template.bicep.yml' - - '.github/workflows/ase-multitenant.bicep.yml' + - '.github/workflows/scenario1.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' - '!scenarios/secure-baseline-multitenant/**.md' diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index 43ac023d..ceb18125 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -1,4 +1,4 @@ -name: 'Scenario 1: Terraform HUB Multi-tenant Secure Baseline' +name: 'Scenario 1: Terraform Multi-Tenant ASEv3 Secure Baseline' on: workflow_dispatch: @@ -59,7 +59,7 @@ jobs: with: modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/hub terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} - backendStateKey: ${{ needs.prepare-environment.outputs.backendStateKey }} + backendStateKey: 'scenario1.hub.tfstate' tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} # Ensure this value is a boolean destroy: ${{ github.event.inputs.destroy == 'true' }} @@ -74,7 +74,7 @@ jobs: with: modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/spoke terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} - backendStateKey: ${{ needs.prepare-environment.outputs.backendStateKey }} + backendStateKey: 'scenario1.spoke.tfstate' tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} # Ensure this value is a boolean destroy: ${{ github.event.inputs.destroy == 'true' }} From 70959e791cab9fec5b3df8a3fea5d5aeaa08f6ab Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 16 Jan 2024 12:44:19 -0600 Subject: [PATCH 30/49] Adding concurrency, removed redundant param files --- .github/workflows/scenario1.bicep.yml | 117 ------------------ .github/workflows/scenario1.terraform.yml | 10 +- .../bicep/parameters/appsvc.parameters.json | 88 ------------- 3 files changed, 8 insertions(+), 207 deletions(-) delete mode 100644 scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 694299c9..0a0f07e2 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -1,23 +1,5 @@ name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' -######################################################### -# Concurrency allows to run 1 cycle at a time -# If worflow is running, 2nd one will automatically go in pending state -# if concurrency is enabled -# If 1st running, 2nd in pending and 3rd is triggered then 2nd which was -# in pending will be cancelled and only 3rd (latest) will run -# -# If this is enabled it will cancel current running and start latest -# cancel-in-progress: true -# -# When a concurrent job or workflow is queued, -# if another job or workflow using the same concurrency group in the repository -# is in progress, the queued job or workflow will be pending. -# -# Any previously pending job or workflow in the concurrency group will be canceled. -# To also cancel any currently running job or workflow in the same concurrency group, -# specify cancel-in-progress: true. -############################################################ concurrency: group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' cancel-in-progress: false @@ -79,102 +61,3 @@ jobs: # Ensure this value is a boolean destroy: ${{ github.event.inputs.destroy == 'true' }} secrets: inherit - -# jobs: -# validate: -# name: "Validate Bicep files" -# runs-on: ubuntu-latest -# steps: -# - name: Checkout the code -# uses: actions/checkout@v4 - -# # Log into Azure via OIDC -# - uses: azure/login@v1 -# with: -# client-id: ${{ secrets.AZURE_CLIENT_ID }} -# tenant-id: ${{ secrets.AZURE_TENANT_ID }} -# subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - -# - name: Run Bicep linter -# run: az bicep build --file main.bicep -# working-directory: ${{ env.modulePath }} - -# # Validate Bicep File -# # Note: Requires same level of permissions to write to resources (no just read only) -# # - name: deployment-validation -# # uses: azure/arm-deploy@v1 -# # with: -# # scope: subscription -# # subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} -# # region: ${{ env.region }} -# # template: ${{ env.modulePath }}/main.bicep -# # parameters: ${{ env.modulePath }}/main.parameters.jsonc -# # failOnStdErr: false -# # additionalArguments: "--what-if" -# # deploymentMode: Validate - - -# build-and-deploy: -# # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - -# timeout-minutes: 360 -# name: "Deploy Bicep templates" -# needs: [lint, validate] -# runs-on: ubuntu-latest -# environment: Production -# steps: -# # Checkout code -# - name: Checkout the code -# uses: actions/checkout@main - -# # Log into Azure via OIDC -# - uses: azure/login@v1 -# with: -# client-id: ${{ secrets.AZURE_CLIENT_ID }} -# tenant-id: ${{ secrets.AZURE_TENANT_ID }} -# subscription-id: ${{ secrets.AZURE_SUBSCRIPTION }} - -# # Validate Bicep File -# - name: what-if -# uses: azure/arm-deploy@v1 -# with: -# scope: subscription -# subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} -# region: ${{ env.region }} -# template: ${{ env.modulePath }}/main.bicep -# parameters: ${{ env.modulePath }}/main.parameters.jsonc -# failOnStdErr: false -# additionalArguments: "--what-if" - -# # Deploy Bicep file -# - name: deploy -# uses: azure/arm-deploy@v1 -# with: -# scope: subscription -# subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} -# region: ${{ env.region }} -# template: ${{ env.modulePath }}/main.bicep -# parameters: ${{ env.modulePath }}/main.parameters.jsonc -# failOnStdErr: false - -# # - name: Run Preflight Validation -# # working-directory: ${{ env.modulePath }} -# # run: | -# # az deployment sub validate \ -# # --location ${{ fromJson(env.config).AZURE_LOCATION }} \ -# # --parameters --template-file main.bicep - -# # Deploy Bicep file, need to point parameters to the main.parameters.json location -# # - name: deploy -# # uses: azure/arm-deploy@v1 -# # with: -# # subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} -# # scope: subscription -# # region: ${{ fromJson(env.config).AZURE_LOCATION }} -# # deploymentName: "${{ fromJson(env.config).DEPLOYMENT_NAME }}-${{ fromJson(env.config).AZURE_LOCATION }}" -# # template: ${{ env.modulePath }}/main.bicep -# # parameters: > -# # workloadName=${{ fromJson(env.config).RESOURCE_NAME_PREFIX }} environment=${{ fromJson(env.config).ENVIRONMENT_TAG }} -# # vmUsername=${{ fromJson(env.config).VM_USERNAME }} vmPassword=${{ secrets.VM_PW }} location=${{ fromJson(env.config).AZURE_LOCATION }} -# # accountName=${{ secrets.ACCOUNT_NAME }} personalAccessToken=${{ secrets.PAT }} CICDAgentType=${{ fromJson(env.config).CICD_AGENT_TYPE}} -# # createRedisResource=${{ fromJson(env.config).CREATE_REDIS_RESOURCE }} redisTier=${{ fromJson(env.config).REDIS_TIER }} \ No newline at end of file diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index ceb18125..5bcd2ebc 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -1,5 +1,9 @@ name: 'Scenario 1: Terraform Multi-Tenant ASEv3 Secure Baseline' +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: false + on: workflow_dispatch: inputs: @@ -16,8 +20,9 @@ on: paths: - '.github/workflows/scenario1.terraform.yml' - '.github/workflows/_template.terraform.yml' - - 'scenarios/secure-baseline-multitenant/terraform/hub/**' - - '!scenarios/secure-baseline-multitenant/terraform/hub/**.md' + - 'scenarios/secure-baseline-multitenant/terraform/**.tf' + - 'scenarios/secure-baseline-multitenant/terraform/**/parameters/ase-multitenant.parameters.tfvars' + - '!scenarios/secure-baseline-multitenant/terraform/**.md' pull_request: branches: @@ -26,6 +31,7 @@ on: - '.github/workflows/scenario1.terraform.yml' - '.github/workflows/_template.terraform.yml' - 'scenarios/secure-baseline-multitenant/terraform/**' + - 'scenarios/secure-baseline-multitenant/terraform/**/parameters/ase-multitenant.parameters.tfvars' - '!scenarios/secure-baseline-multitenant/terraform/**.md' permissions: diff --git a/scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json b/scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json deleted file mode 100644 index 780731b1..00000000 --- a/scenarios/secure-baseline-multitenant/bicep/parameters/appsvc.parameters.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "workloadName" : { - "value": "appsvclza1" - }, - "environmentName": { - "value": "${AZURE_ENV_NAME}" - }, - "deployAseV3": { - "value": false - }, - "vnetHubResourceId": { - "value": "" - }, - "firewallInternalIp": { - "value": "" - }, - "vnetHubAddressSpace": { - "value": "10.242.0.0/20" - }, - "enableEgressLockdown" : { - "value": true - }, - "deployRedis": { - "value": false - }, - "deployAzureSql": { - "value": false - }, - "deployAppConfig": { - "value": false - }, - "deployJumpHost": { - "value": false - }, - "deployOpenAi": { - "value": false - }, - "autoApproveAfdPrivateEndpoint": { - "value": true - }, - "subnetHubFirewallAddressSpace": { - "value": "10.242.0.0/26" - }, - "subnetHubBastionAddressSpace": { - "value": "10.242.0.64/26" - }, - "vnetSpokeAddressSpace": { - "value": "10.240.0.0/20" - }, - "subnetSpokeAppSvcAddressSpace": { - "value": "10.240.0.0/26" - }, - "subnetSpokeDevOpsAddressSpace": { - "value": "10.240.10.128/26" - }, - "subnetSpokePrivateEndpointAddressSpace": { - "value": "10.240.11.0/24" - }, - "webAppPlanSku": { - "value": "S1" - }, - "webAppBaseOs" : - { - "value": "Windows" - }, - "adminUsername": { - "value": "azureuser" - }, - "adminPassword": { - "value": "zxNMasKL12()" - }, - "resourceTags": { - "value": { - "deployment": "bicep" - } - }, - "sqlServerAdministrators": { - "value": { - "login": "Azure AD SQL Admins", - "sid": "xxx-xxxx-xxxx-xxxx", - "tenantId": "xxx-xxxx-xxxx-xxxx" - } - } - } -} From b86ae52038fe9070adab43257542a61fc841fe84 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 16 Jan 2024 12:44:34 -0600 Subject: [PATCH 31/49] Adding concurrency, removed redundant param files --- .../ase-multitenant.parameters.jsonc | 2 +- .../ase-zone-redundant.parameters.jsonc | 129 ------------------ 2 files changed, 1 insertion(+), 130 deletions(-) delete mode 100644 scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc diff --git a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc index f87d79a8..f6fc939e 100644 --- a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc +++ b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc @@ -4,7 +4,7 @@ "parameters": { // max length: 10. Suffix that will be used to name the resources in a pattern like - "workloadName": { - "value": "ase-zones" + "value": "ase-mul" // "value": "merge-mul" }, //Required. The name of the environmentName (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long. diff --git a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc deleted file mode 100644 index 2ff9bc3e..00000000 --- a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-zone-redundant.parameters.jsonc +++ /dev/null @@ -1,129 +0,0 @@ -// Scenario: ASE Zone Redundant Multitenant deployment -// ----------------------------------------------------------------- -// Zone redundancy for the app service plan/environment is controlled by the `webappPlanSku` -// parameter by appending an `AZ` to the suffix like so: -// P3V3 (non zonal redundant) -> P3V3_AZ (zonal redundant) -// ASE_I3V2 (non zonal redundant) -> ASE_I3V2_AZ (zonal redundant) -// Module that controls this behavior can be found here [../../modules/app-service.module.bicep] -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deploying at least three instances in three Availability Zones. EP* is only for functions' - // select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ', 'EP1', 'EP2', 'EP3', 'ASE_I1V2_AZ', 'ASE_I2V2_AZ', 'ASE_I3V2_AZ', 'ASE_I1V2', 'ASE_I2V2', 'ASE_I3V2' - "webAppPlanSku": { - "value": "ASE_I3V2_AZ" - }, - // max length: 10. Suffix that will be used to name the resources in a pattern like - - "workloadName": { - "value": "ase-zones" - // "value": "merge-mul" - }, - //Required. The name of the environmentName (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long. - "environmentName": { - "value": "dev" - }, - //Optional, default is false. Set to true if you want to deploy ASE v3 instead of Multitenant App Service Plan. - "deployAseV3": { - "value": true - }, - //If empty, then a new hub will be deployed. If given, no new hub will be created and we create the peering between spoke and and existing hub vnet - "vnetHubResourceId": { - "value": "" - }, - //If vnetHubResourceId empty, this value is irrelevant. If vnetHubResourceId has value and we need UDR, then the internal IP of the azFW needs to be set. Otherwise no UDR will be created - "firewallInternalIp": { - "value": "10.242.0.4" - }, - "vnetHubAddressSpace": { - "value": "10.242.0.0/20" - }, - // Feature Flags - // set to true if you want to intercept all outbound traffic with azure firewall - "enableEgressLockdown": { - "value": true - }, - // set to true if you want to a redis cache - "deployRedis": { - "value": false - }, - // set to true if you want to deploy a azure SQL server and default database - "deployAzureSql": { - "value": false - }, - // set to true if you want to deploy application configuration - "deployAppConfig": { - "value": false - }, - // set to true if you want to deploy a jumpbox/devops VM - "deployJumpHost": { - "value": false - }, - // Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing , OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites - "deployOpenAi": { - "value": false - }, - // set to true if you want to auto approve the Private Endpoint of the AFD Premium - "autoApproveAfdPrivateEndpoint": { - "value": false - }, - // CIDR of the subnet that will host the azure Firewall - "subnetHubFirewallAddressSpace": { - "value": "10.242.0.0/26" - }, - // CIDR of the subnet that will host the Bastion Service - "subnetHubBastionAddressSpace": { - "value": "10.242.0.64/26" - }, - //CIDR of the spoke vnet that will hold the app services plan and the rest supporting services (and their private endpoints) - "vnetSpokeAddressSpace": { - "value": "10.240.0.0/20" - }, - //CIDR of the subnet that will hold the app services plan. ATTENTION: ASEv3 needs a /24 network - "subnetSpokeAppSvcAddressSpace": { - "value": "10.240.0.0/26" - }, - // //CIDRof the subnet that will hold the private link for the AFD Premium - // "subnetSpokeAfdIngressAddressSpace": { - // "value": "10.240.0.64/26" - // }, - //CIDR of the subnet that will hold devOps agents etc - "subnetSpokeDevOpsAddressSpace": { - "value": "10.240.10.128/26" - }, - //CIDR of the subnet that will hold the private endpoints of the supporting services - "subnetSpokePrivateEndpointAddressSpace": { - "value": "10.240.11.0/24" - }, - - // two options: Windows or Linux - "webAppBaseOs": { - "value": "Windows" - }, - // admin username of the VM agent deployed in the Spoke - "adminUsername": { - "value": "azureuser" - }, - // admin password of the VM agent deployed in the Spoke - "adminPassword": { - "value": "strongpassword" - }, - //Resource tags that we might need to add to all resources (i.e. Environment, Cost center, application name etc) - "resourceTags": { - "value": { - "deployment": "bicep" - } - }, - // The Azure Active Directory (AAD) administrator group used for SQL Server authentication - "sqlServerAdministrators": { - "value": { - // Azure AD group where your Azure administrators are members - "login": "Azure AD SQL Admins", - // Azure AD object ID of the group - "sid": "xxx-xxxx-xxxx-xxxx", - // Azure AD tenant ID where the group is located - "tenantId": "xxxx-xxxxxx-xxxxx-xxxxx-xxx" - } - } - } -} \ No newline at end of file From 92e6bc7186c3fbee835b99fefb0dc803d1dd765f Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 16 Jan 2024 12:47:52 -0600 Subject: [PATCH 32/49] removing test branch trigger --- .github/workflows/scenario1.bicep.yml | 1 - .github/workflows/scenario1.terraform.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 0a0f07e2..9bc2640c 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -10,7 +10,6 @@ on: push: branches: - main - - refactor/iac-cicd paths: - '.github/workflows/_template.bicep.yml' - '.github/workflows/scenario1.bicep.yml' diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index 5bcd2ebc..90fbda25 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -16,7 +16,6 @@ on: push: branches: - main - - refactor/iac-cicd paths: - '.github/workflows/scenario1.terraform.yml' - '.github/workflows/_template.terraform.yml' From 55d048b0ef073789e55e69a85e0ee95bba64d519 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Sat, 20 Jan 2024 15:49:36 -0600 Subject: [PATCH 33/49] cleanup --- .github/workflows/.template.bicep.yml | 20 ++- .github/workflows/scenario1.bicep.yml | 17 ++- .../ase-multitenant.parameters.jsonc | 121 ------------------ 3 files changed, 28 insertions(+), 130 deletions(-) delete mode 100644 scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc diff --git a/.github/workflows/.template.bicep.yml b/.github/workflows/.template.bicep.yml index a9ed0a0c..70e25c5a 100644 --- a/.github/workflows/.template.bicep.yml +++ b/.github/workflows/.template.bicep.yml @@ -13,10 +13,20 @@ on: description: 'Path to the Bicep module' required: true default: 'scenarios/secure-baseline-multitenant/bicep' + deployStackName: + type: string + description: 'Name of the subscription scoped stack to deploy' + required: false + default: 'secure-baseline-multitenant' bicepParamPath: type: string description: 'Path to the Bicep variables' required: true + bicepAdditionalParams: + type: string + description: 'Optional parameters to pass to Bicep in string format' + required: false + default: --deny-settings-mode 'none' destroy: type: boolean description: 'Destroy resources?' @@ -72,23 +82,21 @@ jobs: - name: Deploy Bicep Scenario run: | - stackName=$(basename ${{ inputs.bicepParamPath }} ) - # If Destroy flag is set, delete the stack if ${{ inputs.destroy }}; then - echo "Destroying stack $stackName" + echo "Destroying stack ${{ inputs.deployStackName }}" # Possible flags for delete: --delete-all, --delete-resource-groups, --delete-resources - az stack delete --name $stackName --delete-all --yes + az stack delete --name ${{ inputs.deployStackName }} --delete-all --yes exit 0 # Exit successfully fi - az stack sub create --name $stackName \ + az stack sub create --name ${{ inputs.deployStackName }} \ --location ${{ inputs.region }} \ --template-file ${{ inputs.modulePath }} \ --parameters ${{ inputs.bicepParamPath }} \ - --deny-settings-mode 'none' + ${{ inputs.bicepAdditionalParams }} # Potential Deny Settings # ----------------------------- diff --git a/.github/workflows/scenario1.bicep.yml b/.github/workflows/scenario1.bicep.yml index 9bc2640c..2b9c40d9 100644 --- a/.github/workflows/scenario1.bicep.yml +++ b/.github/workflows/scenario1.bicep.yml @@ -1,5 +1,9 @@ name: 'Scenario 1: Bicep Multi-Tenant ASEv3 Secure Baseline' +# This workflow tests the ASEv3 Secure Baseline Multi-Tenant scenario deployment. +# This will use the default parameter file (main.parameters.jsonc) with an overridden +# SKU to deploy ASEv3 + concurrency: group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' cancel-in-progress: false @@ -14,7 +18,7 @@ on: - '.github/workflows/_template.bicep.yml' - '.github/workflows/scenario1.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' + - 'scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc' - '!scenarios/secure-baseline-multitenant/**.md' pull_request: @@ -24,7 +28,7 @@ on: - '.github/workflows/_template.bicep.yml' - '.github/workflows/scenario1.bicep.yml' - 'scenarios/secure-baseline-multitenant/bicep/**.bicep' - - 'scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc' + - 'scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc' - '!scenarios/secure-baseline-multitenant/**.md' permissions: @@ -32,9 +36,12 @@ permissions: contents: read env: + deployStackName: 'Scenario1-ASEv3-Secure-Baseline-MultiTenant' deploymentPath: 'scenarios/secure-baseline-multitenant/bicep/main.bicep' scenarioName: 'ase-multitenant' region: 'westus2' + # webAppPlanSKU is the only parameter that is overridden for ASEv3 + webAppPlanSku: 'ASE_I3V2_AZ' jobs: prepare-environment: @@ -44,9 +51,11 @@ jobs: - name: Checkout the code uses: actions/checkout@main outputs: + deployStackName: ${{ env.deployStackName }} region: ${{ env.region }} modulePath: ${{ env.deploymentPath }} - bicepParamPath: ${{ env.deploymentPath }}/parameters/${{ env.scenarioName }}.parameters.jsonc + bicepParamPath: ${{ env.deploymentPath }}/main.parameters.jsonc + bicepAdditionalParams: -p webAppPlanSku=${{ env.webAppPlanSku }} --deny-settings-mode 'none' call-workflow-passing-data: name: 'Bicep CICD' @@ -54,9 +63,11 @@ jobs: - prepare-environment uses: ./.github/workflows/.template.bicep.yml with: + deployStackName: ${{ needs.prepare-environment.outputs.deployStackName }} region: ${{ needs.prepare-environment.outputs.region }} modulePath: ${{ needs.prepare-environment.outputs.modulePath }} bicepParamPath: ${{ needs.prepare-environment.outputs.bicepParamPath }} + bicepAdditionalParams: ${{ needs.prepare-environment.outputs.bicepAdditionalParams }} # Ensure this value is a boolean destroy: ${{ github.event.inputs.destroy == 'true' }} secrets: inherit diff --git a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc b/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc deleted file mode 100644 index f6fc939e..00000000 --- a/scenarios/secure-baseline-multitenant/bicep/parameters/ase-multitenant.parameters.jsonc +++ /dev/null @@ -1,121 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - // max length: 10. Suffix that will be used to name the resources in a pattern like - - "workloadName": { - "value": "ase-mul" - // "value": "merge-mul" - }, - //Required. The name of the environmentName (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long. - "environmentName": { - "value": "dev" - }, - //Optional, default is false. Set to true if you want to deploy ASE v3 instead of Multitenant App Service Plan. - "deployAseV3": { - "value": true - }, - //If empty, then a new hub will be deployed. If given, no new hub will be created and we create the peering between spoke and and existing hub vnet - "vnetHubResourceId": { - "value": "" - }, - //If vnetHubResourceId empty, this value is irrelevant. If vnetHubResourceId has value and we need UDR, then the internal IP of the azFW needs to be set. Otherwise no UDR will be created - "firewallInternalIp": { - "value": "10.242.0.4" - }, - "vnetHubAddressSpace": { - "value": "10.242.0.0/20" - }, - // Feature Flags - // set to true if you want to intercept all outbound traffic with azure firewall - "enableEgressLockdown": { - "value": true - }, - // set to true if you want to a redis cache - "deployRedis": { - "value": false - }, - // set to true if you want to deploy a azure SQL server and default database - "deployAzureSql": { - "value": false - }, - // set to true if you want to deploy application configuration - "deployAppConfig": { - "value": false - }, - // set to true if you want to deploy a jumpbox/devops VM - "deployJumpHost": { - "value": false - }, - // Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing , OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites - "deployOpenAi": { - "value": false - }, - // set to true if you want to auto approve the Private Endpoint of the AFD Premium - "autoApproveAfdPrivateEndpoint": { - "value": false - }, - // CIDR of the subnet that will host the azure Firewall - "subnetHubFirewallAddressSpace": { - "value": "10.242.0.0/26" - }, - // CIDR of the subnet that will host the Bastion Service - "subnetHubBastionAddressSpace": { - "value": "10.242.0.64/26" - }, - //CIDR of the spoke vnet that will hold the app services plan and the rest supporting services (and their private endpoints) - "vnetSpokeAddressSpace": { - "value": "10.240.0.0/20" - }, - //CIDR of the subnet that will hold the app services plan. ATTENTION: ASEv3 needs a /24 network - "subnetSpokeAppSvcAddressSpace": { - "value": "10.240.0.0/26" - }, - // //CIDRof the subnet that will hold the private link for the AFD Premium - // "subnetSpokeAfdIngressAddressSpace": { - // "value": "10.240.0.64/26" - // }, - //CIDR of the subnet that will hold devOps agents etc - "subnetSpokeDevOpsAddressSpace": { - "value": "10.240.10.128/26" - }, - //CIDR of the subnet that will hold the private endpoints of the supporting services - "subnetSpokePrivateEndpointAddressSpace": { - "value": "10.240.11.0/24" - }, - // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deploying at least three instances in three Availability Zones. EP* is only for functions' - // select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ', 'EP1', 'EP2', 'EP3', 'ASE_I1V2_AZ', 'ASE_I2V2_AZ', 'ASE_I3V2_AZ', 'ASE_I1V2', 'ASE_I2V2', 'ASE_I3V2' - "webAppPlanSku": { - "value": "ASE_I3V2" - }, - // two options: Windows or Linux - "webAppBaseOs": { - "value": "Windows" - }, - // admin username of the VM agent deployed in the Spoke - "adminUsername": { - "value": "azureuser" - }, - // admin password of the VM agent deployed in the Spoke - "adminPassword": { - "value": "strongpassword" - }, - //Resource tags that we might need to add to all resources (i.e. Environment, Cost center, application name etc) - "resourceTags": { - "value": { - "deployment": "bicep" - } - }, - // The Azure Active Directory (AAD) administrator group used for SQL Server authentication - "sqlServerAdministrators": { - "value": { - // Azure AD group where your Azure administrators are members - "login": "Azure AD SQL Admins", - // Azure AD object ID of the group - "sid": "xxx-xxxx-xxxx-xxxx", - // Azure AD tenant ID where the group is located - "tenantId": "xxxx-xxxxxx-xxxxx-xxxxx-xxx" - } - } - } -} \ No newline at end of file From 1cb2704a3611f66f2d828ea9cfc896c7ee6bec9b Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Sat, 20 Jan 2024 15:49:52 -0600 Subject: [PATCH 34/49] cleanup --- .github/workflows/.template.bicep.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.template.bicep.yml b/.github/workflows/.template.bicep.yml index 70e25c5a..f6e0e979 100644 --- a/.github/workflows/.template.bicep.yml +++ b/.github/workflows/.template.bicep.yml @@ -56,7 +56,7 @@ jobs: run: az bicep build --file ${{ inputs.modulePath }} # working-directory: ${{ inputs.modulePath }} - # TODO: Buildout PSRule policies, review and refactor accordingly. Disabling for now. + # TODO: Buildout PSRule policies # - name: Run PSRule analysis # uses: microsoft/ps-rule@v2.9.0 # with: From c18f0d5f1cdf63a7a6cdce78efbcbc806d4c78a9 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Fri, 16 Feb 2024 09:56:04 -0600 Subject: [PATCH 35/49] wip --- .../secure-baseline-multitenant/.gitignore | 1 + .../terraform/README.md | 63 ++++ .../terraform/_locals.tf | 71 +++++ .../hub/{hub-network.tf => network.tf} | 13 +- .../terraform/main.tf | 54 ++++ .../terraform/multitenant-asev3.tf | 74 +++++ .../terraform/spoke/README.md | 31 +- .../terraform/spoke/app.tf | 5 + .../terraform/spoke/asev3.tf | 52 ++++ .../terraform/spoke/data.tf | 14 +- .../terraform/spoke/identity.tf | 3 + .../terraform/spoke/{law.tf => monitoring.tf} | 0 .../terraform/spoke/network.tf | 82 +++-- .../terraform/spoke/variables.tf | 58 ++-- .../terraform/variables.tf | 280 ++++++++++++++++++ .../terraform-modules/sql-database/README.md | 6 +- .../terraform-modules/sql-database/module.tf | 5 + .../terraform-modules/windows-vm/README.md | 8 +- 18 files changed, 728 insertions(+), 92 deletions(-) create mode 100644 scenarios/secure-baseline-multitenant/terraform/_locals.tf rename scenarios/secure-baseline-multitenant/terraform/hub/{hub-network.tf => network.tf} (83%) create mode 100644 scenarios/secure-baseline-multitenant/terraform/main.tf create mode 100644 scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf create mode 100644 scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf rename scenarios/secure-baseline-multitenant/terraform/spoke/{law.tf => monitoring.tf} (100%) create mode 100644 scenarios/secure-baseline-multitenant/terraform/variables.tf diff --git a/scenarios/secure-baseline-multitenant/.gitignore b/scenarios/secure-baseline-multitenant/.gitignore index 8e843802..d39999b9 100644 --- a/scenarios/secure-baseline-multitenant/.gitignore +++ b/scenarios/secure-baseline-multitenant/.gitignore @@ -1 +1,2 @@ .azure +backend.hcl \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index b5d30b4b..725f7ba9 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -261,3 +261,66 @@ Connect to the VM using the local VM admin credentials and run `dsregcmd /status ``` If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra ID credentials again after a few minutes. If it's not Microsoft Entra ID joined, attempt to re-install the VM extension or manually enroll the VM to Microsoft Entra ID by following the steps in Edge: open Edge and click "Sign in to sync data", select "Work or school account", and then press OK on "Allow my organization to manage my device". It takes a few minutes for the policies to be applied, device scanned and confirmed as secure to access corporate resources. You will know that the process is complete. + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >=1.3 | +| [azurecaf](#requirement\_azurecaf) | >=1.2.23 | +| [azurerm](#requirement\_azurerm) | >=3.66.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | 1.2.27 | +| [azurerm](#provider\_azurerm) | 3.89.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [hub](#module\_hub) | ./hub | n/a | +| [spoke](#module\_spoke) | ./spoke | n/a | + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_spoke_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | +| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | +| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | +| [devops\_settings](#input\_devops\_settings) | The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | +| [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | +| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | +| [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | +| [hub\_remote\_state\_settings](#input\_hub\_remote\_state\_settings) | The settings for the hub remote state. |
object({
resource_group_name = string
storage_account_name = string
container_name = string
key = string
})
| `null` | no | +| [hub\_vnet\_settings](#input\_hub\_vnet\_settings) | The settings for the hub virtual network. |
object({
resource_group_name = string
name = string

firewall = {
private_ip = optional(string)
}
})
| `null` | no | +| [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | +| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | +| [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | +| [sql\_databases](#input\_sql\_databases) | The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | +| [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | +| [tenant\_id](#input\_tenant\_id) | The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [vm\_admin\_password](#input\_vm\_admin\_password) | The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Azure AD username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | + +## Outputs + +No outputs. + diff --git a/scenarios/secure-baseline-multitenant/terraform/_locals.tf b/scenarios/secure-baseline-multitenant/terraform/_locals.tf new file mode 100644 index 00000000..c723afad --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/_locals.tf @@ -0,0 +1,71 @@ +locals { + deployment_name = "sec-baseline-1" + + # # used in spoke-network.tf + # private_dns_zones = [for each in + # [ + # { + # name : "privatelink.azurewebsites.net" + # records : [] + # enabled : true + # }, + # { + # name : "privatelink.vaultcore.azure.net" + # records : [] + # enabled : true + # }, + # { + # name : "privatelink.database.windows.net" + # records : [] + # enabled : var.deployment_options.deploy_sql_database + # }, + # { + # name : "privatelink.azconfig.io" + # records : [] + # enabled : var.deployment_options.deploy_app_config + # }, + # { + # name : "privatelink.redis.cache.windows.net" + # records : [] + # enabled : var.deployment_options.deploy_redis + # }, + # { + # name : "privatelink.openai.azure.com" + # records : [] + # enabled : var.deployment_options.deploy_openai + # } + # ] : each if each.enabled + # ] + + # provisioned_dns_zones = { for i, dns_zone in module.private_dns_zones : dns_zone.name => dns_zone.dns_zone } + + global_settings = merge({ + environment = try(var.global_settings.environment, var.environment) + passthrough = try(var.global_settings.passthrough, false) + prefixes = try(var.global_settings.prefixes, [local.deployment_name, local.short_location]) + suffixes = try(var.global_settings.suffixes, [var.environment]) + random_length = try(var.global_settings.random_length, 0) + regions = try(var.global_settings.regions, null) + tags = try(var.global_settings.tags, null) + use_slug = try(var.global_settings.use_slug, true) + }, var.global_settings) + + short_location_map = { + "eastus" : "eus" + "eastus2" : "eus2" + "westus" : "wus" + "westus2" : "wus2" + "westeurope" : "weu" + "easteurope" : "eeu" + "southcentralus" : "scus" + } + + short_location = try(local.short_location_map[var.location], var.location) + + base_tags = merge({ + "Terraform" = true + "Environment" = local.global_settings.environment + "Owner" = var.owner + "Project" = "[Scenario 1] App Service Landing Zone Accelerator" + }, var.tags) +} diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/hub-network.tf b/scenarios/secure-baseline-multitenant/terraform/hub/network.tf similarity index 83% rename from scenarios/secure-baseline-multitenant/terraform/hub/hub-network.tf rename to scenarios/secure-baseline-multitenant/terraform/hub/network.tf index 0face5eb..d3f04763 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/hub-network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/network.tf @@ -1,5 +1,13 @@ # Hub network config - +# ----- +# - Hub Resource Group +# - VNet +# - Firewall Subnet +# - Bastion Subnet +# - Azure Firewall [optional] +# - Bastion [optional] + +## Create Hub Resource Group with the name generated from global_settings resource "azurecaf_name" "caf_name_hub_rg" { name = var.application_name resource_type = "azurerm_resource_group" @@ -17,6 +25,7 @@ resource "azurerm_resource_group" "hub" { tags = local.base_tags } +## Deploy Hub VNet with Firewall and Bastion subnets module "network" { source = "../../../shared/terraform-modules/network" @@ -41,6 +50,7 @@ module "network" { tags = local.base_tags } +## Deploy Azure Firewall (enabled via deployment option) module "firewall" { count = var.deployment_options.enable_egress_lockdown ? 1 : 0 @@ -60,6 +70,7 @@ module "firewall" { tags = local.base_tags } +## Deploy Bastion (enabled via deployment option) module "bastion" { count = var.deployment_options.deploy_bastion ? 1 : 0 diff --git a/scenarios/secure-baseline-multitenant/terraform/main.tf b/scenarios/secure-baseline-multitenant/terraform/main.tf new file mode 100644 index 00000000..d28d493a --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/main.tf @@ -0,0 +1,54 @@ +terraform { + # must be greater than or equal to 1.2 for OIDC + # must be greater than or equal to 1.3 for OpenAI + required_version = ">=1.3" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.66.0" + } + azurecaf = { + source = "aztfmod/azurecaf" + version = ">=1.2.23" + } + } + + # If called as a module, this backend configuration block will have no effect. + backend "azurerm" {} +} + +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + + virtual_machine { + delete_os_disk_on_deletion = true + graceful_shutdown = false + skip_shutdown_and_force_delete = true + } + } + + # DO NOT CHANGE THE BELOW VALUES + disable_terraform_partner_id = false + partner_id = "cf7e9f0a-f872-49db-b72f-f2e318189a6d" +} + +resource "azurecaf_name" "caf_name_spoke_rg" { + name = var.application_name + resource_type = "azurerm_resource_group" + prefixes = concat(["spoke"], local.global_settings.prefixes) + random_length = local.global_settings.random_length + clean_input = true + passthrough = local.global_settings.passthrough + use_slug = local.global_settings.use_slug +} + +resource "azurerm_resource_group" "spoke" { + name = azurecaf_name.caf_name_spoke_rg.result + location = var.location + + tags = local.base_tags +} \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf new file mode 100644 index 00000000..d4935985 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf @@ -0,0 +1,74 @@ + +# Create Hub via module +module "hub" { + source = "./hub" + + application_name = var.application_name + environment = var.environment + location = var.location + owner = var.owner + + deployment_options = var.deployment_options + global_settings = var.global_settings + tags = var.tags +} + +module "spoke" { + source = "./spoke" + + application_name = var.application_name + environment = var.environment + location = var.location + owner = var.owner + tenant_id = var.tenant_id + + hub_vnet_settings = { + resource_group_name = module.hub.rg_name + name = module.hub.vnet_name + + firewall = { + private_ip = module.hub.firewall_private_ip + } + } + + ## + entra_admin_group_name = var.entra_admin_group_name + entra_admin_group_object_id = var.entra_admin_group_object_id + + vm_entra_admin_object_id = var.vm_entra_admin_object_id + +} + +# Create Spoke via module +module "spoke" { + source = "./spoke" + + application_name = var.application_name + environment = var.environment + location = var.location + owner = var.owner + tenant_id = var.tenant_id + + spoke_vnet_cidr = var.spoke_vnet_cidr + devops_subnet_cidr = var.devops_subnet_cidr + appsvc_subnet_cidr = var.appsvc_subnet_cidr + front_door_subnet_cidr = var.front_door_subnet_cidr + private_link_subnet_cidr = var.private_link_subnet_cidr + vm_admin_password = var.vm_admin_password + vm_admin_username = var.vm_admin_username + vm_entra_admin_username = var.vm_entra_admin_username + + deployment_options = var.deployment_options + + global_settings = var.global_settings + tags = var.tags + + entra_admin_group_name = var.entra_admin_group_name + entra_admin_group_object_id = var.entra_admin_group_object_id + + vm_entra_admin_object_id = var.vm_entra_admin_object_id + + depends_on = [ + module.hub + ] +} \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md index 6326e594..fb166109 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md @@ -39,11 +39,13 @@ | Name | Type | |------|------| | [azurecaf_name.appsvc_subnet](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_asev3](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_id_contributor](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_id_reader](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_spoke_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_app_service_environment_v3.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_environment_v3) | resource | | [azurerm_log_analytics_workspace.law](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/log_analytics_workspace) | resource | | [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | | [azurerm_user_assigned_identity.contributor](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | @@ -56,37 +58,30 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [aad\_admin\_group\_name](#input\_aad\_admin\_group\_name) | The name of the Microsoft Entra ID group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | -| [aad\_admin\_group\_object\_id](#input\_aad\_admin\_group\_object\_id) | The object ID of the Microsoft Entra ID group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | | [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | | [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | | [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | -| [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | -| [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | -| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | +| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_asev3 = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_asev3": false,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | | [devops\_settings](#input\_devops\_settings) | The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | | [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | | [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | -| [firewall\_subnet\_cidr](#input\_firewall\_subnet\_cidr) | [Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26 | `list(string)` |
[
"10.242.0.0/26"
]
| no | -| [firewall\_subnet\_name](#input\_firewall\_subnet\_name) | [Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet' | `string` | `"AzureFirewallSubnet"` | no | | [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | | [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | -| [hub\_settings](#input\_hub\_settings) | The settings for the hub virtual network. |
object({
rg_name = string
vnet_name = string

firewall = object({
private_ip = optional(string)
})
})
| `null` | no | -| [hub\_state\_container\_name](#input\_hub\_state\_container\_name) | The name of the container that holds the Terraform state for the hub | `string` | n/a | yes | -| [hub\_state\_key](#input\_hub\_state\_key) | The key of the Terraform state for the hub | `string` | n/a | yes | -| [hub\_state\_resource\_group\_name](#input\_hub\_state\_resource\_group\_name) | The name of the resource group that holds the Terraform state for the hub | `string` | n/a | yes | -| [hub\_state\_storage\_account\_name](#input\_hub\_state\_storage\_account\_name) | The name of the storage account that holds the Terraform state for the hub | `string` | n/a | yes | -| [hub\_vnet\_cidr](#input\_hub\_vnet\_cidr) | [Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20 | `list(string)` |
[
"10.242.0.0/20"
]
| no | +| [hub\_remote\_state\_settings](#input\_hub\_remote\_state\_settings) | The settings for the hub remote state. |
object({
resource_group_name = string
storage_account_name = string
container_name = string
key = string
})
| `null` | no | +| [hub\_vnet\_settings](#input\_hub\_vnet\_settings) | The settings for the hub virtual network. |
object({
resource_group_name = string
name = string

firewall = optional(object({
private_ip = optional(string)
})
)
})
| `null` | no | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | | [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | +| [sql\_databases](#input\_sql\_databases) | The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | | [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | -| [tenant\_id](#input\_tenant\_id) | The Microsoft Entra ID tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | -| [vm\_aad\_admin\_object\_id](#input\_vm\_aad\_admin\_object\_id) | The Microsoft Entra ID object ID for the VM admin user/group. If vm\_aad\_admin\_username is not specified, this value will be used. | `string` | `null` | no | -| [vm\_aad\_admin\_username](#input\_vm\_aad\_admin\_username) | [Optional] The Microsoft Entra ID username for the VM admin account. If vm\_aad\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | -| [vm\_admin\_password](#input\_vm\_admin\_password) | The password for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra ID admin account. | `string` | `null` | no | -| [vm\_admin\_username](#input\_vm\_admin\_username) | The username for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra ID admin account. | `string` | `null` | no | +| [tenant\_id](#input\_tenant\_id) | The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [vm\_admin\_password](#input\_vm\_admin\_password) | The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Azure AD username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | ## Outputs diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf index c04c88ac..79a63e92 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf @@ -1,8 +1,13 @@ +# Spoke application deployment +# ------ +# - App Service +# - locals { sql_connstring = length(module.sql_database) > 0 ? module.sql_database[0].sql_db_connection_string : "SQL_NOT_PROVISIONED" redis_connstring = length(module.redis_cache) > 0 ? module.redis_cache[0].redis_connection_string : "REDIS_NOT_PROVISIONED" } +# Deploy the App Service module "app_service" { source = "../../../shared/terraform-modules/app-service" diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf new file mode 100644 index 00000000..aecfd149 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf @@ -0,0 +1,52 @@ +# Not broken into its own module as it is only used in this scenario +resource "azurecaf_name" "caf_name_asev3" { + count = var.deployment_options.deploy_asev3 ? 1 : 0 + + name = var.application_name + resource_type = "azurerm_app_service_environment" + prefixes = var.global_settings.prefixes + suffixes = var.global_settings.suffixes + random_length = var.global_settings.random_length + clean_input = true + passthrough = var.global_settings.passthrough + + use_slug = var.global_settings.use_slug +} + +resource "azurerm_app_service_environment_v3" "this" { + count = var.deployment_options.deploy_asev3 ? 1 : 0 + + name = azurecaf_name.caf_name_asev3.0.result + resource_group_name = azurerm_resource_group.spoke.name + + # a /24 or larger CIDR is required. Once associated with an ASE, this size cannot be changed. + subnet_id = module.network.subnets["serverFarm"].id + + # Possible values are None (for an External VIP Type), and "Web, Publishing" (for an Internal VIP Type). + internal_load_balancing_mode = "Web, Publishing" + + # You can only set either dedicated_host_count or zone_redundant but not both. + # Changing this forces a new resource to be created. + # dedicated_host_count = 2 + + # Changing this forces a new resource to be created. + zone_redundant = false + + + cluster_setting { + name = "DisableTls1.0" + value = "1" + } + + cluster_setting { + name = "InternalEncryption" + value = "true" + } + + cluster_setting { + name = "FrontEndSSLCipherSuiteOrder" + value = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + } + + tags = local.base_tags +} \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf index 48fe4423..7c4c79a8 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf @@ -1,14 +1,12 @@ +# Lookup the Hub VNet by either the `hub_remote_state_settings` or the `hub_vnet_settings` variables +# If both are provided, hub_vnet_settings will be used. data "terraform_remote_state" "hub" { + count = var.hub_vnet_settings == null ? 1 : 0 backend = "azurerm" - config = { - resource_group_name = var.hub_state_resource_group_name - storage_account_name = var.hub_state_storage_account_name - container_name = var.hub_state_container_name - key = var.hub_state_key - } + config = var.hub_remote_state_settings } data "azurerm_virtual_network" "hub" { - name = data.terraform_remote_state.hub.outputs.vnet_name - resource_group_name = data.terraform_remote_state.hub.outputs.rg_name + name = var.hub_vnet_settings == null ? data.terraform_remote_state.hub[0].outputs.vnet_name : var.hub_vnet_settings.name + resource_group_name = var.hub_vnet_settings == null ? data.terraform_remote_state.hub[0].outputs.rg_name : var.hub_vnet_settings.resource_group_name } diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf index db18967a..47d1b99a 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf @@ -1,3 +1,6 @@ +# Creates two user-assigned-identities for Reader and Contributor roles +# to be consumed in app.tf + resource "azurecaf_name" "caf_name_id_reader" { name = var.application_name resource_type = "azurerm_user_assigned_identity" diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/law.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/monitoring.tf similarity index 100% rename from scenarios/secure-baseline-multitenant/terraform/spoke/law.tf rename to scenarios/secure-baseline-multitenant/terraform/spoke/monitoring.tf diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf index 8c10272e..9e0554f4 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf @@ -1,8 +1,31 @@ # Spoke network config +# ----- +# - Spoke Resource Group +# - VNet +# - Server Farm Subnet (App Service/compute resources) +# - Ingress Subnet (Azure Front Door network ingress subnet) +# - Private Link Subnet (Private DNS Zones) +# - DevOps Subnet (optional Self Hosted CICD agent) +# - Private DNS Zones +# - User Defined Routes [optional] +# - Azure FrontDoor + +## Create Spoke Resource Group with the name generated from global_settings +resource "azurecaf_name" "caf_name_spoke_rg" { + name = var.application_name + resource_type = "azurerm_resource_group" + prefixes = concat(["spoke"], local.global_settings.prefixes) + random_length = local.global_settings.random_length + clean_input = true + passthrough = local.global_settings.passthrough + use_slug = local.global_settings.use_slug +} -resource "random_integer" "unique_id" { - min = 1 - max = 9999 +resource "azurerm_resource_group" "spoke" { + name = azurecaf_name.caf_name_spoke_rg.result + location = var.location + + tags = local.base_tags } resource "azurecaf_name" "appsvc_subnet" { @@ -15,6 +38,7 @@ resource "azurecaf_name" "appsvc_subnet" { use_slug = local.global_settings.use_slug } +## Deploy Spoke VNet with Server Farm, Ingress, Private Link and DevOps subnets module "network" { source = "../../../shared/terraform-modules/network" @@ -61,6 +85,7 @@ module "network" { tags = local.base_tags } +## Deploy Private DNS Zones module "private_dns_zones" { source = "../../../shared/terraform-modules/private-dns-zone" count = length(local.private_dns_zones) @@ -77,30 +102,13 @@ module "private_dns_zones" { tags = local.base_tags } -module "user_defined_routes" { - count = var.deployment_options.enable_egress_lockdown ? 1 : 0 - - source = "../../../shared/terraform-modules/user-defined-routes" - - resource_group = azurerm_resource_group.spoke.name - location = var.location - route_table_name = "egress-lockdown" - global_settings = local.global_settings - - routes = [ - { - name = "defaultRoute" - address_prefix = "0.0.0.0/0" - next_hop_type = "VirtualAppliance" - next_hop_in_ip_address = data.terraform_remote_state.hub.outputs.firewall_private_ip - } - ] - - subnet_ids = module.network.subnet_ids - tags = local.base_tags +# TODO: Deprecate the random_integer unique_id logic +resource "random_integer" "unique_id" { + min = 1 + max = 9999 } - +## Deploy Azure Front Door with basic endpoint configuration for the web app module "frontdoor" { source = "../../../shared/terraform-modules/frontdoor" @@ -129,3 +137,27 @@ module "frontdoor" { module.app_service ] } + +## Deploy User Defined Routes (UDR) to route all traffic to the Azure Firewall (enabled via deployment option) +module "user_defined_routes" { + count = var.deployment_options.enable_egress_lockdown ? 1 : 0 + + source = "../../../shared/terraform-modules/user-defined-routes" + + resource_group = azurerm_resource_group.spoke.name + location = var.location + route_table_name = "egress-lockdown" + global_settings = local.global_settings + + routes = [ + { + name = "defaultRoute" + address_prefix = "0.0.0.0/0" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = data.terraform_remote_state.hub.outputs.firewall_private_ip + } + ] + + subnet_ids = module.network.subnet_ids + tags = local.base_tags +} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index 8c064392..0681e1de 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -1,3 +1,8 @@ +# spoke variables.tf + +##################################### +# Common variables for naming and tagging +##################################### variable "global_settings" { type = map(any) description = "[Optional] Global settings to configure each module with the appropriate naming standards." @@ -9,26 +14,6 @@ variable "owner" { description = "[Required] Owner of the deployment." } -variable "hub_state_resource_group_name" { - type = string - description = "The name of the resource group that holds the Terraform state for the hub" -} - -variable "hub_state_storage_account_name" { - type = string - description = "The name of the storage account that holds the Terraform state for the hub" -} - -variable "hub_state_container_name" { - type = string - description = "The name of the container that holds the Terraform state for the hub" -} - -variable "hub_state_key" { - type = string - description = "The key of the Terraform state for the hub" -} - variable "application_name" { type = string description = "The name of your application" @@ -132,20 +117,6 @@ variable "private_link_subnet_cidr" { default = ["10.240.11.0/24"] } -variable "hub_settings" { - type = object({ - rg_name = string - vnet_name = string - - firewall = object({ - private_ip = optional(string) - }) - }) - - description = "The settings for the hub virtual network." - - default = null -} variable "vm_admin_username" { type = string @@ -169,11 +140,29 @@ variable "vm_entra_admin_object_id" { description = "The Microsoft Entra object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." default = null } + +variable "sql_databases" { + type = list(object({ + name = string + sku_name = string + })) + + description = "The settings for the SQL databases." + + default = [ + { + name = "sample-db" + sku_name = "S0" + } + ] +} + variable "deployment_options" { type = object({ enable_waf = bool enable_egress_lockdown = bool enable_diagnostic_settings = bool + deploy_asev3 = bool deploy_bastion = bool deploy_redis = bool deploy_sql_database = bool @@ -188,6 +177,7 @@ variable "deployment_options" { enable_waf = true enable_egress_lockdown = true enable_diagnostic_settings = true + deploy_asev3 = false deploy_bastion = true deploy_redis = true deploy_sql_database = true diff --git a/scenarios/secure-baseline-multitenant/terraform/variables.tf b/scenarios/secure-baseline-multitenant/terraform/variables.tf new file mode 100644 index 00000000..c13f54a2 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/variables.tf @@ -0,0 +1,280 @@ +# terraform variables.tf + +##################################### +# Common variables for naming and tagging +##################################### +variable "global_settings" { + type = map(any) + description = "[Optional] Global settings to configure each module with the appropriate naming standards." + default = {} +} + +variable "owner" { + type = string + description = "[Required] Owner of the deployment." +} + +variable "application_name" { + type = string + description = "The name of your application" + default = "sec-baseline-1-spoke" +} + +variable "environment" { + type = string + description = "The environment (dev, qa, staging, prod)" + default = "dev" +} + +variable "location" { + type = string + description = "The Azure region where all resources in this example should be created" + default = "westus2" +} + +variable "tenant_id" { + type = string + description = "The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant." + default = null +} + +variable "tags" { + type = map(string) + description = "[Optional] Additional tags to assign to your resources" + default = {} +} + +##################################### +# Spoke Resource Configuration Variables +##################################### +variable "hub_remote_state_settings" { + type = object({ + resource_group_name = string + storage_account_name = string + container_name = string + key = string + }) + + description = "The settings for the hub remote state." + + default = null + + validation { + condition = var.hub_remote_state_settings != null ? (var.hub_remote_state_settings.resource_group_name != null && var.hub_remote_state_settings.storage_account_name != null && var.hub_remote_state_settings.container_name != null && var.hub_remote_state_settings.key != null) : true + error_message = "Either `hub_remote_state_settings` or `hub_vnet_settings` must be provided. If hub_remote_state_settings is not null, `resource_group_name`, `storage_account_name`, `container_name` and `key` attributes of the hub remote state must all be provided." + } +} + +variable "hub_vnet_settings" { + type = object({ + resource_group_name = string + name = string + + firewall = { + private_ip = optional(string) + } + }) + + description = "The settings for the hub virtual network." + default = null + + validation { + condition = var.hub_vnet_settings != null ? (var.hub_vnet_settings.rg_name != null && var.hub_vnet_settings.vnet_name != null) : true + error_message = "Either `hub_remote_state_settings` or hub_vnet_settings` must be provided. If hub_vnet_settings is not null, `rg_name` and `vnet_name` attributes of the hub vnet must both be provided." + } +} + +variable "entra_admin_group_object_id" { + type = string + description = "The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" + default = null +} + +variable "entra_admin_group_name" { + type = string + description = "The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" + default = null +} + +variable "spoke_vnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20" + default = ["10.240.0.0/20"] +} + +variable "devops_subnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16" + default = ["10.240.10.128/26"] +} + +variable "appsvc_subnet_cidr" { + type = list(string) + description = "The CIDR block for the subnet." + default = ["10.240.0.0/26"] +} + +variable "front_door_subnet_cidr" { + type = list(string) + description = "The CIDR block for the subnet." + default = ["10.240.0.64/26"] +} + + +variable "private_link_subnet_cidr" { + type = list(string) + description = "The CIDR block for the subnet." + default = ["10.240.11.0/24"] +} + + +variable "vm_admin_username" { + type = string + description = "The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." + default = null +} + +variable "vm_admin_password" { + type = string + description = "The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." + default = null +} + +variable "vm_entra_admin_username" { + type = string + description = "[Optional] The Azure AD username for the VM admin account. If vm_entra_admin_object_id is not specified, this value will be used." + default = null +} + +variable "vm_entra_admin_object_id" { + type = string + description = "The Azure AD object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." + default = null +} + +variable "sql_databases" { + type = list(object({ + name = string + sku_name = string + })) + + description = "The settings for the SQL databases." + + default = [ + { + name = "sample-db" + sku_name = "S0" + } + ] +} + +variable "deployment_options" { + type = object({ + enable_waf = bool + enable_egress_lockdown = bool + enable_diagnostic_settings = bool + deploy_bastion = bool + deploy_redis = bool + deploy_sql_database = bool + deploy_app_config = bool + deploy_vm = bool + deploy_openai = bool + }) + + description = "Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache." + + default = { + enable_waf = true + enable_egress_lockdown = true + enable_diagnostic_settings = true + deploy_bastion = true + deploy_redis = true + deploy_sql_database = true + deploy_app_config = true + deploy_vm = true + deploy_openai = true + } +} + +variable "appsvc_options" { + type = object({ + service_plan = object({ + os_type = string + sku_name = string + worker_count = optional(number) + zone_redundant = optional(bool) + }) + web_app = object({ + slots = list(string) + + application_stack = object({ + current_stack = string # required for windows + dotnet_version = optional(string) + docker_image = optional(string) # linux only + docker_image_tag = optional(string) # linux only + php_version = optional(string) + node_version = optional(string) + java_version = optional(string) + python = optional(bool) # windows only + python_version = optional(string) # linux only + java_server = optional(string) # linux only + java_server_version = optional(string) # linux only + go_version = optional(string) # linux only + ruby_version = optional(string) # linux only + }) + }) + }) + + description = "The options for the app service" + + default = { + service_plan = { + os_type = "Windows" + sku_name = "S1" + } + web_app = { + slots = [] + + application_stack = { + current_stack = "dotnet" + dotnet_version = "6.0" + } + } + } + + validation { + condition = contains(["Windows", "Linux"], var.appsvc_options.service_plan.os_type) + error_message = "Please, choose among one of the following operating systems: Windows or Linux." + } + + # validation { + # condition = contains(["S1", "S2", "S3", "P1v2", "P2v2", "P3v2"], var.appsvc_options.service_plan.sku_name) + # error_message = "Please, choose among one of the following SKUs for production workloads: S1, S2, S3, P1v2, P2v2 or P3v2." + # } + + validation { + condition = contains(["dotnet", "dotnetcore", "java", "php", "python", "node"], var.appsvc_options.web_app.application_stack.current_stack) + error_message = "Please, choose among one of the following stacks: dotnet, dotnetcore, java, php, python or node." + } +} + +variable "devops_settings" { + type = object({ + github_runner = optional(object({ + repository_url = string + token = string + })) + + devops_agent = optional(object({ + organization_url = string + token = string + })) + }) + + description = "The settings for the Azure DevOps agent or GitHub runner" + + default = { + github_runner = null + devops_agent = null + } +} \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/sql-database/README.md b/scenarios/shared/terraform-modules/sql-database/README.md index 15b594d7..5e82cdc3 100644 --- a/scenarios/shared/terraform-modules/sql-database/README.md +++ b/scenarios/shared/terraform-modules/sql-database/README.md @@ -9,8 +9,9 @@ No requirements. | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azuread](#provider\_azuread) | 2.47.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.27 | +| [azurerm](#provider\_azurerm) | 3.92.0 | ## Modules @@ -26,6 +27,7 @@ No modules. | [azurerm_mssql_server.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/mssql_server) | resource | | [azurerm_private_dns_a_record.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_a_record) | resource | | [azurerm_private_endpoint.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azuread_group.sql_admin_group](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | ## Inputs diff --git a/scenarios/shared/terraform-modules/sql-database/module.tf b/scenarios/shared/terraform-modules/sql-database/module.tf index c5d7ccf0..82199ccd 100644 --- a/scenarios/shared/terraform-modules/sql-database/module.tf +++ b/scenarios/shared/terraform-modules/sql-database/module.tf @@ -10,6 +10,11 @@ resource "azurecaf_name" "caf_name_sqlserver" { use_slug = var.global_settings.use_slug } +data "azuread_group" "sql_admin_group" { + display_name = var.entra_admin_group_name + object_id = var.entra_admin_group_object_id + security_enabled = true +} # Create the SQL Server resource "azurerm_mssql_server" "this" { diff --git a/scenarios/shared/terraform-modules/windows-vm/README.md b/scenarios/shared/terraform-modules/windows-vm/README.md index d9be0244..ca0beaf8 100644 --- a/scenarios/shared/terraform-modules/windows-vm/README.md +++ b/scenarios/shared/terraform-modules/windows-vm/README.md @@ -9,10 +9,10 @@ No requirements. | Name | Version | |------|---------| -| [azuread](#provider\_azuread) | 2.39.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | -| [random](#provider\_random) | 3.5.1 | +| [azuread](#provider\_azuread) | 2.47.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.27 | +| [azurerm](#provider\_azurerm) | 3.92.0 | +| [random](#provider\_random) | 3.6.0 | ## Modules From c1724600b64896518105ad2b121f7fee439c8c7e Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Thu, 2 May 2024 10:43:03 -0400 Subject: [PATCH 36/49] Functional deployment, further testing required --- .../terraform/README.md | 29 +++--- .../terraform/hub/README.md | 8 +- .../terraform/hub/outputs.tf | 14 ++- .../terraform/multitenant-asev3.tf | 62 +++++------- .../terraform/spoke/README.md | 30 +++--- .../terraform/spoke/data.tf | 12 --- .../terraform/spoke/{shared.tf => devops.tf} | 3 +- .../terraform/spoke/network.tf | 12 +-- .../terraform/spoke/variables.tf | 12 +-- .../terraform/variables.tf | 99 +++++++++---------- .../app-service/windows-web-app/README.md | 2 +- .../app-service/windows-web-app/module.tf | 2 +- .../app-service/windows-web-app/variables.tf | 2 +- .../private-endpoint/README.md | 2 +- .../private-endpoint/module.tf | 5 +- .../terraform-modules/sql-database/module.tf | 2 +- .../terraform-modules/windows-vm/variables.tf | 3 +- 17 files changed, 135 insertions(+), 164 deletions(-) delete mode 100644 scenarios/secure-baseline-multitenant/terraform/spoke/data.tf rename scenarios/secure-baseline-multitenant/terraform/spoke/{shared.tf => devops.tf} (94%) diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index 725f7ba9..432e4fea 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -297,27 +297,30 @@ If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra |------|-------------|------|---------|:--------:| | [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | | [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | -| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | -| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | -| [devops\_settings](#input\_devops\_settings) | The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | +| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | +| [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | +| [deployment\_options](#input\_deployment\_options) | [Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | +| [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | | [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | -| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | -| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | [Required] The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | [Required] The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | | [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | -| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | +| [firewall\_subnet\_cidr](#input\_firewall\_subnet\_cidr) | [Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26 | `list(string)` |
[
"10.242.0.0/26"
]
| no | +| [firewall\_subnet\_name](#input\_firewall\_subnet\_name) | [Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet' | `string` | `"AzureFirewallSubnet"` | no | +| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | | [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | -| [hub\_remote\_state\_settings](#input\_hub\_remote\_state\_settings) | The settings for the hub remote state. |
object({
resource_group_name = string
storage_account_name = string
container_name = string
key = string
})
| `null` | no | -| [hub\_vnet\_settings](#input\_hub\_vnet\_settings) | The settings for the hub virtual network. |
object({
resource_group_name = string
name = string

firewall = {
private_ip = optional(string)
}
})
| `null` | no | +| [hub\_vnet\_cidr](#input\_hub\_vnet\_cidr) | [Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20 | `list(string)` |
[
"10.242.0.0/20"
]
| no | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | -| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | +| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | -| [sql\_databases](#input\_sql\_databases) | The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | +| [sql\_databases](#input\_sql\_databases) | [Optional] The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | | [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | | [tenant\_id](#input\_tenant\_id) | The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | -| [vm\_admin\_password](#input\_vm\_admin\_password) | The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | -| [vm\_admin\_username](#input\_vm\_admin\_username) | The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | -| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_admin\_password](#input\_vm\_admin\_password) | [Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | | [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Azure AD username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | ## Outputs diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/README.md b/scenarios/secure-baseline-multitenant/terraform/hub/README.md index 5745f2a8..b44d332c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/hub/README.md @@ -54,10 +54,10 @@ | Name | Description | |------|-------------| -| [bastion\_name](#output\_bastion\_name) | n/a | | [firewall\_private\_ip](#output\_firewall\_private\_ip) | n/a | | [firewall\_rules](#output\_firewall\_rules) | n/a | -| [rg\_name](#output\_rg\_name) | n/a | -| [vnet\_id](#output\_vnet\_id) | n/a | -| [vnet\_name](#output\_vnet\_name) | n/a | +| [resource\_group\_name](#output\_resource\_group\_name) | n/a | +| [virtual\_network](#output\_virtual\_network) | n/a | +| [virtual\_network\_id](#output\_virtual\_network\_id) | n/a | +| [virtual\_network\_name](#output\_virtual\_network\_name) | n/a | diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf b/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf index 70e6037c..210a3fa4 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf @@ -1,19 +1,23 @@ -output "rg_name" { +output "resource_group_name" { value = azurerm_resource_group.hub.name } -output "vnet_name" { +output "virtual_network_name" { value = module.network.vnet_name } -output "vnet_id" { +output "virtual_network_id" { value = module.network.vnet_id } -output "bastion_name" { - value = var.deployment_options.deploy_bastion ? module.bastion[0].name : null +output "virtual_network" { + value = module.network.vnet } +# output "bastion_name" { +# value = var.deployment_options.deploy_bastion ? module.bastion[0].name : null +# } + output "firewall_private_ip" { # the 0 index for the module is needed as the module is a count value = var.deployment_options.enable_egress_lockdown ? module.firewall[0].private_ip_address : null diff --git a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf index d4935985..76706dab 100644 --- a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf +++ b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf @@ -8,6 +8,16 @@ module "hub" { location = var.location owner = var.owner + # Optional Network Config Variables + hub_vnet_cidr = var.hub_vnet_cidr + spoke_vnet_cidr = var.spoke_vnet_cidr + firewall_subnet_name = var.firewall_subnet_name + firewall_subnet_cidr = var.firewall_subnet_cidr + bastion_subnet_name = var.bastion_subnet_name + bastion_subnet_cidr = var.bastion_subnet_cidr + devops_subnet_cidr = var.devops_subnet_cidr + + # Optional Deployment Variables deployment_options = var.deployment_options global_settings = var.global_settings tags = var.tags @@ -22,53 +32,27 @@ module "spoke" { owner = var.owner tenant_id = var.tenant_id - hub_vnet_settings = { - resource_group_name = module.hub.rg_name - name = module.hub.vnet_name - - firewall = { - private_ip = module.hub.firewall_private_ip - } - } - - ## entra_admin_group_name = var.entra_admin_group_name entra_admin_group_object_id = var.entra_admin_group_object_id + appsvc_options = var.appsvc_options - vm_entra_admin_object_id = var.vm_entra_admin_object_id - -} - -# Create Spoke via module -module "spoke" { - source = "./spoke" - - application_name = var.application_name - environment = var.environment - location = var.location - owner = var.owner - tenant_id = var.tenant_id - + # Spoke Network Configuration Variables + hub_virtual_network = module.hub.virtual_network + firewall_private_ip = module.hub.firewall_private_ip + firewall_rules = module.hub.firewall_rules spoke_vnet_cidr = var.spoke_vnet_cidr devops_subnet_cidr = var.devops_subnet_cidr appsvc_subnet_cidr = var.appsvc_subnet_cidr front_door_subnet_cidr = var.front_door_subnet_cidr private_link_subnet_cidr = var.private_link_subnet_cidr - vm_admin_password = var.vm_admin_password - vm_admin_username = var.vm_admin_username - vm_entra_admin_username = var.vm_entra_admin_username - - deployment_options = var.deployment_options - - global_settings = var.global_settings - tags = var.tags - - entra_admin_group_name = var.entra_admin_group_name - entra_admin_group_object_id = var.entra_admin_group_object_id + # Optional Self-hosted Agent Config Variables + vm_admin_username = "jinlelocal" + # vm_admin_username = var.vm_admin_password + # vm_admin_password = var.vm_admin_password + vm_entra_admin_username = var.vm_entra_admin_username vm_entra_admin_object_id = var.vm_entra_admin_object_id - depends_on = [ - module.hub - ] -} \ No newline at end of file + # Spoke Resource Configuration Variables + sql_databases = var.sql_databases +} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md index fb166109..00a2923d 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md @@ -16,7 +16,6 @@ | [azurecaf](#provider\_azurecaf) | 1.2.26 | | [azurerm](#provider\_azurerm) | 3.85.0 | | [random](#provider\_random) | 3.6.0 | -| [terraform](#provider\_terraform) | n/a | ## Modules @@ -51,36 +50,35 @@ | [azurerm_user_assigned_identity.contributor](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | | [azurerm_user_assigned_identity.reader](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | | [random_integer.unique_id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) | resource | -| [azurerm_virtual_network.hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/virtual_network) | data source | -| [terraform_remote_state.hub](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | -| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | -| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [appsvc\_options](#input\_appsvc\_options) | [Optional] The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | +| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | | [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_asev3 = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_asev3": false,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | -| [devops\_settings](#input\_devops\_settings) | The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | +| [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | | [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | -| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | -| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | [Required] The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | [Required] The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | | [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | -| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | +| [firewall\_private\_ip](#input\_firewall\_private\_ip) | n/a | `string` | n/a | yes | +| [firewall\_rules](#input\_firewall\_rules) | n/a | `any` | n/a | yes | +| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | | [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | -| [hub\_remote\_state\_settings](#input\_hub\_remote\_state\_settings) | The settings for the hub remote state. |
object({
resource_group_name = string
storage_account_name = string
container_name = string
key = string
})
| `null` | no | -| [hub\_vnet\_settings](#input\_hub\_vnet\_settings) | The settings for the hub virtual network. |
object({
resource_group_name = string
name = string

firewall = optional(object({
private_ip = optional(string)
})
)
})
| `null` | no | +| [hub\_virtual\_network](#input\_hub\_virtual\_network) | [Required] Hub virtual network object that is live in Azure. Use either a data block or output of the `Hub` module (virtual\_network) to provide this value | `any` | n/a | yes | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | -| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | +| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | -| [sql\_databases](#input\_sql\_databases) | The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | +| [sql\_databases](#input\_sql\_databases) | [Optional] The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | | [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | | [tenant\_id](#input\_tenant\_id) | The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | -| [vm\_admin\_password](#input\_vm\_admin\_password) | The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | -| [vm\_admin\_username](#input\_vm\_admin\_username) | The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | -| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_admin\_password](#input\_vm\_admin\_password) | [Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | | [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Azure AD username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | ## Outputs diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf deleted file mode 100644 index 7c4c79a8..00000000 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf +++ /dev/null @@ -1,12 +0,0 @@ -# Lookup the Hub VNet by either the `hub_remote_state_settings` or the `hub_vnet_settings` variables -# If both are provided, hub_vnet_settings will be used. -data "terraform_remote_state" "hub" { - count = var.hub_vnet_settings == null ? 1 : 0 - backend = "azurerm" - config = var.hub_remote_state_settings -} - -data "azurerm_virtual_network" "hub" { - name = var.hub_vnet_settings == null ? data.terraform_remote_state.hub[0].outputs.vnet_name : var.hub_vnet_settings.name - resource_group_name = var.hub_vnet_settings == null ? data.terraform_remote_state.hub[0].outputs.rg_name : var.hub_vnet_settings.resource_group_name -} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/devops.tf similarity index 94% rename from scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf rename to scenarios/secure-baseline-multitenant/terraform/spoke/devops.tf index 875fe2ec..4f1c794d 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/devops.tf @@ -22,8 +22,7 @@ module "devops_vm" { entra_admin_username = var.vm_entra_admin_username entra_admin_object_id = var.vm_entra_admin_object_id global_settings = local.global_settings - - tags = local.base_tags + tags = local.base_tags identity = { type = "UserAssigned" diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf index 9e0554f4..bd939fa6 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf @@ -48,9 +48,9 @@ module "network" { name = var.application_name vnet_cidr = var.spoke_vnet_cidr peering_vnet = { - id = data.azurerm_virtual_network.hub.id - name = data.azurerm_virtual_network.hub.name - resource_group = data.azurerm_virtual_network.hub.resource_group_name + id = var.hub_virtual_network.id + name = var.hub_virtual_network.name + resource_group = var.hub_virtual_network.resource_group_name } subnets = [ @@ -90,13 +90,13 @@ module "private_dns_zones" { source = "../../../shared/terraform-modules/private-dns-zone" count = length(local.private_dns_zones) - resource_group = data.terraform_remote_state.hub.outputs.rg_name + resource_group = var.hub_virtual_network.resource_group_name global_settings = local.global_settings dns_zone_name = local.private_dns_zones[count.index].name dns_records = lookup(local.private_dns_zones[count.index], "records", []) vnet_links = [ - data.azurerm_virtual_network.hub.id + var.hub_virtual_network.id ] tags = local.base_tags @@ -154,7 +154,7 @@ module "user_defined_routes" { name = "defaultRoute" address_prefix = "0.0.0.0/0" next_hop_type = "VirtualAppliance" - next_hop_in_ip_address = data.terraform_remote_state.hub.outputs.firewall_private_ip + next_hop_in_ip_address = var.firewall_private_ip } ] diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index 0681e1de..9b2c373d 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -100,20 +100,20 @@ variable "devops_subnet_cidr" { variable "appsvc_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.0.0/26"] } variable "front_door_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.0.64/26"] } variable "private_link_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.11.0/24"] } @@ -147,7 +147,7 @@ variable "sql_databases" { sku_name = string })) - description = "The settings for the SQL databases." + description = "[Optional] The settings for the SQL databases." default = [ { @@ -216,7 +216,7 @@ variable "appsvc_options" { }) }) - description = "The options for the app service" + description = "[Optional] The options for the app service" default = { service_plan = { @@ -262,7 +262,7 @@ variable "devops_settings" { })) }) - description = "The settings for the Azure DevOps agent or GitHub runner" + description = "[Optional] The settings for the Azure DevOps agent or GitHub runner" default = { github_runner = null diff --git a/scenarios/secure-baseline-multitenant/terraform/variables.tf b/scenarios/secure-baseline-multitenant/terraform/variables.tf index c13f54a2..ee046804 100644 --- a/scenarios/secure-baseline-multitenant/terraform/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/variables.tf @@ -45,55 +45,35 @@ variable "tags" { } ##################################### -# Spoke Resource Configuration Variables +# Hub Network Configuration Variables ##################################### -variable "hub_remote_state_settings" { - type = object({ - resource_group_name = string - storage_account_name = string - container_name = string - key = string - }) - - description = "The settings for the hub remote state." - - default = null - - validation { - condition = var.hub_remote_state_settings != null ? (var.hub_remote_state_settings.resource_group_name != null && var.hub_remote_state_settings.storage_account_name != null && var.hub_remote_state_settings.container_name != null && var.hub_remote_state_settings.key != null) : true - error_message = "Either `hub_remote_state_settings` or `hub_vnet_settings` must be provided. If hub_remote_state_settings is not null, `resource_group_name`, `storage_account_name`, `container_name` and `key` attributes of the hub remote state must all be provided." - } +variable "bastion_subnet_name" { + type = string + description = "[Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet'" + default = "AzureBastionSubnet" } -variable "hub_vnet_settings" { - type = object({ - resource_group_name = string - name = string - - firewall = { - private_ip = optional(string) - } - }) - - description = "The settings for the hub virtual network." - default = null - - validation { - condition = var.hub_vnet_settings != null ? (var.hub_vnet_settings.rg_name != null && var.hub_vnet_settings.vnet_name != null) : true - error_message = "Either `hub_remote_state_settings` or hub_vnet_settings` must be provided. If hub_vnet_settings is not null, `rg_name` and `vnet_name` attributes of the hub vnet must both be provided." - } +variable "firewall_subnet_name" { + type = string + description = "[Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet'" + default = "AzureFirewallSubnet" +} +variable "hub_vnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20" + default = ["10.242.0.0/20"] } -variable "entra_admin_group_object_id" { - type = string - description = "The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" - default = null +variable "firewall_subnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26" + default = ["10.242.0.0/26"] } -variable "entra_admin_group_name" { - type = string - description = "The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" - default = null +variable "bastion_subnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26" + default = ["10.242.0.64/26"] } variable "spoke_vnet_cidr" { @@ -108,35 +88,50 @@ variable "devops_subnet_cidr" { default = ["10.240.10.128/26"] } +##################################### +# Spoke Resource Configuration Variables +##################################### +variable "entra_admin_group_object_id" { + type = string + description = "[Required] The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" + default = null +} + +variable "entra_admin_group_name" { + type = string + description = "[Required] The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" + default = null +} + + variable "appsvc_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.0.0/26"] } variable "front_door_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.0.64/26"] } - variable "private_link_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.11.0/24"] } variable "vm_admin_username" { type = string - description = "The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." + description = "[Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." default = null } variable "vm_admin_password" { type = string - description = "The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." + description = "[Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." default = null } @@ -148,7 +143,7 @@ variable "vm_entra_admin_username" { variable "vm_entra_admin_object_id" { type = string - description = "The Azure AD object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." + description = "[Optional] The Azure AD object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." default = null } @@ -158,7 +153,7 @@ variable "sql_databases" { sku_name = string })) - description = "The settings for the SQL databases." + description = "[Optional] The settings for the SQL databases." default = [ { @@ -181,7 +176,7 @@ variable "deployment_options" { deploy_openai = bool }) - description = "Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache." + description = "[Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache." default = { enable_waf = true @@ -271,7 +266,7 @@ variable "devops_settings" { })) }) - description = "The settings for the Azure DevOps agent or GitHub runner" + description = "[Optional] The settings for the Azure DevOps agent or GitHub runner" default = { github_runner = null diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md index dab6e024..96077964 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md @@ -48,7 +48,7 @@ No requirements. | [service\_plan\_resource](#input\_service\_plan\_resource) | The service plan resource where the web application will be created | `any` | n/a | yes | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | | [web\_app\_name](#input\_web\_app\_name) | The name of the web application | `string` | n/a | yes | -| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | +| [webapp\_options](#input\_webapp\_options) | [Required] The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | ## Outputs diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf index ebf704f2..fd0c4d67 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf @@ -163,7 +163,7 @@ module "private_endpoint_slot" { subnet_id = var.frontend_subnet_id private_connection_resource_id = azurerm_windows_web_app.this.id - subresource_names = ["sites-${var.webapp_options.slots[0]}"] + subresource_names = length(var.webapp_options.slots) < 1 ? null : ["sites-${var.webapp_options.slots[0]}"] private_dns_zone = var.private_dns_zone diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf index 2fd517b5..a2c9b1f2 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf @@ -112,7 +112,7 @@ variable "webapp_options" { }) }) - description = "The options for the app service" + description = "[Required] The options for the app service" validation { condition = contains(["dotnet", "dotnetcore", "java", "php", "python", "node"], var.webapp_options.application_stack.current_stack) diff --git a/scenarios/shared/terraform-modules/private-endpoint/README.md b/scenarios/shared/terraform-modules/private-endpoint/README.md index 90dcab06..1abf7091 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/README.md +++ b/scenarios/shared/terraform-modules/private-endpoint/README.md @@ -9,7 +9,7 @@ No requirements. | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurerm](#provider\_azurerm) | 3.101.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/private-endpoint/module.tf b/scenarios/shared/terraform-modules/private-endpoint/module.tf index ef153253..31eb95e8 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/module.tf +++ b/scenarios/shared/terraform-modules/private-endpoint/module.tf @@ -8,8 +8,9 @@ resource "azurerm_private_endpoint" "this" { private_service_connection { name = var.name private_connection_resource_id = var.private_connection_resource_id - subresource_names = var.subresource_names - is_manual_connection = false + + subresource_names = length(var.subresource_names) == 0 ? null : var.subresource_names + is_manual_connection = false } tags = local.tags diff --git a/scenarios/shared/terraform-modules/sql-database/module.tf b/scenarios/shared/terraform-modules/sql-database/module.tf index 82199ccd..65a64c86 100644 --- a/scenarios/shared/terraform-modules/sql-database/module.tf +++ b/scenarios/shared/terraform-modules/sql-database/module.tf @@ -11,7 +11,7 @@ resource "azurecaf_name" "caf_name_sqlserver" { } data "azuread_group" "sql_admin_group" { - display_name = var.entra_admin_group_name + display_name = var.entra_admin_group_object_id == null ? var.entra_admin_group_name : null object_id = var.entra_admin_group_object_id security_enabled = true } diff --git a/scenarios/shared/terraform-modules/windows-vm/variables.tf b/scenarios/shared/terraform-modules/windows-vm/variables.tf index 213bd502..b1e645c1 100644 --- a/scenarios/shared/terraform-modules/windows-vm/variables.tf +++ b/scenarios/shared/terraform-modules/windows-vm/variables.tf @@ -38,8 +38,7 @@ variable "key_vault_id" { } variable "admin_username" { - type = string - default = null + type = string } variable "admin_password" { From 7b99f006100ed84c6acf1dbf34f2b36ff2dbbf2a Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Sat, 4 May 2024 13:42:14 -0500 Subject: [PATCH 37/49] Spoke deployment fixes/cleanup --- .../terraform/README.md | 3 +- .../terraform/multitenant-asev3.tf | 9 ++++-- .../terraform/outputs.tf | 0 .../terraform/spoke/asev3.tf | 10 +++--- .../terraform/spoke/network.tf | 3 +- .../terraform/variables.tf | 32 +++++++++++-------- 6 files changed, 32 insertions(+), 25 deletions(-) create mode 100644 scenarios/secure-baseline-multitenant/terraform/outputs.tf diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index 432e4fea..d7cf524f 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -300,7 +300,7 @@ If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra | [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | | [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | | [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | -| [deployment\_options](#input\_deployment\_options) | [Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | +| [deployment\_options](#input\_deployment\_options) | [Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
deploy_asev3 = bool
})
|
{
"deploy_app_config": false,
"deploy_asev3": false,
"deploy_bastion": false,
"deploy_openai": false,
"deploy_redis": false,
"deploy_sql_database": false,
"deploy_vm": false,
"enable_diagnostic_settings": false,
"enable_egress_lockdown": false,
"enable_waf": false
}
| no | | [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | | [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | | [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | [Required] The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | @@ -318,7 +318,6 @@ If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra | [sql\_databases](#input\_sql\_databases) | [Optional] The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | | [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | | [tenant\_id](#input\_tenant\_id) | The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | -| [vm\_admin\_password](#input\_vm\_admin\_password) | [Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | | [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | | [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | | [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Azure AD username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | diff --git a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf index 76706dab..30929537 100644 --- a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf +++ b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf @@ -47,12 +47,15 @@ module "spoke" { private_link_subnet_cidr = var.private_link_subnet_cidr # Optional Self-hosted Agent Config Variables - vm_admin_username = "jinlelocal" - # vm_admin_username = var.vm_admin_password - # vm_admin_password = var.vm_admin_password + vm_admin_username = var.vm_admin_username vm_entra_admin_username = var.vm_entra_admin_username vm_entra_admin_object_id = var.vm_entra_admin_object_id # Spoke Resource Configuration Variables sql_databases = var.sql_databases + + # Optional Deployment Variables + deployment_options = var.deployment_options + global_settings = var.global_settings + tags = var.tags } diff --git a/scenarios/secure-baseline-multitenant/terraform/outputs.tf b/scenarios/secure-baseline-multitenant/terraform/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf index aecfd149..90cc621f 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf @@ -4,13 +4,13 @@ resource "azurecaf_name" "caf_name_asev3" { name = var.application_name resource_type = "azurerm_app_service_environment" - prefixes = var.global_settings.prefixes - suffixes = var.global_settings.suffixes - random_length = var.global_settings.random_length + prefixes = local.global_settings.prefixes + suffixes = local.global_settings.suffixes + random_length = local.global_settings.random_length clean_input = true - passthrough = var.global_settings.passthrough + passthrough = local.global_settings.passthrough - use_slug = var.global_settings.use_slug + use_slug = local.global_settings.use_slug } resource "azurerm_app_service_environment_v3" "this" { diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf index bd939fa6..975dc870 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf @@ -14,7 +14,8 @@ resource "azurecaf_name" "caf_name_spoke_rg" { name = var.application_name resource_type = "azurerm_resource_group" - prefixes = concat(["spoke"], local.global_settings.prefixes) + # prefixes = concat(["spoke"], local.global_settings.prefixes) + prefixes = local.global_settings.prefixes random_length = local.global_settings.random_length clean_input = true passthrough = local.global_settings.passthrough diff --git a/scenarios/secure-baseline-multitenant/terraform/variables.tf b/scenarios/secure-baseline-multitenant/terraform/variables.tf index ee046804..7484d3cb 100644 --- a/scenarios/secure-baseline-multitenant/terraform/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/variables.tf @@ -129,11 +129,13 @@ variable "vm_admin_username" { default = null } -variable "vm_admin_password" { - type = string - description = "[Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." - default = null -} +## Shouldn't be passing around passwords or have it captured in source control +## Use Hashicorp Vault or KeyVault instead. +# variable "vm_admin_password" { +# type = string +# description = "[Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." +# default = null +# } variable "vm_entra_admin_username" { type = string @@ -174,20 +176,22 @@ variable "deployment_options" { deploy_app_config = bool deploy_vm = bool deploy_openai = bool + deploy_asev3 = bool }) description = "[Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache." default = { - enable_waf = true - enable_egress_lockdown = true - enable_diagnostic_settings = true - deploy_bastion = true - deploy_redis = true - deploy_sql_database = true - deploy_app_config = true - deploy_vm = true - deploy_openai = true + enable_waf = false + enable_egress_lockdown = false + enable_diagnostic_settings = false + deploy_bastion = false + deploy_redis = false + deploy_sql_database = false + deploy_app_config = false + deploy_vm = false + deploy_openai = false + deploy_asev3 = false } } From ba24d5925f0e7be8043679b17d8f27036039579b Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Sat, 4 May 2024 13:47:10 -0500 Subject: [PATCH 38/49] added backend.hcl file --- .../terraform/backend.hcl.template | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 scenarios/secure-baseline-multitenant/terraform/backend.hcl.template diff --git a/scenarios/secure-baseline-multitenant/terraform/backend.hcl.template b/scenarios/secure-baseline-multitenant/terraform/backend.hcl.template new file mode 100644 index 00000000..62dc2d20 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/backend.hcl.template @@ -0,0 +1,10 @@ +### To use this template, rename the file to `backend.tf` +### and update the values below to match your remote state config +### +### To use this file as part of your deployment, provide the following flag as you initialize: +### terraform init -backend-config=backend.tf + +resource_group_name = "my-tf-remote-state-rg-name" +storage_account_name = "my-tf-remote-state-sa-name" +container_name = "my-tf-remote-state-container-name" +key = "my-state-file-name.tfstate" \ No newline at end of file From 021ec673c3b00d8046c9e71fbf00001893d1812b Mon Sep 17 00:00:00 2001 From: ahmeds Date: Tue, 13 Jul 2021 10:57:04 +0200 Subject: [PATCH 39/49] shared resource group --- deployment/bicep/main.bicep | 82 ++++++++----------- deployment/bicep/parameters.json | 4 +- deployment/bicep/shared/azmon.bicep | 40 +++++++++ deployment/bicep/shared/createvmwindows.bicep | 26 +++--- deployment/bicep/shared/shared.bicep | 46 ++--------- deployment/bicep/shared/vm-nic.bicep | 2 +- .../bicep/vnettest/vnetWithBastian.bicep | 73 +++++++++++++++++ .../bicep/vnettest/vnetWithOutBastian.bicep | 37 +++++++++ 8 files changed, 207 insertions(+), 103 deletions(-) create mode 100644 deployment/bicep/shared/azmon.bicep create mode 100644 deployment/bicep/vnettest/vnetWithBastian.bicep create mode 100644 deployment/bicep/vnettest/vnetWithOutBastian.bicep diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep index 4eccdf32..72501004 100644 --- a/deployment/bicep/main.bicep +++ b/deployment/bicep/main.bicep @@ -1,7 +1,7 @@ targetScope='subscription' param workloadName string -var location = deployment().location -@description('The environment for which the deployment is being executed') +param location string = deployment().location +@description('The-- environment for which the deployment is being executed') @allowed([ 'dev' 'uat' @@ -13,7 +13,7 @@ param environment string // parameters for azure devops agent param vmazdevopsUsername string param vmazdevopsPassword string -param azureDevOpsAccount string +param vstsAccount string param personalAccessToken string // Variables @@ -24,24 +24,15 @@ var sharedResourceGroupName = 'rg-shared-${resourceSuffix}' var aseResourceGroupName = 'rg-ase-${resourceSuffix}' // Create resources name using these objects and pass it as a params in module var sharedResourceGroupResources = { - 'appInsightsName':'appi-${resourceSuffix}' - 'logAnalyticsWorkspaceName': 'log-${resourceSuffix}' - 'environmentName': environment - 'resourceSuffix' : resourceSuffix - 'vmSuffix' : vmSuffix - 'keyVaultName':'kv-${workloadName}-${environment}' // Must be between 3-24 alphanumeric characters + 'appInsightsName':'appin-${resourceSuffix}' + 'logAnalyticsWorkspaceName': 'logananalyticsws-${resourceSuffix}' + 'environmentName': environment + 'resourceSuffix' : resourceSuffix + 'vmSuffix' : vmSuffix } -resource networkingRG 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: networkingResourceGroupName - location: location -} - - - - resource aseResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: aseResourceGroupName location: location @@ -49,8 +40,17 @@ resource aseResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +// shared resource group + +// for testing -- need a subnet +var NetworkResourceGroupName = 'rg-network-${resourceSuffix}' + +resource networkRg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: NetworkResourceGroupName + location: location +} module vnet_generic './vnettest/vnetWithOutBastian.bicep' = { name: 'vnet' @@ -71,48 +71,30 @@ resource sharedRG 'Microsoft.Resources/resourceGroups@2021-04-01' = { } - -module networking 'networking.bicep' = { - name: 'networkingresources' - scope: resourceGroup(networkingRG.name) - params: { - workloadName: workloadName - environment: environment - } -} - -var jumpboxSubnetId= networking.outputs.jumpBoxSubnetId -var agentSubnetId=networking.outputs.devOpsSubnetId -module shared './shared/shared.bicep' = { dependsOn: [ - networking - ] +module shared './shared/shared.bicep' = { name: 'sharedresources' scope: resourceGroup(sharedRG.name) params: { location: location sharedResourceGroupResources : sharedResourceGroupResources - jumpboxSubnetId: jumpboxSubnetId - agentSubnetId: agentSubnetId + subnetId: subnetId vmazdevopsPassword:vmazdevopsPassword vmazdevopsUsername: vmazdevopsUsername personalAccessToken: personalAccessToken - azureDevOpsAccount: azureDevOpsAccount + vstsAccount: vstsAccount resourceGroupName: sharedRG.name } } -// module ase 'ase.bicep' = { -// dependsOn: [ -// networking -// shared -// ] -// scope: resourceGroup(aseResourceGroup.name) -// name: 'aseresources' -// params: { -// location: location -// workloadName: workloadName -// environment: environment -// aseSubnetName: networking.outputs.aseSubnetName -// aseSubnetId: networking.outputs.aseSubnetid -// } -// } +module ase 'ase.bicep' = { + dependsOn: [ + shared + ] + scope: resourceGroup(aseResourceGroup.name) + name: 'aseresources' + params: { + location: location + workloadName: workloadName + environment: environment + } +} diff --git a/deployment/bicep/parameters.json b/deployment/bicep/parameters.json index ea5c69d4..265d106e 100644 --- a/deployment/bicep/parameters.json +++ b/deployment/bicep/parameters.json @@ -8,14 +8,14 @@ "vmazdevopsPassword": { "value": "$1test_password" }, - "azureDevOpsAccount": { + "vstsAccount": { "value": "https://dev.azure.com/ahmeds" }, "personalAccessToken": { "value": "lbjxhqqxyovi5g6psgvcmncrlnv72o55fcfsqhwg33xaaurapykq" }, "workloadName" :{ - "value": "ahmsWL" + "value": "demoWL" }, "environment" :{ "value": "dev" diff --git a/deployment/bicep/shared/azmon.bicep b/deployment/bicep/shared/azmon.bicep new file mode 100644 index 00000000..901ee0f5 --- /dev/null +++ b/deployment/bicep/shared/azmon.bicep @@ -0,0 +1,40 @@ +targetScope='resourceGroup' +param location string =resourceGroup().location +param sharedResourceGroupResources object + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-03-01-preview' = { + name: sharedResourceGroupResources.logAnalyticsWorkspaceName + location: location + tags: { + Environment: sharedResourceGroupResources.environmentName + + } + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + + +resource appInsights 'microsoft.insights/components@2020-02-02-preview' = { + name: sharedResourceGroupResources.appInsightsName + location: location + kind: 'string' + tags: { + displayName: 'AppInsight' + Environment: sharedResourceGroupResources.environmentName + + } + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + +output appInsightsConnectionString string = appInsights.properties.ConnectionString + diff --git a/deployment/bicep/shared/createvmwindows.bicep b/deployment/bicep/shared/createvmwindows.bicep index f5dfb0e1..9892abc4 100644 --- a/deployment/bicep/shared/createvmwindows.bicep +++ b/deployment/bicep/shared/createvmwindows.bicep @@ -1,4 +1,4 @@ - +param namePrefix string = 'unique' param location string = resourceGroup().location param subnetId string param osDiskType string = 'Standard_LRS' @@ -6,33 +6,39 @@ param vmSize string = 'Standard_D4_v3' param username string param password string param windowsOSVersion string = '2016-Datacenter' +//var vmName = '${namePrefix}${uniqueString(resourceGroup().id)}' param vmName string param deployAgent bool=false -@description('The Azure DevOps account name]') -param azureDevOpsAccount string='' +@description('The Visual Studio Team Services account name, that is, the first part of your VSTSAccount.visualstudio.com') +param vstsAccount string='' -@description('The personal access token to connect to Azure DevOps') +@description('The personal access token to connect to VSTS') @secure() param personalAccessToken string='' -@description('The Azure DevOps build agent pool for this build agent to join. Use \'Default\' if you don\'t have a separate pool.') +@description('The Visual Studio Team Services build agent pool for this build agent to join. Use \'Default\' if you don\'t have a separate pool.') param poolName string = 'Default' @description('Enable autologon to run the build agent in interactive mode that can sustain machine reboots.
Set this to true if the agents will be used to run UI tests.') param enableAutologon bool = false - @description('The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated.') -param artifactsLocation string = 'https://raw.githubusercontent.com/ahmedsza/azdevopsagent/main/setupagent.ps1' +param artifactsLocation string = 'https://raw.githubusercontent.com/ahmedsza/azdevopsagent/main/' +@description('The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated.') +@secure() +param artifactsLocationSasToken string = '' +var vstsAgentName = 'agent-${vmName}' +//var vstsParameters = '-vstsAccount ${vstsAccount} -personalAccessToken ${personalAccessToken} -AgentName ${vstsAgentName} -PoolName ${poolName} -runAsAutoLogon ${enableAutologon} -vmAdminUserName ${username} -vmAdminPassword ${password}' +var vstsParameters = '-url ${vstsAccount} -pat ${personalAccessToken} -agent ${vstsAgentName} -pool ${poolName}' -var azureDevOpsAgentName = 'agent-${vmName}' // Bring in the nic module nic './vm-nic.bicep' = { name: '${vmName}-nic' params: { + namePrefix: '${vmName}-hdd' subnetId: subnetId vmName: vmName } @@ -88,9 +94,9 @@ resource vm_CustomScript 'Microsoft.Compute/virtualMachines/extensions@2018-06-0 typeHandlerVersion: '1.10' settings: { fileUris: [ - artifactsLocation + 'https://raw.githubusercontent.com/ahmedsza/azdevopsagent/main/setupagent.ps1' ] - commandToExecute: 'powershell.exe -ExecutionPolicy Unrestricted -Command ./setupagent.ps1 -url ${azureDevOpsAccount} -pat ${personalAccessToken} -agent ${azureDevOpsAgentName} -pool ${poolName} -runAsAutoLogon ${enableAutologon} -vmAdminUserName ${username} -vmAdminPassword ${password}' + commandToExecute: 'powershell.exe -ExecutionPolicy Unrestricted -Command ./setupagent.ps1 -url ${vstsAccount} -pat ${personalAccessToken} -agent ${vstsAgentName} -pool ${poolName}' } } } diff --git a/deployment/bicep/shared/shared.bicep b/deployment/bicep/shared/shared.bicep index 293fc3b8..17aebdba 100644 --- a/deployment/bicep/shared/shared.bicep +++ b/deployment/bicep/shared/shared.bicep @@ -2,11 +2,10 @@ targetScope='resourceGroup' param location string param sharedResourceGroupResources object -param jumpboxSubnetId string -param agentSubnetId string +param subnetId string param vmazdevopsUsername string param vmazdevopsPassword string -param azureDevOpsAccount string +param vstsAccount string param personalAccessToken string @@ -33,13 +32,13 @@ module vm_devopswinvm './createvmwindows.bicep' = { name: 'azdevopsvm' scope: resourceGroup(resourceGroupName) params: { - subnetId: agentSubnetId + subnetId: subnetId username: vmazdevopsUsername password: vmazdevopsPassword vmName: 'azdevops-${sharedResourceGroupResources.vmSuffix}' - azureDevOpsAccount: azureDevOpsAccount + vstsAccount: vstsAccount personalAccessToken: personalAccessToken - deployAgent: true + deployAgent: false } } @@ -47,45 +46,12 @@ module vm_jumpboxwinvm './createvmwindows.bicep' = { name: 'jumpboxwinvm' scope: resourceGroup(resourceGroupName) params: { - subnetId: jumpboxSubnetId + subnetId: subnetId username: vmazdevopsUsername password: vmazdevopsPassword vmName: 'jumpbox-${sharedResourceGroupResources.vmSuffix}' } } -resource key_vault 'Microsoft.KeyVault/vaults@2019-09-01' = { - name: sharedResourceGroupResources.keyVaultName - location: location - properties: { - tenantId: subscription().tenantId - sku: { - family: 'A' - name: 'standard' - } - accessPolicies: [ - // { - // tenantId: 'string' - // objectId: 'string' - // applicationId: 'string' - // permissions: { - // keys: [ - // 'string' - // ] - // secrets: [ - // 'string' - // ] - // certificates: [ - // 'string' - // ] - // storage: [ - // 'string' - // ] - // } - // } - ] - } -} - output devopsAgentvmName string = vm_devopswinvm.name output jumpBoxvmName string = vm_jumpboxwinvm.name diff --git a/deployment/bicep/shared/vm-nic.bicep b/deployment/bicep/shared/vm-nic.bicep index cc75f3f2..62e265e6 100644 --- a/deployment/bicep/shared/vm-nic.bicep +++ b/deployment/bicep/shared/vm-nic.bicep @@ -1,4 +1,4 @@ - +param namePrefix string = 'unique' param location string = resourceGroup().location param subnetId string param privateIPAddress string = '10.0.0.4' diff --git a/deployment/bicep/vnettest/vnetWithBastian.bicep b/deployment/bicep/vnettest/vnetWithBastian.bicep new file mode 100644 index 00000000..f44dc17b --- /dev/null +++ b/deployment/bicep/vnettest/vnetWithBastian.bicep @@ -0,0 +1,73 @@ +param namePrefix string = 'unique' +param location string = resourceGroup().location +param bastionSubnetIpPrefix string = '10.1.1.0/27' +param bastionHostName string='bastionhost' +var name = '${namePrefix}-${uniqueString(resourceGroup().id)}' +var subnetName = 'main-subnet' + +resource publicIp 'Microsoft.Network/publicIPAddresses@2020-06-01' = { + name: '${bastionHostName}-pip' + location: location + sku: { + name: 'Standard' + } + properties: { + publicIPAllocationMethod: 'Static' + } +} + +resource vnet_generic 'Microsoft.Network/virtualNetworks@2020-08-01' = { + name: name + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.1.0.0/16' + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: '10.1.0.0/24' + delegations: [] + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + } + ] + virtualNetworkPeerings: [] + enableDdosProtection: false + } +} + +resource subNet 'Microsoft.Network/virtualNetworks/subnets@2020-06-01' = { + name: '${vnet_generic.name}/AzureBastionSubnet' + properties: { + addressPrefix: bastionSubnetIpPrefix + } +} + +resource bastionHost 'Microsoft.Network/bastionHosts@2020-06-01' = { + name: bastionHostName + location: location + properties: { + ipConfigurations: [ + { + name: 'IpConf' + properties: { + subnet: { + id: subNet.id + } + publicIPAddress: { + id: publicIp.id + } + } + } + ] + } +} + +output vnetId string = vnet_generic.id +output subnetId string = '${vnet_generic.id}/subnets/${subnetName}' +output subnetName string = subnetName diff --git a/deployment/bicep/vnettest/vnetWithOutBastian.bicep b/deployment/bicep/vnettest/vnetWithOutBastian.bicep new file mode 100644 index 00000000..aca369be --- /dev/null +++ b/deployment/bicep/vnettest/vnetWithOutBastian.bicep @@ -0,0 +1,37 @@ +param namePrefix string = 'unique' +param location string = resourceGroup().location +var name = '${namePrefix}-${uniqueString(resourceGroup().id)}' +var subnetName = 'main-subnet' + + + +resource vnet_generic 'Microsoft.Network/virtualNetworks@2020-08-01' = { + name: name + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.1.0.0/16' + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: '10.1.0.0/24' + delegations: [] + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + } + ] + virtualNetworkPeerings: [] + enableDdosProtection: false + } +} + + + +output vnetId string = vnet_generic.id +output subnetId string = '${vnet_generic.id}/subnets/${subnetName}' +output subnetName string = subnetName From 2b1767c6e09a75aa6bc2bf16b3afac19aa19360a Mon Sep 17 00:00:00 2001 From: ahmeds Date: Tue, 13 Jul 2021 14:53:47 +0200 Subject: [PATCH 40/49] cleaned up windows vm create and custom script --- deployment/bicep/main.bicep | 4 +-- deployment/bicep/parameters.json | 6 ++--- deployment/bicep/shared/createvmwindows.bicep | 27 ++++++++----------- deployment/bicep/shared/shared.bicep | 4 +-- deployment/bicep/shared/vm-nic.bicep | 2 +- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/deployment/bicep/main.bicep b/deployment/bicep/main.bicep index 72501004..3a2a2e57 100644 --- a/deployment/bicep/main.bicep +++ b/deployment/bicep/main.bicep @@ -13,7 +13,7 @@ param environment string // parameters for azure devops agent param vmazdevopsUsername string param vmazdevopsPassword string -param vstsAccount string +param azureDevOpsAccount string param personalAccessToken string // Variables @@ -81,7 +81,7 @@ module shared './shared/shared.bicep' = { vmazdevopsPassword:vmazdevopsPassword vmazdevopsUsername: vmazdevopsUsername personalAccessToken: personalAccessToken - vstsAccount: vstsAccount + azureDevOpsAccount: azureDevOpsAccount resourceGroupName: sharedRG.name } } diff --git a/deployment/bicep/parameters.json b/deployment/bicep/parameters.json index 265d106e..7cd37471 100644 --- a/deployment/bicep/parameters.json +++ b/deployment/bicep/parameters.json @@ -8,11 +8,11 @@ "vmazdevopsPassword": { "value": "$1test_password" }, - "vstsAccount": { - "value": "https://dev.azure.com/ahmeds" + "azureDevOpsAccount": { + "value": "https://dev.azure.com/ORGNAME" }, "personalAccessToken": { - "value": "lbjxhqqxyovi5g6psgvcmncrlnv72o55fcfsqhwg33xaaurapykq" + "value": "PUTINTOKEN" }, "workloadName" :{ "value": "demoWL" diff --git a/deployment/bicep/shared/createvmwindows.bicep b/deployment/bicep/shared/createvmwindows.bicep index 9892abc4..15a0e217 100644 --- a/deployment/bicep/shared/createvmwindows.bicep +++ b/deployment/bicep/shared/createvmwindows.bicep @@ -1,4 +1,4 @@ -param namePrefix string = 'unique' + param location string = resourceGroup().location param subnetId string param osDiskType string = 'Standard_LRS' @@ -6,39 +6,34 @@ param vmSize string = 'Standard_D4_v3' param username string param password string param windowsOSVersion string = '2016-Datacenter' -//var vmName = '${namePrefix}${uniqueString(resourceGroup().id)}' + param vmName string param deployAgent bool=false -@description('The Visual Studio Team Services account name, that is, the first part of your VSTSAccount.visualstudio.com') -param vstsAccount string='' +@description('The Azure DevOps account name]') +param azureDevOpsAccount string='' -@description('The personal access token to connect to VSTS') +@description('The personal access token to connect to Azure DevOps') @secure() param personalAccessToken string='' -@description('The Visual Studio Team Services build agent pool for this build agent to join. Use \'Default\' if you don\'t have a separate pool.') +@description('The Azure DevOps build agent pool for this build agent to join. Use \'Default\' if you don\'t have a separate pool.') param poolName string = 'Default' @description('Enable autologon to run the build agent in interactive mode that can sustain machine reboots.
Set this to true if the agents will be used to run UI tests.') param enableAutologon bool = false + @description('The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated.') -param artifactsLocation string = 'https://raw.githubusercontent.com/ahmedsza/azdevopsagent/main/' +param artifactsLocation string = 'https://raw.githubusercontent.com/ahmedsza/azdevopsagent/main/setupagent.ps1' -@description('The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated.') -@secure() -param artifactsLocationSasToken string = '' -var vstsAgentName = 'agent-${vmName}' -//var vstsParameters = '-vstsAccount ${vstsAccount} -personalAccessToken ${personalAccessToken} -AgentName ${vstsAgentName} -PoolName ${poolName} -runAsAutoLogon ${enableAutologon} -vmAdminUserName ${username} -vmAdminPassword ${password}' -var vstsParameters = '-url ${vstsAccount} -pat ${personalAccessToken} -agent ${vstsAgentName} -pool ${poolName}' +var azureDevOpsAgentName = 'agent-${vmName}' // Bring in the nic module nic './vm-nic.bicep' = { name: '${vmName}-nic' params: { - namePrefix: '${vmName}-hdd' subnetId: subnetId vmName: vmName } @@ -94,9 +89,9 @@ resource vm_CustomScript 'Microsoft.Compute/virtualMachines/extensions@2018-06-0 typeHandlerVersion: '1.10' settings: { fileUris: [ - 'https://raw.githubusercontent.com/ahmedsza/azdevopsagent/main/setupagent.ps1' + artifactsLocation ] - commandToExecute: 'powershell.exe -ExecutionPolicy Unrestricted -Command ./setupagent.ps1 -url ${vstsAccount} -pat ${personalAccessToken} -agent ${vstsAgentName} -pool ${poolName}' + commandToExecute: 'powershell.exe -ExecutionPolicy Unrestricted -Command ./setupagent.ps1 -url ${azureDevOpsAccount} -pat ${personalAccessToken} -agent ${azureDevOpsAgentName} -pool ${poolName} -runAsAutoLogon ${enableAutologon} -vmAdminUserName ${username} -vmAdminPassword ${password}' } } } diff --git a/deployment/bicep/shared/shared.bicep b/deployment/bicep/shared/shared.bicep index 17aebdba..66d91800 100644 --- a/deployment/bicep/shared/shared.bicep +++ b/deployment/bicep/shared/shared.bicep @@ -5,7 +5,7 @@ param sharedResourceGroupResources object param subnetId string param vmazdevopsUsername string param vmazdevopsPassword string -param vstsAccount string +param azureDevOpsAccount string param personalAccessToken string @@ -36,7 +36,7 @@ module vm_devopswinvm './createvmwindows.bicep' = { username: vmazdevopsUsername password: vmazdevopsPassword vmName: 'azdevops-${sharedResourceGroupResources.vmSuffix}' - vstsAccount: vstsAccount + azureDevOpsAccount: azureDevOpsAccount personalAccessToken: personalAccessToken deployAgent: false } diff --git a/deployment/bicep/shared/vm-nic.bicep b/deployment/bicep/shared/vm-nic.bicep index 62e265e6..cc75f3f2 100644 --- a/deployment/bicep/shared/vm-nic.bicep +++ b/deployment/bicep/shared/vm-nic.bicep @@ -1,4 +1,4 @@ -param namePrefix string = 'unique' + param location string = resourceGroup().location param subnetId string param privateIPAddress string = '10.0.0.4' From bee64fb92fe83283656cccbebcf4d6eccc449f22 Mon Sep 17 00:00:00 2001 From: Kunal Babre Date: Wed, 15 Nov 2023 22:05:12 -0800 Subject: [PATCH 41/49] Add files via upload From 5886131ba5b19525a6ad3807c73b506d1171ab0b Mon Sep 17 00:00:00 2001 From: Kunal Babre Date: Fri, 15 Dec 2023 19:15:13 -0800 Subject: [PATCH 42/49] Add files via upload From c0632913df83eb74c5599f5f661d6a599e2c6bde Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Wed, 29 May 2024 10:51:34 -0500 Subject: [PATCH 43/49] pulling latest --- .../terraform/README.md | 2 ++ .../terraform/multitenant-asev3.tf | 3 ++ .../terraform/spoke/README.md | 2 ++ .../terraform/spoke/ai.tf | 36 ++++++++++--------- .../terraform/spoke/variables.tf | 27 ++++++++++++++ .../terraform/variables.tf | 27 ++++++++++++++ .../cognitive-services/openai/README.md | 22 ++++++++---- .../cognitive-services/openai/variables.tf | 6 ++-- 8 files changed, 99 insertions(+), 26 deletions(-) diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index d7cf524f..704c1a43 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -312,6 +312,8 @@ If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra | [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | | [hub\_vnet\_cidr](#input\_hub\_vnet\_cidr) | [Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20 | `list(string)` |
[
"10.242.0.0/20"
]
| no | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"F1"` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | | [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | diff --git a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf index 30929537..4d3f0043 100644 --- a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf +++ b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf @@ -36,6 +36,9 @@ module "spoke" { entra_admin_group_object_id = var.entra_admin_group_object_id appsvc_options = var.appsvc_options + oai_deployment_models = var.oai_deployment_models + oai_sku_name = var.oai_sku_name + # Spoke Network Configuration Variables hub_virtual_network = module.hub.virtual_network firewall_private_ip = module.hub.firewall_private_ip diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md index 00a2923d..574a289e 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md @@ -70,6 +70,8 @@ | [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | | [hub\_virtual\_network](#input\_hub\_virtual\_network) | [Required] Hub virtual network object that is live in Azure. Use either a data block or output of the `Hub` module (virtual\_network) to provide this value | `any` | n/a | yes | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `null` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | | [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf index bf7e25b8..8fb550f7 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf @@ -7,25 +7,27 @@ module "openai" { resource_group_name = azurerm_resource_group.spoke.name location = azurerm_resource_group.spoke.location - deployment = { - "text-embedding-ada-002" = { - name = "text-embedding-ada-002" - model_format = "OpenAI" - model_name = "text-embedding-ada-002" - model_version = "2" - scale_type = "Standard" - } - "gpt-35-turbo" = { - name = "gpt-35-turbo" - model_format = "OpenAI" - model_name = "gpt-35-turbo" - model_version = "0613" - scale_type = "Standard" - } - } - pe_private_link_subnet_id = module.network.subnets["privateLink"].id private_dns_zone = local.provisioned_dns_zones["privatelink.openai.azure.com"] + sku_name = var.oai_sku_name + deployment = var.oai_deployment_models + # { + # "text-embedding-ada-002" = { + # name = "text-embedding-ada-002" + # model_format = "OpenAI" + # model_name = "text-embedding-ada-002" + # model_version = "2" + # scale_type = "Standard" + # } + # "gpt-35-turbo" = { + # name = "gpt-35-turbo" + # model_format = "OpenAI" + # model_name = "gpt-35-turbo" + # model_version = "0613" + # scale_type = "Standard" + # } + # } + network_acls = [ { diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index 9b2c373d..d896d26a 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -157,6 +157,33 @@ variable "sql_databases" { ] } +variable "oai_sku_name" { + description = "[Optional] The SKU name for the OpenAI resource" + type = string + default = null +} + +variable "oai_deployment_models" { + description = "[Optional] Map to specify deployment models for the OpenAI resource" + type = any + default = { + "text-embedding-ada-002" = { + name = "text-embedding-ada-002" + model_format = "OpenAI" + model_name = "text-embedding-ada-002" + model_version = "2" + scale_type = "Standard" + } + "gpt-35-turbo" = { + name = "gpt-35-turbo" + model_format = "OpenAI" + model_name = "gpt-35-turbo" + model_version = "0613" + scale_type = "Standard" + } + } +} + variable "deployment_options" { type = object({ enable_waf = bool diff --git a/scenarios/secure-baseline-multitenant/terraform/variables.tf b/scenarios/secure-baseline-multitenant/terraform/variables.tf index 7484d3cb..b1a2977c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/variables.tf @@ -91,6 +91,33 @@ variable "devops_subnet_cidr" { ##################################### # Spoke Resource Configuration Variables ##################################### +variable "oai_sku_name" { + description = "[Optional] The SKU name for the OpenAI resource" + type = string + default = "F1" +} + +variable "oai_deployment_models" { + description = "[Optional] Map to specify deployment models for the OpenAI resource" + type = any + default = { + "text-embedding-ada-002" = { + name = "text-embedding-ada-002" + model_format = "OpenAI" + model_name = "text-embedding-ada-002" + model_version = "2" + scale_type = "Standard" + } + "gpt-35-turbo" = { + name = "gpt-35-turbo" + model_format = "OpenAI" + model_name = "gpt-35-turbo" + model_version = "0613" + scale_type = "Standard" + } + } +} + variable "entra_admin_group_object_id" { type = string description = "[Required] The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/README.md b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md index 14258195..208d236b 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/README.md +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md @@ -12,18 +12,21 @@ | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.72.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 3.105.0 | ## Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [private\_endpoint](#module\_private\_endpoint) | ../../private-endpoint | n/a | ## Resources | Name | Type | |------|------| -| [azurecaf_name.caf_name_akv](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_oai](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.priv_endpoint](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurerm_cognitive_account.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account) | resource | | [azurerm_cognitive_deployment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment) | resource | @@ -34,21 +37,28 @@ No modules. | [application\_name](#input\_application\_name) | Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | | [custom\_subdomain\_name](#input\_custom\_subdomain\_name) | The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix. | `string` | `null` | no | | [customer\_managed\_key](#input\_customer\_managed\_key) | type = object({
key\_vault\_key\_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account.
identity\_client\_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account.
}) |
object({
key_vault_key_id = string
identity_client_id = optional(string)
})
| `null` | no | +| [default\_tags\_enabled](#input\_default\_tags\_enabled) | Determines whether or not default tags are applied to resources. If set to true, tags will be applied. If set to false, tags will not be applied. | `bool` | `false` | no | | [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model = {
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
}
scale = {
scale\_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created.
}
rai\_policy\_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
})) |
map(object({
name = string
model_format = string
model_name = string
model_version = string
scale_type = string
rai_policy_name = optional(string)
}))
| `{}` | no | +| [diagnostic\_setting](#input\_diagnostic\_setting) | A map of objects that represent the configuration for a diagnostic setting."
type = map(object({
name = (Required) Specifies the name of the diagnostic setting. Changing this forces a new resource to be created.
log\_analytics\_workspace\_id = (Optional) (Optional) Specifies the resource id of an Azure Log Analytics workspace where diagnostics data should be sent.
log\_analytics\_destination\_type = (Optional) Possible values are `AzureDiagnostics` and `Dedicated`. When set to Dedicated, logs sent to a Log Analytics workspace will go into resource specific tables, instead of the legacy `AzureDiagnostics` table.
eventhub\_name = (Optional) Specifies the name of the Event Hub where diagnostics data should be sent.
eventhub\_authorization\_rule\_id = (Optional) Specifies the resource id of an Event Hub Namespace Authorization Rule used to send diagnostics data.
storage\_account\_id = (Optional) Specifies the resource id of an Azure storage account where diagnostics data should be sent.
partner\_solution\_id = (Optional) The resource id of the market partner solution where diagnostics data should be sent. For potential partner integrations, click to learn more about partner integration.
audit\_log\_retention\_policy = (Optional) Specifies the retention policy for the audit log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
request\_response\_log\_retention\_policy = (Optional) Specifies the retention policy for the request response log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
trace\_log\_retention\_policy = (Optional) Specifies the retention policy for the trace log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
metric\_retention\_policy = (Optional) Specifies the retention policy for the metric. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
})) |
map(object({
name = string
log_analytics_workspace_id = optional(string)
log_analytics_destination_type = optional(string)
eventhub_name = optional(string)
eventhub_authorization_rule_id = optional(string)
storage_account_id = optional(string)
partner_solution_id = optional(string)
audit_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
request_response_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
trace_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
metric_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
}))
| `{}` | no | | [dynamic\_throttling\_enabled](#input\_dynamic\_throttling\_enabled) | Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled. | `bool` | `null` | no | | [environment](#input\_environment) | Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | | [fqdns](#input\_fqdns) | List of FQDNs allowed for the Cognitive Account. | `list(string)` | `null` | no | | [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | -| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
| `null` | no | +| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
|
{
"type": "SystemAssigned"
}
| no | | [local\_auth\_enabled](#input\_local\_auth\_enabled) | Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`. | `bool` | `true` | no | | [location](#input\_location) | Azure OpenAI deployment region. Set this variable to `null` would use resource group's location. | `string` | n/a | yes | | [network\_acls](#input\_network\_acls) | type = set(object({
default\_action = (Required) The Default Action to use when no rules match from ip\_rules / virtual\_network\_rules. Possible values are `Allow` and `Deny`.
ip\_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account.
virtual\_network\_rules = optional(set(object({
subnet\_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account.
ignore\_missing\_vnet\_service\_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`.
})))
})) |
set(object({
default_action = string
ip_rules = optional(set(string))
virtual_network_rules = optional(set(object({
subnet_id = string
ignore_missing_vnet_service_endpoint = optional(bool, false)
})))
}))
| `null` | no | | [outbound\_network\_access\_restricted](#input\_outbound\_network\_access\_restricted) | Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [pe\_private\_link\_subnet\_id](#input\_pe\_private\_link\_subnet\_id) | The ID of the Subnet which the Private Endpoint should be created in. Changing this forces a new resource to be created. | `string` | n/a | yes | +| [pe\_subresource](#input\_pe\_subresource) | A list of subresource names which the Private Endpoint is able to connect to. `subresource_names` corresponds to `group_id`. Possible values are detailed in the product [documentation](https://docs.microsoft.com/azure/private-link/private-endpoint-overview#private-link-resource) in the `Subresources` column. Changing this forces a new resource to be created. | `list(string)` |
[
"account"
]
| no | +| [private\_dns\_zone](#input\_private\_dns\_zone) | The Private DNS Zone you'd like to use. |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | | [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether public network access is allowed for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | | [resource\_group\_name](#input\_resource\_group\_name) | Name of the azure resource group to use. The resource group must exist. | `string` | n/a | yes | -| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"S0"` | no | +| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"F0"` | no | | [storage](#input\_storage) | type = list(object({
storage\_account\_id = (Required) Full resource id of a Microsoft.Storage resource.
identity\_client\_id = (Optional) The client ID of the managed identity associated with the storage resource.
})) |
list(object({
storage_account_id = string
identity_client_id = optional(string)
}))
| `[]` | no | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | +| [tracing\_tags\_enabled](#input\_tracing\_tags\_enabled) | Whether enable tracing tags that generated by BridgeCrew Yor. | `bool` | `false` | no | +| [tracing\_tags\_prefix](#input\_tracing\_tags\_prefix) | Default prefix for generated tracing tags | `string` | `"avm_"` | no | ## Outputs diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf index 44f9a7e0..d069301c 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf @@ -92,7 +92,7 @@ variable "identity" { type = string identity_ids = optional(list(string)) }) - default = { + default = { type = "SystemAssigned" } description = <<-DESCRIPTION @@ -144,7 +144,7 @@ variable "pe_subresource" { } variable "pe_private_link_subnet_id" { - type = string + type = string description = "The ID of the Subnet which the Private Endpoint should be created in. Changing this forces a new resource to be created." } @@ -208,7 +208,7 @@ variable "public_network_access_enabled" { variable "sku_name" { type = string - default = "S0" + default = "F0" description = "Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`." } From 7c742801b9721e134a68a92744966a91c64ca52e Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Tue, 18 Jun 2024 19:39:38 -0500 Subject: [PATCH 44/49] updating defaults, added to suffix --- scenarios/secure-baseline-multitenant/terraform/README.md | 2 +- .../secure-baseline-multitenant/terraform/hub/network.tf | 1 + .../secure-baseline-multitenant/terraform/spoke/README.md | 2 +- .../secure-baseline-multitenant/terraform/spoke/network.tf | 6 +++++- .../terraform/spoke/variables.tf | 2 +- .../secure-baseline-multitenant/terraform/variables.tf | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index 704c1a43..9585e8a1 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -313,7 +313,7 @@ If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra | [hub\_vnet\_cidr](#input\_hub\_vnet\_cidr) | [Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20 | `list(string)` |
[
"10.242.0.0/20"
]
| no | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | | [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | -| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"F1"` | no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"S0"` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | | [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/network.tf b/scenarios/secure-baseline-multitenant/terraform/hub/network.tf index d3f04763..b116ab53 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/network.tf @@ -12,6 +12,7 @@ resource "azurecaf_name" "caf_name_hub_rg" { name = var.application_name resource_type = "azurerm_resource_group" prefixes = local.global_settings.prefixes + suffixes = local.global_settings.suffixes random_length = local.global_settings.random_length clean_input = true passthrough = local.global_settings.passthrough diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md index 574a289e..bef4381c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md @@ -71,7 +71,7 @@ | [hub\_virtual\_network](#input\_hub\_virtual\_network) | [Required] Hub virtual network object that is live in Azure. Use either a data block or output of the `Hub` module (virtual\_network) to provide this value | `any` | n/a | yes | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | | [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | -| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `null` | no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"S0"` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | | [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf index 975dc870..fc3f3945 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf @@ -15,7 +15,9 @@ resource "azurecaf_name" "caf_name_spoke_rg" { name = var.application_name resource_type = "azurerm_resource_group" # prefixes = concat(["spoke"], local.global_settings.prefixes) - prefixes = local.global_settings.prefixes + prefixes = local.global_settings.prefixes + suffixes = local.global_settings.suffixes + random_length = local.global_settings.random_length clean_input = true passthrough = local.global_settings.passthrough @@ -33,6 +35,8 @@ resource "azurecaf_name" "appsvc_subnet" { name = var.application_name resource_type = "azurerm_subnet" prefixes = concat(["spoke"], local.global_settings.prefixes) + suffixes = local.global_settings.suffixes + random_length = local.global_settings.random_length clean_input = true passthrough = local.global_settings.passthrough diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index d896d26a..92372713 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -160,7 +160,7 @@ variable "sql_databases" { variable "oai_sku_name" { description = "[Optional] The SKU name for the OpenAI resource" type = string - default = null + default = "S0" } variable "oai_deployment_models" { diff --git a/scenarios/secure-baseline-multitenant/terraform/variables.tf b/scenarios/secure-baseline-multitenant/terraform/variables.tf index b1a2977c..3f915bbf 100644 --- a/scenarios/secure-baseline-multitenant/terraform/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/variables.tf @@ -94,7 +94,7 @@ variable "devops_subnet_cidr" { variable "oai_sku_name" { description = "[Optional] The SKU name for the OpenAI resource" type = string - default = "F1" + default = "S0" } variable "oai_deployment_models" { From 645de9e0eb5e7a1d4f4dd2cdc8c3b817a2e9528d Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Mon, 14 Oct 2024 13:57:10 -0500 Subject: [PATCH 45/49] updating provider, testing e2e deployment --- .pre-commit-config.yaml | 7 ++- .../terraform/README.md | 15 ++--- .../terraform/hub/_locals.tf | 2 +- .../terraform/main.tf | 2 +- .../terraform/multitenant-asev3.tf | 1 + .../terraform/spoke/README.md | 20 ++++--- .../terraform/spoke/_locals.tf | 2 +- .../terraform/spoke/ai.tf | 4 ++ .../terraform/spoke/app.tf | 12 ++-- .../terraform/spoke/asev3.tf | 16 ++++-- .../terraform/spoke/network.tf | 13 +++++ .../terraform/spoke/variables.tf | 20 +++++-- .../terraform/variables.tf | 13 ++++- .../app-configuration/main.tf | 4 ++ .../terraform-modules/app-service/README.md | 8 ++- .../app-service/linux-web-app/README.md | 8 +-- .../app-service/linux-web-app/main.tf | 4 ++ .../app-service/linux-web-app/module.tf | 37 +++++++------ .../app-service/linux-web-app/outputs.tf | 16 +++--- .../terraform-modules/app-service/main.tf | 4 ++ .../terraform-modules/app-service/module.tf | 55 ++++++++++++------- .../terraform-modules/app-service/outputs.tf | 10 ++-- .../app-service/variables.tf | 29 +++++++--- .../app-service/windows-web-app/README.md | 11 ++-- .../app-service/windows-web-app/main.tf | 4 ++ .../app-service/windows-web-app/module.tf | 45 ++++++++------- .../app-service/windows-web-app/outputs.tf | 16 +++--- .../app-service/windows-web-app/variables.tf | 9 ++- .../shared/terraform-modules/bastion/main.tf | 4 ++ .../cognitive-services/openai/README.md | 8 +-- .../cognitive-services/openai/main.tf | 2 +- .../cognitive-services/openai/module.tf | 9 ++- .../cognitive-services/openai/variables.tf | 22 ++++---- .../shared/terraform-modules/firewall/main.tf | 4 ++ .../terraform-modules/frontdoor/main.tf | 4 ++ .../terraform-modules/key-vault/README.md | 14 +++-- .../terraform-modules/key-vault/main.tf | 4 ++ .../shared/terraform-modules/network/main.tf | 4 ++ .../shared/terraform-modules/openai/README.md | 10 ++-- .../shared/terraform-modules/openai/main.tf | 2 +- .../private-dns-zone/main.tf | 4 ++ .../private-endpoint/main.tf | 4 ++ .../private-endpoint/module.tf | 2 +- .../shared/terraform-modules/redis/README.md | 4 +- .../shared/terraform-modules/redis/main.tf | 4 ++ .../shared/terraform-modules/redis/module.tf | 4 +- .../terraform-modules/sql-database/main.tf | 4 ++ .../user-defined-routes/README.md | 4 +- .../user-defined-routes/main.tf | 4 ++ .../user-defined-routes/module.tf | 2 +- .../terraform-modules/windows-vm/README.md | 16 +++--- .../terraform-modules/windows-vm/main.tf | 4 ++ 52 files changed, 345 insertions(+), 185 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 058a9482..e0692c30 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,12 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.74.1 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases + rev: v1.96.1 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases hooks: - id: terraform_fmt - - id: terraform_validate + # - id: terraform_validate + # args: + # - --tf-init-args=-upgrade + # - --hook-config=--retry-once-with-cleanup=true # Boolean. true or false - id: terraform_docs args: - --hook-config=--path-to-file=README.md # Valid UNIX path. I.e. ../TFDOC.md or docs/README.md etc. diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index 9585e8a1..5ce63a52 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -261,21 +261,21 @@ Connect to the VM using the local VM admin credentials and run `dsregcmd /status ``` If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra ID credentials again after a few minutes. If it's not Microsoft Entra ID joined, attempt to re-install the VM extension or manually enroll the VM to Microsoft Entra ID by following the steps in Edge: open Edge and click "Sign in to sync data", select "Work or school account", and then press OK on "Allow my organization to manage my device". It takes a few minutes for the policies to be applied, device scanned and confirmed as secure to access corporate resources. You will know that the process is complete. - + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >=1.3 | | [azurecaf](#requirement\_azurecaf) | >=1.2.23 | -| [azurerm](#requirement\_azurerm) | >=3.66.0 | +| [azurerm](#requirement\_azurerm) | 4.5.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.27 | -| [azurerm](#provider\_azurerm) | 3.89.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules @@ -289,15 +289,16 @@ If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra | Name | Type | |------|------| | [azurecaf_name.caf_name_spoke_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | -| [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | +| [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/resource_group) | resource | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | -| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | +| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = optional(list(string))

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1",
"zone_redundant": true
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | | [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [ase\_subnet\_cidr](#input\_ase\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26 | `list(string)` |
[
"10.240.5.0/24"
]
| no | | [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | | [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | | [deployment\_options](#input\_deployment\_options) | [Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
deploy_asev3 = bool
})
|
{
"deploy_app_config": false,
"deploy_asev3": false,
"deploy_bastion": false,
"deploy_openai": false,
"deploy_redis": false,
"deploy_sql_database": false,
"deploy_vm": false,
"enable_diagnostic_settings": false,
"enable_egress_lockdown": false,
"enable_waf": false
}
| no | @@ -327,4 +328,4 @@ If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra ## Outputs No outputs. - + diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf b/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf index 6df104aa..402a1cc1 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf @@ -1,5 +1,5 @@ locals { - deployment_name = "sec-baseline-1-hub" + deployment_name = "sec-baseline-hub" global_settings = merge({ environment = try(var.global_settings.environment, var.environment) diff --git a/scenarios/secure-baseline-multitenant/terraform/main.tf b/scenarios/secure-baseline-multitenant/terraform/main.tf index d28d493a..1fb93016 100644 --- a/scenarios/secure-baseline-multitenant/terraform/main.tf +++ b/scenarios/secure-baseline-multitenant/terraform/main.tf @@ -6,7 +6,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = ">=3.66.0" + version = "4.5.0" } azurecaf = { source = "aztfmod/azurecaf" diff --git a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf index 4d3f0043..f59c3405 100644 --- a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf +++ b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf @@ -44,6 +44,7 @@ module "spoke" { firewall_private_ip = module.hub.firewall_private_ip firewall_rules = module.hub.firewall_rules spoke_vnet_cidr = var.spoke_vnet_cidr + ase_subnet_cidr = var.ase_subnet_cidr devops_subnet_cidr = var.devops_subnet_cidr appsvc_subnet_cidr = var.appsvc_subnet_cidr front_door_subnet_cidr = var.front_door_subnet_cidr diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md index bef4381c..a1dd27d9 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md @@ -1,13 +1,13 @@ # spoke - + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >=1.3 | | [azurecaf](#requirement\_azurecaf) | >=1.2.23 | -| [azurerm](#requirement\_azurerm) | >=3.66.0 | +| [azurerm](#requirement\_azurerm) | 4.5.0 | ## Providers @@ -44,11 +44,11 @@ | [azurecaf_name.caf_name_law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_spoke_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | -| [azurerm_app_service_environment_v3.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_environment_v3) | resource | -| [azurerm_log_analytics_workspace.law](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/log_analytics_workspace) | resource | -| [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | -| [azurerm_user_assigned_identity.contributor](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | -| [azurerm_user_assigned_identity.reader](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | +| [azurerm_app_service_environment_v3.this](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/app_service_environment_v3) | resource | +| [azurerm_log_analytics_workspace.law](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/log_analytics_workspace) | resource | +| [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/resource_group) | resource | +| [azurerm_user_assigned_identity.contributor](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/user_assigned_identity) | resource | +| [azurerm_user_assigned_identity.reader](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/user_assigned_identity) | resource | | [random_integer.unique_id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) | resource | ## Inputs @@ -56,8 +56,9 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | -| [appsvc\_options](#input\_appsvc\_options) | [Optional] The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | +| [appsvc\_options](#input\_appsvc\_options) | [Optional] The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": null
}
}
| no | | [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [ase\_subnet\_cidr](#input\_ase\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26 | `list(string)` |
[
"10.240.5.0/24"
]
| no | | [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_asev3 = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_asev3": false,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | | [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | | [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | @@ -82,6 +83,7 @@ | [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account. | `string` | `null` | no | | [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Azure AD object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | | [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Azure AD username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | +| [zone\_redundant](#input\_zone\_redundant) | [Optional] Enable zone redundancy for the app service environment. Defaults to true | `bool` | `true` | no | ## Outputs @@ -98,4 +100,4 @@ | [web\_app\_name](#output\_web\_app\_name) | n/a | | [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | | [web\_app\_uri](#output\_web\_app\_uri) | n/a | - + diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf index 199aad07..32d74dc9 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf @@ -1,5 +1,5 @@ locals { - deployment_name = "sec-baseline-1-spoke" + deployment_name = "sec-baseline-spoke" # used in spoke-network.tf private_dns_zones = [for each in diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf index 8fb550f7..989a904e 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf @@ -33,6 +33,10 @@ module "openai" { { default_action = "Deny" virtual_network_rules = [ + var.deployment_options.deploy_asev3 ? { + subnet_id = module.network.subnets["hostingEnvironments"].id + ignore_missing_vnet_service_endpoint = true + } : null, { subnet_id = module.network.subnets["serverFarm"].id ignore_missing_vnet_service_endpoint = true diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf index 79a63e92..f2387bd4 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf @@ -5,6 +5,9 @@ locals { sql_connstring = length(module.sql_database) > 0 ? module.sql_database[0].sql_db_connection_string : "SQL_NOT_PROVISIONED" redis_connstring = length(module.redis_cache) > 0 ? module.redis_cache[0].redis_connection_string : "REDIS_NOT_PROVISIONED" + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + is_isolated_sku = can(regex("(?i)^I.*v2$", var.appsvc_options.service_plan.sku_name)) } # Deploy the App Service @@ -18,10 +21,11 @@ module "app_service" { log_analytics_workspace_id = azurerm_log_analytics_workspace.law.id enable_diagnostic_settings = var.deployment_options.enable_diagnostic_settings - appsvc_subnet_id = module.network.subnets["serverFarm"].id - frontend_subnet_id = module.network.subnets["ingress"].id - service_plan_options = var.appsvc_options.service_plan - + # If isolated SKU (using App Service Environment) then do not enable vnet integration + appsvc_subnet_id = local.is_isolated_sku == true ? null : module.network.subnets["serverFarm"].id + frontend_subnet_id = module.network.subnets["ingress"].id + service_plan_options = var.appsvc_options.service_plan + app_service_environment_id = var.deployment_options.deploy_asev3 ? azurerm_app_service_environment_v3.this[0].id : null identity = { type = "UserAssigned" identity_ids = [ diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf index 90cc621f..a2fff7be 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf @@ -20,17 +20,20 @@ resource "azurerm_app_service_environment_v3" "this" { resource_group_name = azurerm_resource_group.spoke.name # a /24 or larger CIDR is required. Once associated with an ASE, this size cannot be changed. - subnet_id = module.network.subnets["serverFarm"].id + subnet_id = module.network.subnets["hostingEnvironments"].id # Possible values are None (for an External VIP Type), and "Web, Publishing" (for an Internal VIP Type). internal_load_balancing_mode = "Web, Publishing" + # Required for Private DNS and Endpoint configs + allow_new_private_endpoint_connections = true + # You can only set either dedicated_host_count or zone_redundant but not both. # Changing this forces a new resource to be created. # dedicated_host_count = 2 # Changing this forces a new resource to be created. - zone_redundant = false + zone_redundant = var.zone_redundant cluster_setting { @@ -38,10 +41,11 @@ resource "azurerm_app_service_environment_v3" "this" { value = "1" } - cluster_setting { - name = "InternalEncryption" - value = "true" - } + ## Caution: Enabling internal encryption will add hours to your deployment time + # cluster_setting { + # name = "InternalEncryption" + # value = "true" + # } cluster_setting { name = "FrontEndSSLCipherSuiteOrder" diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf index fc3f3945..1eac3b6b 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf @@ -43,6 +43,7 @@ resource "azurecaf_name" "appsvc_subnet" { use_slug = local.global_settings.use_slug } + ## Deploy Spoke VNet with Server Farm, Ingress, Private Link and DevOps subnets module "network" { source = "../../../shared/terraform-modules/network" @@ -58,6 +59,7 @@ module "network" { resource_group = var.hub_virtual_network.resource_group_name } + subnets = [ { name = "serverFarm" @@ -70,6 +72,17 @@ module "network" { } } }, + var.deployment_options.deploy_asev3 ? { + name = "hostingEnvironments" + subnet_cidr = var.ase_subnet_cidr + delegation = { + name = "Microsoft.Web.hostingEnvironments" + service_delegation = { + name = "Microsoft.Web/hostingEnvironments" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } + } : null, { name = "ingress" subnet_cidr = var.front_door_subnet_cidr diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index 92372713..22ba07ea 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -98,6 +98,11 @@ variable "devops_subnet_cidr" { default = ["10.240.10.128/26"] } +variable "ase_subnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26" + default = ["10.240.5.0/24"] +} variable "appsvc_subnet_cidr" { type = list(string) description = "[Optional] The CIDR block for the subnet." @@ -157,6 +162,12 @@ variable "sql_databases" { ] } +variable "zone_redundant" { + type = bool + description = "[Optional] Enable zone redundancy for the app service environment. Defaults to true" + default = true +} + variable "oai_sku_name" { description = "[Optional] The SKU name for the OpenAI resource" type = string @@ -217,10 +228,9 @@ variable "deployment_options" { variable "appsvc_options" { type = object({ service_plan = object({ - os_type = string - sku_name = string - worker_count = optional(number) - zone_redundant = optional(bool) + os_type = string + sku_name = string + worker_count = optional(number) }) web_app = object({ slots = list(string) @@ -251,7 +261,7 @@ variable "appsvc_options" { sku_name = "S1" } web_app = { - slots = [] + slots = null application_stack = { current_stack = "dotnet" diff --git a/scenarios/secure-baseline-multitenant/terraform/variables.tf b/scenarios/secure-baseline-multitenant/terraform/variables.tf index 3f915bbf..e2795978 100644 --- a/scenarios/secure-baseline-multitenant/terraform/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/variables.tf @@ -88,6 +88,12 @@ variable "devops_subnet_cidr" { default = ["10.240.10.128/26"] } +variable "ase_subnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26" + default = ["10.240.5.0/24"] +} + ##################################### # Spoke Resource Configuration Variables ##################################### @@ -231,7 +237,7 @@ variable "appsvc_options" { zone_redundant = optional(bool) }) web_app = object({ - slots = list(string) + slots = optional(list(string)) application_stack = object({ current_stack = string # required for windows @@ -255,8 +261,9 @@ variable "appsvc_options" { default = { service_plan = { - os_type = "Windows" - sku_name = "S1" + os_type = "Windows" + sku_name = "S1" + zone_redundant = true } web_app = { slots = [] diff --git a/scenarios/shared/terraform-modules/app-configuration/main.tf b/scenarios/shared/terraform-modules/app-configuration/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-configuration/main.tf +++ b/scenarios/shared/terraform-modules/app-configuration/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/README.md b/scenarios/shared/terraform-modules/app-service/README.md index 50893e8d..9a8c246e 100644 --- a/scenarios/shared/terraform-modules/app-service/README.md +++ b/scenarios/shared/terraform-modules/app-service/README.md @@ -25,6 +25,7 @@ No requirements. |------|------| | [azurecaf_name.caf_name_appinsights](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_asp](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_app_service_virtual_network_swift_connection.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_virtual_network_swift_connection) | resource | | [azurerm_application_insights.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/application_insights) | resource | | [azurerm_service_plan.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/service_plan) | resource | @@ -32,6 +33,7 @@ No requirements. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [app\_service\_environment\_id](#input\_app\_service\_environment\_id) | The app service environment id | `string` | `null` | no | | [application\_name](#input\_application\_name) | The name of your application | `string` | n/a | yes | | [appsvc\_subnet\_id](#input\_appsvc\_subnet\_id) | The subnet id where the app service will be integrated | `string` | `null` | no | | [deploy\_web\_app](#input\_deploy\_web\_app) | Feature flag to deploy a web app as part of the module | `bool` | `true` | no | @@ -43,9 +45,9 @@ No requirements. | [log\_analytics\_workspace\_id](#input\_log\_analytics\_workspace\_id) | The log analytics workspace id | `string` | n/a | yes | | [private\_dns\_zone](#input\_private\_dns\_zone) | The private dns zone id where the app service will be integrated |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | | [resource\_group](#input\_resource\_group) | The name of the resource group where all resources in this example should be created. | `string` | n/a | yes | -| [service\_plan\_options](#input\_service\_plan\_options) | The options for the app service |
object({
os_type = string
sku_name = string
app_service_environment_id = optional(string)
worker_count = optional(number)
zone_redundant = optional(bool)
})
|
{
"os_type": "Windows",
"sku_name": "S1"
}
| no | +| [service\_plan\_options](#input\_service\_plan\_options) | The options for the app service |
object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
|
{
"os_type": "Windows",
"sku_name": "S1",
"worker_count": 3
}
| no | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | -| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
slots = optional(list(string))

application_stack = optional(object({
current_stack = optional(string) # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
}))
})
|
{
"application_stack": {},
"slots": []
}
| no | +| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
slots = list(string)
instrumentation_key = optional(string)
ai_connection_string = optional(string)
vnet_route_all_enabled = optional(bool)
use_32_bit_worker = optional(bool)
vnet_integration_enabled = optional(bool)

application_stack = optional(object({
current_stack = optional(string) # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
}))
})
|
{
"application_stack": {},
"slots": []
}
| no | ## Outputs @@ -57,6 +59,6 @@ No requirements. | [web\_app\_principal\_id](#output\_web\_app\_principal\_id) | n/a | | [web\_app\_slot\_hostname](#output\_web\_app\_slot\_hostname) | n/a | | [web\_app\_slot\_id](#output\_web\_app\_slot\_id) | n/a | +| [web\_app\_slot\_identities](#output\_web\_app\_slot\_identities) | n/a | | [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | -| [web\_app\_slot\_principal\_id](#output\_web\_app\_slot\_principal\_id) | n/a | diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md b/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md index 932a27fa..6f050bac 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md @@ -58,8 +58,8 @@ No requirements. | [web\_app\_id](#output\_web\_app\_id) | n/a | | [web\_app\_name](#output\_web\_app\_name) | n/a | | [web\_app\_principal\_id](#output\_web\_app\_principal\_id) | n/a | -| [web\_app\_slot\_hostname](#output\_web\_app\_slot\_hostname) | n/a | -| [web\_app\_slot\_id](#output\_web\_app\_slot\_id) | n/a | -| [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | -| [web\_app\_slot\_principal\_id](#output\_web\_app\_slot\_principal\_id) | n/a | +| [web\_app\_slot\_hostnames](#output\_web\_app\_slot\_hostnames) | n/a | +| [web\_app\_slot\_identities](#output\_web\_app\_slot\_identities) | n/a | +| [web\_app\_slot\_ids](#output\_web\_app\_slot\_ids) | n/a | +| [web\_app\_slot\_names](#output\_web\_app\_slot\_names) | n/a | diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf index d9c5614d..bf11c877 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf @@ -28,12 +28,11 @@ resource "azurerm_linux_web_app" "this" { use_32_bit_worker = false application_stack { - docker_image = var.webapp_options.application_stack.docker_image - docker_image_tag = var.webapp_options.application_stack.docker_image_tag - dotnet_version = var.webapp_options.application_stack.dotnet_version - java_version = var.webapp_options.application_stack.java_version - php_version = var.webapp_options.application_stack.php_version - node_version = var.webapp_options.application_stack.node_version + docker_image_name = "${var.webapp_options.application_stack.docker_image}:${var.webapp_options.application_stack.docker_image_tag}" + dotnet_version = var.webapp_options.application_stack.dotnet_version + java_version = var.webapp_options.application_stack.java_version + php_version = var.webapp_options.application_stack.php_version + node_version = var.webapp_options.application_stack.node_version } } @@ -135,7 +134,8 @@ module "private_endpoint" { } resource "azurerm_linux_web_app_slot" "slot" { - name = var.webapp_options.slots[0] + count = length(var.webapp_options.slots) + name = var.webapp_options.slots[count.index] app_service_id = azurerm_linux_web_app.this.id virtual_network_subnet_id = var.appsvc_subnet_id https_only = true @@ -150,37 +150,38 @@ resource "azurerm_linux_web_app_slot" "slot" { use_32_bit_worker = false application_stack { - docker_image = var.webapp_options.application_stack.docker_image - docker_image_tag = var.webapp_options.application_stack.docker_image_tag - dotnet_version = var.webapp_options.application_stack.dotnet_version - java_version = var.webapp_options.application_stack.java_version - php_version = var.webapp_options.application_stack.php_version - node_version = var.webapp_options.application_stack.node_version + docker_image_name = "${var.webapp_options.application_stack.docker_image}:${var.webapp_options.application_stack.docker_image_tag}" + dotnet_version = var.webapp_options.application_stack.dotnet_version + java_version = var.webapp_options.application_stack.java_version + php_version = var.webapp_options.application_stack.php_version + node_version = var.webapp_options.application_stack.node_version } } } resource "azurecaf_name" "slot" { - name = "${azurecaf_name.caf_name_linwebapp.result}-${var.webapp_options.slots[0]}" + count = length(var.webapp_options.slots) + name = "${azurecaf_name.caf_name_linwebapp.result}-${var.webapp_options.slots[count.index]}" resource_type = "azurerm_private_endpoint" } module "private_endpoint_slot" { source = "../../private-endpoint" + count = length(var.webapp_options.slots) - name = azurecaf_name.slot.result + name = "${azurecaf_name.slot[count.index].result}-${azurerm_linux_web_app_slot.slot[count.index].name}" resource_group = var.resource_group location = var.location subnet_id = var.frontend_subnet_id private_connection_resource_id = azurerm_linux_web_app.this.id - subresource_names = ["sites-${var.webapp_options.slots[0]}"] + subresource_names = ["sites"] private_dns_zone = var.private_dns_zone private_dns_records = [ - lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot.name}"), - lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot.name}.scm") + lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot[count.index].name}"), + lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot[count.index].name}.scm") ] depends_on = [ diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf index 58dfad3a..266aeb5f 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf @@ -14,18 +14,18 @@ output "web_app_principal_id" { value = azurerm_linux_web_app.this.identity.0.principal_id } -output "web_app_slot_id" { - value = azurerm_linux_web_app_slot.slot.id +output "web_app_slot_ids" { + value = azurerm_linux_web_app_slot.slot.*.id } -output "web_app_slot_name" { - value = azurerm_linux_web_app_slot.slot.name +output "web_app_slot_names" { + value = azurerm_linux_web_app_slot.slot.*.name } -output "web_app_slot_hostname" { - value = azurerm_linux_web_app_slot.slot.default_hostname +output "web_app_slot_hostnames" { + value = azurerm_linux_web_app_slot.slot.*.default_hostname } -output "web_app_slot_principal_id" { - value = azurerm_linux_web_app_slot.slot.identity.0.principal_id +output "web_app_slot_identities" { + value = azurerm_linux_web_app_slot.slot.*.identity } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/main.tf b/scenarios/shared/terraform-modules/app-service/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-service/main.tf +++ b/scenarios/shared/terraform-modules/app-service/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/module.tf b/scenarios/shared/terraform-modules/app-service/module.tf index 760fffb6..61e0b985 100644 --- a/scenarios/shared/terraform-modules/app-service/module.tf +++ b/scenarios/shared/terraform-modules/app-service/module.tf @@ -3,6 +3,9 @@ locals { ai_connection_string = azurerm_application_insights.this.connection_string instrumentation_key = azurerm_application_insights.this.instrumentation_key }, var.webapp_options) + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + is_isolated_sku = can(regex("(?i)^I.*v2$", var.service_plan_options.sku_name)) } resource "azurecaf_name" "caf_name_asp" { @@ -24,10 +27,13 @@ resource "azurerm_service_plan" "this" { sku_name = var.service_plan_options.sku_name os_type = var.service_plan_options.os_type - app_service_environment_id = lookup(var.service_plan_options, "app_service_environment_id", null) - worker_count = coalesce(var.service_plan_options.worker_count, 1) - zone_balancing_enabled = coalesce(var.service_plan_options.zone_redundant, false) - tags = local.tags + app_service_environment_id = var.app_service_environment_id + worker_count = coalesce(var.service_plan_options.worker_count, 3) + + # For ASEv3 hosted ASP, zone redundancy is managed at the ASE level + zone_balancing_enabled = var.app_service_environment_id != null ? true : false + + tags = local.tags } module "windows_web_app" { @@ -41,13 +47,15 @@ module "windows_web_app" { location = var.location service_plan_id = azurerm_service_plan.this.id service_plan_resource = azurerm_service_plan.this - appsvc_subnet_id = var.appsvc_subnet_id - frontend_subnet_id = var.frontend_subnet_id - webapp_options = local.webapp_options - private_dns_zone = var.private_dns_zone - identity = var.identity - global_settings = var.global_settings - tags = var.tags + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + appsvc_subnet_id = local.is_isolated_sku == true ? null : var.appsvc_subnet_id + frontend_subnet_id = var.frontend_subnet_id + webapp_options = local.webapp_options + private_dns_zone = var.private_dns_zone + identity = var.identity + global_settings = var.global_settings + tags = var.tags log_analytics_workspace_id = var.log_analytics_workspace_id enable_diagnostic_settings = var.enable_diagnostic_settings @@ -64,14 +72,23 @@ module "linux_web_app" { location = var.location service_plan_id = azurerm_service_plan.this.id service_plan_resource = azurerm_service_plan.this - appsvc_subnet_id = var.appsvc_subnet_id - frontend_subnet_id = var.frontend_subnet_id - webapp_options = local.webapp_options - private_dns_zone = var.private_dns_zone - identity = var.identity - global_settings = var.global_settings - tags = var.tags + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + appsvc_subnet_id = local.is_isolated_sku == true ? null : var.appsvc_subnet_id + frontend_subnet_id = var.frontend_subnet_id + webapp_options = local.webapp_options + private_dns_zone = var.private_dns_zone + identity = var.identity + global_settings = var.global_settings + tags = var.tags log_analytics_workspace_id = var.log_analytics_workspace_id enable_diagnostic_settings = var.enable_diagnostic_settings -} \ No newline at end of file +} + +# Only required for vnet integration (non ASE) +resource "azurerm_app_service_virtual_network_swift_connection" "this" { + count = local.is_isolated_sku == true ? 0 : 1 + app_service_id = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_id : module.linux_web_app[0].web_app_id : null + subnet_id = var.appsvc_subnet_id +} diff --git a/scenarios/shared/terraform-modules/app-service/outputs.tf b/scenarios/shared/terraform-modules/app-service/outputs.tf index f3719fb9..9e3ba6c8 100644 --- a/scenarios/shared/terraform-modules/app-service/outputs.tf +++ b/scenarios/shared/terraform-modules/app-service/outputs.tf @@ -15,17 +15,17 @@ output "web_app_principal_id" { } output "web_app_slot_id" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_id : module.linux_web_app[0].web_app_slot_id : null + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_ids : module.linux_web_app[0].web_app_slot_ids : null } output "web_app_slot_name" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_name : module.linux_web_app[0].web_app_slot_name : null + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_names : module.linux_web_app[0].web_app_slot_names : null } output "web_app_slot_hostname" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_hostname : module.linux_web_app[0].web_app_slot_hostname : null + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_hostnames : module.linux_web_app[0].web_app_slot_hostnames : null } -output "web_app_slot_principal_id" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_principal_id : module.linux_web_app[0].web_app_slot_principal_id : null +output "web_app_slot_identities" { + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_identities : module.linux_web_app[0].web_app_slot_identities : null } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/variables.tf b/scenarios/shared/terraform-modules/app-service/variables.tf index 655dd2f3..e88f2bca 100644 --- a/scenarios/shared/terraform-modules/app-service/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/variables.tf @@ -42,20 +42,26 @@ variable "log_analytics_workspace_id" { description = "The log analytics workspace id" } +variable "app_service_environment_id" { + type = string + description = "The app service environment id" + default = null +} + variable "service_plan_options" { type = object({ - os_type = string - sku_name = string - app_service_environment_id = optional(string) - worker_count = optional(number) - zone_redundant = optional(bool) + os_type = string + sku_name = string + worker_count = optional(number) + zone_redundant = optional(bool) }) description = "The options for the app service" default = { - os_type = "Windows" - sku_name = "S1" + os_type = "Windows" + sku_name = "S1" + worker_count = 3 } validation { @@ -90,7 +96,12 @@ variable "identity" { variable "webapp_options" { type = object({ - slots = optional(list(string)) + slots = list(string) + instrumentation_key = optional(string) + ai_connection_string = optional(string) + vnet_route_all_enabled = optional(bool) + use_32_bit_worker = optional(bool) + vnet_integration_enabled = optional(bool) application_stack = optional(object({ current_stack = optional(string) # required for windows @@ -119,6 +130,8 @@ variable "webapp_options" { default = { slots = [] application_stack = {} + + } } diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md index 96077964..c8f66dbf 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md @@ -17,7 +17,6 @@ No requirements. | Name | Source | Version | |------|--------|---------| | [private\_endpoint](#module\_private\_endpoint) | ../../private-endpoint | n/a | -| [private\_endpoint\_slot](#module\_private\_endpoint\_slot) | ../../private-endpoint | n/a | ## Resources @@ -48,7 +47,7 @@ No requirements. | [service\_plan\_resource](#input\_service\_plan\_resource) | The service plan resource where the web application will be created | `any` | n/a | yes | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | | [web\_app\_name](#input\_web\_app\_name) | The name of the web application | `string` | n/a | yes | -| [webapp\_options](#input\_webapp\_options) | [Required] The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | +| [webapp\_options](#input\_webapp\_options) | [Required] The options for the app service |
object({
slots = list(string)
instrumentation_key = optional(string)
ai_connection_string = optional(string)
vnet_route_all_enabled = optional(bool)
use_32_bit_worker = optional(bool)
vnet_integration_enabled = optional(bool)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | ## Outputs @@ -58,8 +57,8 @@ No requirements. | [web\_app\_id](#output\_web\_app\_id) | n/a | | [web\_app\_name](#output\_web\_app\_name) | n/a | | [web\_app\_principal\_id](#output\_web\_app\_principal\_id) | n/a | -| [web\_app\_slot\_hostname](#output\_web\_app\_slot\_hostname) | n/a | -| [web\_app\_slot\_id](#output\_web\_app\_slot\_id) | n/a | -| [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | -| [web\_app\_slot\_principal\_id](#output\_web\_app\_slot\_principal\_id) | n/a | +| [web\_app\_slot\_hostnames](#output\_web\_app\_slot\_hostnames) | n/a | +| [web\_app\_slot\_identities](#output\_web\_app\_slot\_identities) | n/a | +| [web\_app\_slot\_ids](#output\_web\_app\_slot\_ids) | n/a | +| [web\_app\_slot\_names](#output\_web\_app\_slot\_names) | n/a | diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf index fd0c4d67..1d2da448 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf @@ -11,7 +11,7 @@ resource "azurecaf_name" "caf_name_winwebapp" { } resource "azurerm_windows_web_app" "this" { - name = var.web_app_name + name = azurecaf_name.caf_name_winwebapp.result resource_group_name = var.resource_group location = var.location https_only = true @@ -24,8 +24,8 @@ resource "azurerm_windows_web_app" "this" { } site_config { - vnet_route_all_enabled = true - use_32_bit_worker = false + vnet_route_all_enabled = coalesce(var.webapp_options.vnet_route_all_enabled, true) + use_32_bit_worker = coalesce(var.webapp_options.use_32_bit_worker, true) application_stack { current_stack = coalesce(var.webapp_options.application_stack.current_stack, "dotnet") @@ -126,17 +126,19 @@ module "private_endpoint" { } resource "azurerm_windows_web_app_slot" "slot" { - name = var.webapp_options.slots[0] + count = length(var.webapp_options.slots) + name = var.webapp_options.slots[count.index] app_service_id = azurerm_windows_web_app.this.id virtual_network_subnet_id = var.appsvc_subnet_id https_only = true identity { type = var.identity.type - identity_ids = var.identity.type == "SystemAssigned" ? [] : var.identity.identity_ids + identity_ids = var.identity.type == "SystemAssigned" ? null : var.identity.identity_ids } site_config { + vnet_route_all_enabled = true use_32_bit_worker = false @@ -150,25 +152,28 @@ resource "azurerm_windows_web_app_slot" "slot" { } resource "azurecaf_name" "slot" { - name = "${var.web_app_name}-${var.webapp_options.slots[0]}" + count = length(var.webapp_options.slots) + name = "${var.web_app_name}-${var.webapp_options.slots[count.index]}" resource_type = "azurerm_private_endpoint" } -module "private_endpoint_slot" { - source = "../../private-endpoint" +# module "private_endpoint_slot" { +# source = "../../private-endpoint" - name = azurecaf_name.slot.result - resource_group = var.resource_group - location = var.location - subnet_id = var.frontend_subnet_id - private_connection_resource_id = azurerm_windows_web_app.this.id +# count = length(var.webapp_options.slots) - subresource_names = length(var.webapp_options.slots) < 1 ? null : ["sites-${var.webapp_options.slots[0]}"] +# name = "${azurecaf_name.slot[count.index].result}-${azurerm_windows_web_app_slot.slot[count.index].name}" +# resource_group = var.resource_group +# location = var.location +# subnet_id = var.frontend_subnet_id +# private_connection_resource_id = azurerm_windows_web_app_slot.slot[count.index].id - private_dns_zone = var.private_dns_zone +# subresource_names = ["sites"] - private_dns_records = [ - lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot.name}"), - lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot.name}.scm") - ] -} \ No newline at end of file +# private_dns_zone = var.private_dns_zone + +# private_dns_records = [ +# lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot[count.index].name}"), +# lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot[count.index].name}.scm") +# ] +# } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf index 8f54e8fa..cc28ccd1 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf @@ -14,18 +14,18 @@ output "web_app_principal_id" { value = azurerm_windows_web_app.this.identity.0.principal_id } -output "web_app_slot_id" { - value = azurerm_windows_web_app_slot.slot.id +output "web_app_slot_ids" { + value = azurerm_windows_web_app_slot.slot.*.id } -output "web_app_slot_name" { - value = azurerm_windows_web_app_slot.slot.name +output "web_app_slot_names" { + value = azurerm_windows_web_app_slot.slot.*.name } -output "web_app_slot_hostname" { - value = azurerm_windows_web_app_slot.slot.default_hostname +output "web_app_slot_hostnames" { + value = azurerm_windows_web_app_slot.slot.*.default_hostname } -output "web_app_slot_principal_id" { - value = azurerm_windows_web_app_slot.slot.identity.0.principal_id +output "web_app_slot_identities" { + value = azurerm_windows_web_app_slot.slot.*.identity } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf index a2c9b1f2..d1167858 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf @@ -91,9 +91,12 @@ variable "service_plan_options" { variable "webapp_options" { type = object({ - instrumentation_key = string - ai_connection_string = string - slots = list(string) + slots = list(string) + instrumentation_key = optional(string) + ai_connection_string = optional(string) + vnet_route_all_enabled = optional(bool) + use_32_bit_worker = optional(bool) + vnet_integration_enabled = optional(bool) application_stack = object({ current_stack = string # required for windows diff --git a/scenarios/shared/terraform-modules/bastion/main.tf b/scenarios/shared/terraform-modules/bastion/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/bastion/main.tf +++ b/scenarios/shared/terraform-modules/bastion/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/README.md b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md index 208d236b..a390d479 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/README.md +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md @@ -1,12 +1,12 @@ # openai - + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.0 | +| [azurerm](#requirement\_azurerm) | >= 4.0 | ## Providers @@ -38,7 +38,7 @@ | [custom\_subdomain\_name](#input\_custom\_subdomain\_name) | The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix. | `string` | `null` | no | | [customer\_managed\_key](#input\_customer\_managed\_key) | type = object({
key\_vault\_key\_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account.
identity\_client\_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account.
}) |
object({
key_vault_key_id = string
identity_client_id = optional(string)
})
| `null` | no | | [default\_tags\_enabled](#input\_default\_tags\_enabled) | Determines whether or not default tags are applied to resources. If set to true, tags will be applied. If set to false, tags will not be applied. | `bool` | `false` | no | -| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model = {
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
}
scale = {
scale\_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created.
}
rai\_policy\_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
})) |
map(object({
name = string
model_format = string
model_name = string
model_version = string
scale_type = string
rai_policy_name = optional(string)
}))
| `{}` | no | +| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
sku\_name = (Required) The name of the SKU. Possible values are `Standard`, `GlobalBatch`, `GlobalStandard`, and `ProvisionedManaged
sku_tier = (Optional) The tier of the SKU. Possible values are `Free`, `Basic`, `Standard`, `Premium`, and `Enterprise`

rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
}))
` |
map(object({
name = string
model_format = string
model_name = string
model_version = string
rai_policy_name = optional(string)
sku_name = optional(string)
sku_tier = optional(string)
sku_size = optional(string)
sku_family = optional(string)
sku_capacity = optional(number)
}))
| n/a | yes | | [diagnostic\_setting](#input\_diagnostic\_setting) | A map of objects that represent the configuration for a diagnostic setting."
type = map(object({
name = (Required) Specifies the name of the diagnostic setting. Changing this forces a new resource to be created.
log\_analytics\_workspace\_id = (Optional) (Optional) Specifies the resource id of an Azure Log Analytics workspace where diagnostics data should be sent.
log\_analytics\_destination\_type = (Optional) Possible values are `AzureDiagnostics` and `Dedicated`. When set to Dedicated, logs sent to a Log Analytics workspace will go into resource specific tables, instead of the legacy `AzureDiagnostics` table.
eventhub\_name = (Optional) Specifies the name of the Event Hub where diagnostics data should be sent.
eventhub\_authorization\_rule\_id = (Optional) Specifies the resource id of an Event Hub Namespace Authorization Rule used to send diagnostics data.
storage\_account\_id = (Optional) Specifies the resource id of an Azure storage account where diagnostics data should be sent.
partner\_solution\_id = (Optional) The resource id of the market partner solution where diagnostics data should be sent. For potential partner integrations, click to learn more about partner integration.
audit\_log\_retention\_policy = (Optional) Specifies the retention policy for the audit log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
request\_response\_log\_retention\_policy = (Optional) Specifies the retention policy for the request response log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
trace\_log\_retention\_policy = (Optional) Specifies the retention policy for the trace log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
metric\_retention\_policy = (Optional) Specifies the retention policy for the metric. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
})) |
map(object({
name = string
log_analytics_workspace_id = optional(string)
log_analytics_destination_type = optional(string)
eventhub_name = optional(string)
eventhub_authorization_rule_id = optional(string)
storage_account_id = optional(string)
partner_solution_id = optional(string)
audit_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
request_response_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
trace_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
metric_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
}))
| `{}` | no | | [dynamic\_throttling\_enabled](#input\_dynamic\_throttling\_enabled) | Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled. | `bool` | `null` | no | | [environment](#input\_environment) | Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | @@ -68,4 +68,4 @@ | [openai\_primary\_key](#output\_openai\_primary\_key) | The primary access key for the Cognitive Service Account. | | [openai\_secondary\_key](#output\_openai\_secondary\_key) | The secondary access key for the Cognitive Service Account. | | [openai\_subdomain](#output\_openai\_subdomain) | The subdomain used to connect to the Cognitive Service Account. | - + diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf index 4a4ebfe0..222aa939 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.0" + version = ">= 4.0" } azurecaf = { source = "aztfmod/azurecaf" diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf index 75c67de7..27b94f1d 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf @@ -78,8 +78,13 @@ resource "azurerm_cognitive_deployment" "this" { name = each.value.model_name version = each.value.model_version } - scale { - type = each.value.scale_type + + sku { + name = each.value.sku_name + tier = each.value.sku_tier + size = coalesce(each.value.sku_size, null) + family = coalesce(each.value.sku_family, null) + capacity = coalesce(each.value.sku_capacity, null) } } diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf index d069301c..52670b7b 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf @@ -47,22 +47,24 @@ variable "deployment" { model_format = string model_name = string model_version = string - scale_type = string rai_policy_name = optional(string) + sku_name = optional(string) + sku_tier = optional(string) + sku_size = optional(string) + sku_family = optional(string) + sku_capacity = optional(number) })) - default = {} + description = <<-DESCRIPTION type = map(object({ name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created. cognitive_account_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created. - model = { - model_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI. - model_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. - model_version = (Required) The version of Cognitive Services Account Deployment model. - } - scale = { - scale_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created. - } + model_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI. + model_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. + model_version = (Required) The version of Cognitive Services Account Deployment model. + sku_name = (Required) The name of the SKU. Possible values are `Standard`, `GlobalBatch`, `GlobalStandard`, and `ProvisionedManaged + sku_tier = (Optional) The tier of the SKU. Possible values are `Free`, `Basic`, `Standard`, `Premium`, and `Enterprise` + rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created. })) DESCRIPTION diff --git a/scenarios/shared/terraform-modules/firewall/main.tf b/scenarios/shared/terraform-modules/firewall/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/firewall/main.tf +++ b/scenarios/shared/terraform-modules/firewall/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/frontdoor/main.tf b/scenarios/shared/terraform-modules/frontdoor/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/frontdoor/main.tf +++ b/scenarios/shared/terraform-modules/frontdoor/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/key-vault/README.md b/scenarios/shared/terraform-modules/key-vault/README.md index 423b3f13..355a8a0e 100644 --- a/scenarios/shared/terraform-modules/key-vault/README.md +++ b/scenarios/shared/terraform-modules/key-vault/README.md @@ -1,16 +1,18 @@ # key-vault - + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules @@ -44,7 +46,7 @@ No modules. | [secret\_reader\_identities](#input\_secret\_reader\_identities) | The list of identities that will be granted secret reader permissions | `list(string)` | n/a | yes | | [sku\_name](#input\_sku\_name) | The sku name for the app service plan | `string` | `"standard"` | no | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | -| [tenant\_id](#input\_tenant\_id) | The Microsoft Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [tenant\_id](#input\_tenant\_id) | The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | | [unique\_id](#input\_unique\_id) | The unique id | `string` | n/a | yes | ## Outputs @@ -53,4 +55,4 @@ No modules. |------|-------------| | [vault\_name](#output\_vault\_name) | n/a | | [vault\_uri](#output\_vault\_uri) | n/a | - + diff --git a/scenarios/shared/terraform-modules/key-vault/main.tf b/scenarios/shared/terraform-modules/key-vault/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/key-vault/main.tf +++ b/scenarios/shared/terraform-modules/key-vault/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/network/main.tf b/scenarios/shared/terraform-modules/network/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/network/main.tf +++ b/scenarios/shared/terraform-modules/network/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/openai/README.md b/scenarios/shared/terraform-modules/openai/README.md index 14258195..c4b942da 100644 --- a/scenarios/shared/terraform-modules/openai/README.md +++ b/scenarios/shared/terraform-modules/openai/README.md @@ -1,19 +1,19 @@ # openai - + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.0 | +| [azurerm](#requirement\_azurerm) | >= 4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.72.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules @@ -58,4 +58,4 @@ No modules. | [openai\_primary\_key](#output\_openai\_primary\_key) | The primary access key for the Cognitive Service Account. | | [openai\_secondary\_key](#output\_openai\_secondary\_key) | The secondary access key for the Cognitive Service Account. | | [openai\_subdomain](#output\_openai\_subdomain) | The subdomain used to connect to the Cognitive Service Account. | - + diff --git a/scenarios/shared/terraform-modules/openai/main.tf b/scenarios/shared/terraform-modules/openai/main.tf index 4a4ebfe0..222aa939 100644 --- a/scenarios/shared/terraform-modules/openai/main.tf +++ b/scenarios/shared/terraform-modules/openai/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.0" + version = ">= 4.0" } azurecaf = { source = "aztfmod/azurecaf" diff --git a/scenarios/shared/terraform-modules/private-dns-zone/main.tf b/scenarios/shared/terraform-modules/private-dns-zone/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/private-dns-zone/main.tf +++ b/scenarios/shared/terraform-modules/private-dns-zone/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/private-endpoint/main.tf b/scenarios/shared/terraform-modules/private-endpoint/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/main.tf +++ b/scenarios/shared/terraform-modules/private-endpoint/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/private-endpoint/module.tf b/scenarios/shared/terraform-modules/private-endpoint/module.tf index 31eb95e8..ec7901aa 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/module.tf +++ b/scenarios/shared/terraform-modules/private-endpoint/module.tf @@ -9,7 +9,7 @@ resource "azurerm_private_endpoint" "this" { name = var.name private_connection_resource_id = var.private_connection_resource_id - subresource_names = length(var.subresource_names) == 0 ? null : var.subresource_names + subresource_names = var.subresource_names is_manual_connection = false } diff --git a/scenarios/shared/terraform-modules/redis/README.md b/scenarios/shared/terraform-modules/redis/README.md index c21899e6..e9f94235 100644 --- a/scenarios/shared/terraform-modules/redis/README.md +++ b/scenarios/shared/terraform-modules/redis/README.md @@ -9,8 +9,8 @@ No requirements. | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/redis/main.tf b/scenarios/shared/terraform-modules/redis/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/redis/main.tf +++ b/scenarios/shared/terraform-modules/redis/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/redis/module.tf b/scenarios/shared/terraform-modules/redis/module.tf index 64dae234..70ff2d21 100644 --- a/scenarios/shared/terraform-modules/redis/module.tf +++ b/scenarios/shared/terraform-modules/redis/module.tf @@ -17,12 +17,12 @@ resource "azurerm_redis_cache" "this" { capacity = 2 family = "C" sku_name = var.sku_name - enable_non_ssl_port = false + non_ssl_port_enabled = false minimum_tls_version = "1.2" public_network_access_enabled = false redis_configuration { - enable_authentication = true + authentication_enabled = true } tags = local.tags diff --git a/scenarios/shared/terraform-modules/sql-database/main.tf b/scenarios/shared/terraform-modules/sql-database/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/sql-database/main.tf +++ b/scenarios/shared/terraform-modules/sql-database/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/user-defined-routes/README.md b/scenarios/shared/terraform-modules/user-defined-routes/README.md index ddf14097..237c721d 100644 --- a/scenarios/shared/terraform-modules/user-defined-routes/README.md +++ b/scenarios/shared/terraform-modules/user-defined-routes/README.md @@ -9,8 +9,8 @@ No requirements. | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/user-defined-routes/main.tf b/scenarios/shared/terraform-modules/user-defined-routes/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/user-defined-routes/main.tf +++ b/scenarios/shared/terraform-modules/user-defined-routes/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/user-defined-routes/module.tf b/scenarios/shared/terraform-modules/user-defined-routes/module.tf index 9084f52d..a8ff6d73 100644 --- a/scenarios/shared/terraform-modules/user-defined-routes/module.tf +++ b/scenarios/shared/terraform-modules/user-defined-routes/module.tf @@ -7,7 +7,7 @@ resource "azurerm_route_table" "this" { name = azurecaf_name.route_table.result location = var.location resource_group_name = var.resource_group - disable_bgp_route_propagation = false + bgp_route_propagation_enabled = false tags = local.tags } diff --git a/scenarios/shared/terraform-modules/windows-vm/README.md b/scenarios/shared/terraform-modules/windows-vm/README.md index ca0beaf8..467ad4ce 100644 --- a/scenarios/shared/terraform-modules/windows-vm/README.md +++ b/scenarios/shared/terraform-modules/windows-vm/README.md @@ -1,18 +1,20 @@ # windows-vm - + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azuread](#provider\_azuread) | 2.47.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.27 | -| [azurerm](#provider\_azurerm) | 3.92.0 | -| [random](#provider\_random) | 3.6.0 | +| [azuread](#provider\_azuread) | 3.0.2 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | +| [random](#provider\_random) | 3.6.3 | ## Modules @@ -62,4 +64,4 @@ No modules. | [principal\_id](#output\_principal\_id) | n/a | | [private\_ip\_address](#output\_private\_ip\_address) | n/a | | [vm\_key\_vault\_secret\_ids](#output\_vm\_key\_vault\_secret\_ids) | n/a | - + diff --git a/scenarios/shared/terraform-modules/windows-vm/main.tf b/scenarios/shared/terraform-modules/windows-vm/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/windows-vm/main.tf +++ b/scenarios/shared/terraform-modules/windows-vm/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } From 344d722812303f1aa377714251af1806e79e5d7f Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Mon, 14 Oct 2024 18:10:15 -0500 Subject: [PATCH 46/49] precommit --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e0692c30..2ed879dc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ repos: rev: v1.96.1 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases hooks: - id: terraform_fmt + ## Commenting out due to provider mismatch throwing false-positive errors on expected parameters # - id: terraform_validate # args: # - --tf-init-args=-upgrade From 0dc52237e81a0e010266819b0fd5373b3e5be405 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Mon, 14 Oct 2024 19:01:32 -0500 Subject: [PATCH 47/49] Resolved merge conflicts, tested tf local --- .../terraform/README.md | 12 ++--- .../terraform/backend.hcl | 4 ++ .../terraform/hub/backend.hcl | 4 ++ .../terraform/hub/variables.tf | 2 +- .../terraform/spoke/app.tf | 7 +-- .../terraform/spoke/backend.hcl | 4 ++ .../ase-multitenant.parameters.tfvars | 20 ++++---- .../terraform/spoke/variables.tf | 51 +++++++------------ 8 files changed, 49 insertions(+), 55 deletions(-) create mode 100644 scenarios/secure-baseline-multitenant/terraform/backend.hcl create mode 100644 scenarios/secure-baseline-multitenant/terraform/hub/backend.hcl create mode 100644 scenarios/secure-baseline-multitenant/terraform/spoke/backend.hcl diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index 5ce63a52..c1eb1069 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -1,4 +1,4 @@ -# Multi-tenant App Service Secure Baseline Terraform Implementation +# Multitenant App Service Secure Baseline Terraform Implementation ## Steps of Implementation for App Service Construction Set @@ -37,11 +37,11 @@ location = "swedencentral" location_short = "swe" tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -entra_admin_group_object_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -entra_admin_group_name = "Microsoft Entra ID SQL Admins" -vm_entra_admin_username = "bob@contoso.com" +aad_admin_group_object_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +aad_admin_group_name = "Microsoft Entra ID SQL Admins" +vm_aad_admin_username = "bob@contoso.com" -# Optionally provide non-Entra admin credentials for the VM +# Optionally provide non-AAD admin credentials for the VM # vm_admin_username = "daniem" # vm_admin_password = "**************" @@ -172,7 +172,7 @@ az upgrade az network bastion rdp --name bast-bastion --resource-group rg-hub --target-resource-id /subscriptions/{subscription-id}/resourceGroups/{rg-name}/providers/Microsoft.Compute/virtualMachines/{vm-name} --disable-gateway ``` -If you experience issues connecting to the DevOps VM using your Microsoft Entra ID credentials, see [Unable to connect to DevOps VM using Microsoft Entra ID credentials](#unable-to-connect-to-devops-vm-using-microsoft-entra-id-credentials) +If you experience issues connecting to the DevOps VM using your Microsoft Entra ID credentials, see [Unable to connect to DevOps VM using Microsoft Entra ID credentials](#unable-to-connect-to-devops-vm-using-aad-credentials) Once completed, you should be able to connect to the SQL Server using the Microsoft Entra ID account from SQL Server Management Studio. On the sample database (sample-db by default), run the following commands to create the user and grant minimal permissions (the exact command will be provided in the output of the Terraform deployment): diff --git a/scenarios/secure-baseline-multitenant/terraform/backend.hcl b/scenarios/secure-baseline-multitenant/terraform/backend.hcl new file mode 100644 index 00000000..6b12d55c --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/backend.hcl @@ -0,0 +1,4 @@ +resource_group_name = "backend-appsrvc-dev-westus2-001" +storage_account_name = "stbackendappsrwestus2001" +container_name = "tfstate" +key = "scenario1.localtest.tfstate" \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/backend.hcl b/scenarios/secure-baseline-multitenant/terraform/hub/backend.hcl new file mode 100644 index 00000000..bd866c5a --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/hub/backend.hcl @@ -0,0 +1,4 @@ +resource_group_name = "backend-appsrvc-dev-westus2-001" +storage_account_name = "stbackendappsrwestus2001" +container_name = "tfstate" +key = "scenario1.hub.tfstate" \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf b/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf index cb81a7e6..4174d3b4 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf @@ -22,7 +22,7 @@ variable "owner" { # variable "tenant_id" { # type = string -# description = "[Required] The Microsoft Entra tenant ID for the identities" +# description = "[Required] The Azure AD tenant ID for the identities" # } variable "tags" { diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf index f2387bd4..c575cba4 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf @@ -65,12 +65,7 @@ module "sql_database" { private_link_subnet_id = module.network.subnets["privateLink"].id global_settings = local.global_settings tags = local.base_tags - sql_databases = [ - { - name = "sample-db" - sku_name = "S0" - } - ] + sql_databases = var.sql_databases private_dns_zone = local.provisioned_dns_zones["privatelink.database.windows.net"] } diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/backend.hcl b/scenarios/secure-baseline-multitenant/terraform/spoke/backend.hcl new file mode 100644 index 00000000..918f9c0a --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/backend.hcl @@ -0,0 +1,4 @@ +resource_group_name = "backend-appsrvc-dev-westus2-001" +storage_account_name = "stbackendappsrwestus2001" +container_name = "tfstate" +key = "scenario1.spoke.tfstate" \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/parameters/ase-multitenant.parameters.tfvars b/scenarios/secure-baseline-multitenant/terraform/spoke/parameters/ase-multitenant.parameters.tfvars index 76d70cac..f33a8d0c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/parameters/ase-multitenant.parameters.tfvars +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/parameters/ase-multitenant.parameters.tfvars @@ -4,21 +4,23 @@ location = "westus3" owner = "cloudops@contoso.com" # For the hub state, use the same settings as the remote state config on the hub deployment from /hub/main.tf -hub_state_resource_group_name = "backend-appsrvc-dev-westus2-001" -hub_state_storage_account_name = "stbackendappsrwestus2001" -hub_state_container_name = "tfstate" -hub_state_key = "scenario1.hub.tfstate" +hub_remote_state_settings = { + storage_account_name = "stbackendappsrwestus2001" + container_name = "tfstate" + key = "scenario1.hub.tfstate" + resource_group_name = "backend-appsrvc-dev-westus2-001" +} entra_admin_group_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" -entra_admin_group_name = "AppSvcLZA Microsoft Entra SQL Admins" +entra_admin_group_name = "AppSvcLZA Azure AD SQL Admins" -## Lookup the Microsoft Entra User +## Lookup the Azure AD User # vm_entra_admin_username = "my-user@contoso.com" -## Reference an existing Microsoft Entra User/Group Object ID to bypass lookup -vm_entra_admin_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" # "AppSvcLZA Microsoft Entra SQL Admins" +## Reference an existing Azure AD User/Group Object ID to bypass lookup +vm_entra_admin_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" # "AppSvcLZA Azure AD SQL Admins" -## Optionally provide non-Entra ID admin credentials for the VM +## Optionally provide non-entra admin credentials for the VM # vm_admin_username = "daniem" # vm_admin_password = "**************" diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index 22ba07ea..b9b16e32 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -34,7 +34,7 @@ variable "location" { variable "tenant_id" { type = string - description = "The Microsoft Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant." + description = "The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant." default = null } @@ -47,43 +47,27 @@ variable "tags" { ##################################### # Spoke Resource Configuration Variables ##################################### -variable "entra_admin_group_object_id" { - type = string - description = "The object ID of the Microsoft Entra group that should be granted SQL Admin permissions to the SQL Server" +variable "hub_virtual_network" { + type = any + description = "[Required] Hub virtual network object that is live in Azure. Use either a data block or output of the `Hub` module (virtual_network) to provide this value" } -variable "entra_admin_group_name" { - type = string - description = "The name of the Microsoft Entra group that should be granted SQL Admin permissions to the SQL Server" +variable "firewall_private_ip" { + type = string } -variable "bastion_subnet_name" { - type = string - description = "[Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet'" - default = "AzureBastionSubnet" +variable "firewall_rules" { + type = any } -variable "firewall_subnet_name" { +variable "entra_admin_group_object_id" { type = string - description = "[Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet'" - default = "AzureFirewallSubnet" -} -variable "hub_vnet_cidr" { - type = list(string) - description = "[Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20" - default = ["10.242.0.0/20"] -} - -variable "firewall_subnet_cidr" { - type = list(string) - description = "[Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26" - default = ["10.242.0.0/26"] + description = "[Required] The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" } -variable "bastion_subnet_cidr" { - type = list(string) - description = "[Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26" - default = ["10.242.0.64/26"] +variable "entra_admin_group_name" { + type = string + description = "[Required] The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server" } variable "spoke_vnet_cidr" { @@ -125,24 +109,25 @@ variable "private_link_subnet_cidr" { variable "vm_admin_username" { type = string - description = "The username for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra admin account." + description = "[Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." default = null } variable "vm_admin_password" { type = string - description = "The password for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra admin account." + description = "[Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Azure AD admin account." default = null } variable "vm_entra_admin_username" { type = string - description = "[Optional] The Microsoft Entra username for the VM admin account. If vm_entra_admin_object_id is not specified, this value will be used." + description = "[Optional] The Azure AD username for the VM admin account. If vm_entra_admin_object_id is not specified, this value will be used." default = null } + variable "vm_entra_admin_object_id" { type = string - description = "The Microsoft Entra object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." + description = "[Optional] The Azure AD object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." default = null } From 8093f279f014ef3615825307efb25b7e5c7ea82f Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Mon, 14 Oct 2024 19:02:15 -0500 Subject: [PATCH 48/49] adding rest of changes --- .../secure-baseline-multitenant/.gitignore | 2 - .../secure-baseline-multitenant/README.md | 144 ------------------ .../secure-baseline-multitenant/azure.yaml | 9 -- 3 files changed, 155 deletions(-) delete mode 100644 scenarios/secure-baseline-multitenant/.gitignore delete mode 100644 scenarios/secure-baseline-multitenant/README.md delete mode 100644 scenarios/secure-baseline-multitenant/azure.yaml diff --git a/scenarios/secure-baseline-multitenant/.gitignore b/scenarios/secure-baseline-multitenant/.gitignore deleted file mode 100644 index d39999b9..00000000 --- a/scenarios/secure-baseline-multitenant/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.azure -backend.hcl \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/README.md b/scenarios/secure-baseline-multitenant/README.md deleted file mode 100644 index 5533c267..00000000 --- a/scenarios/secure-baseline-multitenant/README.md +++ /dev/null @@ -1,144 +0,0 @@ -# App Service Secure Baseline (Multi-tenant and ASE) - -This reference architecture shows how to run a web-app workload on Azure App Services in a secure configuration. This secure baseline follow [Defense in Depth](https://learn.microsoft.com/en-us/shows/azure-videos/defense-in-depth-security-in-azure) approach to protect AppService workload against cloud vulnerabilities along with additional [Well-Architected Framework](https://learn.microsoft.com/en-us/azure/architecture/framework/) pillars to enable a resilient solution. - -## Quick deployment to Azure - -You can deploy the current LZA directly in your azure subscription by hitting the button below or using Azure Dev CLI. - -### Deploy to Azure via Portal - -[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#view/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fappservice-landing-zone-accelerator%2Fmain%2Fscenarios%2Fsecure-baseline-multitenant%2Fazure-resource-manager%2Fmain.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fappservice-landing-zone-accelerator%2Fmain%2Fscenarios%2Fsecure-baseline-multitenant%2Fazure-resource-manager%2Fmain-portal-ux.json) - -### Using Codespaces via Azure Dev CLI - -- Visit [github.com/Azure/appservice-landing-zone-accelerator](https://github.com/Azure/appservice-landing-zone-accelerator) -- Click on the `Green Code` button. -- Navigate to the `CodeSpaces` tab and create a new code space. -- Open the terminal by pressing ```Ctrl + ` ```. -- Navigate to the scenario folder using the command `cd /workspaces/appservice-landing-zone-accelerator/scenarios/secure-baseline-multitenant`. -- Login to Azure using the command `azd auth login`. -- Use the command `azd up` to deploy, provide environment name and subscription to deploy to. -- Finally, use the command `azd down` to clean up resources deployed. - -# Architecture - -![image](/docs/Images/home-page.gif) - -See: [Multitenant](/docs/Images/Multitenant/AppServiceLandingZoneArchitecture-multitenant.png) | [ASE](/docs/Images/Multitenant/AppServiceLandingZoneArchitecture-ASE.png) | [Visio](/docs/App-Service-LZA.vsdx) - -## Core architecture components - -- The application's users are authenticated by [Microsoft Entra ID](https://azure.microsoft.com/services/active-directory/) or [Microsoft Entra ID B2C](https://azure.microsoft.com/services/active-directory/external-identities/b2c/). The browser performs DNS lookups to resolve addresses to Azure Front Door. -- [Virtual Network](https://learn.microsoft.com/en-us/azure/virtual-network/network-security-group-how-it-works) enables Azure resources to securely communicate with each other, the internet, and on-premises networks by creating boundaries, isolation and segmentation of your workloads in the cloud, much like a physical network. -- [Network Security Group](https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview?toc=%2Fazure%2Fnetworking%2Ffundamentals%2Ftoc.json) is a set of security policies that Allow or Deny Inbound/Outbound traffic (Protocols/Ports). -- [Azure Front Door](https://azure.microsoft.com/services/frontdoor/) is a public front-end for all internet requests, acting as a global HTTP reverse proxy and cache in front of several Azure services. Front Door also provides automatic protection from layer 3 and 4 DDoS attacks, and a range of other features including WAF (web application firewall), caching, and custom rules to enhance the security and performance of your application. -- [Azure App Service (Premium)](https://azure.microsoft.com/services/app-service/) hosts the front-end API applications that are called by the app. Deployment slots are used to provide zero-downtime releases. -- App Services use [Virtual Network (VNet) Integration](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration#regional-virtual-network-integration) to connect to backend Azure services over a private VNet. -- [Azure Cache for Redis](https://azure.microsoft.com/services/cache/) provides a high-performance distributed cache for output, session, and general-purpose caching. -- [Azure SQL DB](https://azure.microsoft.com/en-us/products/azure-sql/database/) provides a fully managed relational database service for back-end application services. -- [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview) provides REST API access to OpenAI's powerful language models including the GPT-4, GPT-3.5-Turbo, and Embeddings model series. -- [Private Endpoints](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview) allow connections to Azure services from private VNets, and allow the public endpoints on these services to be disabled. -- [Azure private DNS](https://learn.microsoft.com/en-us/azure/dns/private-dns-overview) automatically configures and updates the DNS records required by private endpoint services. -- [Azure Key Vault](https://azure.microsoft.com/services/key-vault/) securely stores secrets and certificates to be accessed by Azure services. -- [Azure Monitor](https://azure.microsoft.com/services/monitor/) and [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview) collect service logs and application performance metrics for observability. - -## Networking - -Network design topology is based on [Hub and Spoke](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/hub-spoke-network-topology) that allows to govern, secure and route traffic in a granular mode. - -Private endpoints are used throughout this architecture to improve security. While private endpoints don't directly improve, or reduce, the availability of this solution, they allow important security principles to be applied. For more information about security design principles, see [Azure well architected framework - Security pillar](https://learn.microsoft.com/en-us/azure/architecture/framework/security/security-principles). - -Network segmentation boundaries are established along public and private lines. Azure Front Door and Azure App Service are designed to operate on the public internet. These services have their public endpoints enabled. However, App Service has access restrictions in place to ensure that only traffic allowed by Front Door WAF (Web Application Firewall) is allowed to ingress into the App Service. - -Azure services that don't require access from the public internet have private endpoints enabled and public endpoints disabled. The Azure data services SQL DB, SQL DB and Azure Cache for Redis all have public endpoints disabled. Each private endpoint is deployed into one subnet that is dedicate to integrated private link services. Azure service firewalls are used to only allow traffic from other authorized Azure services. Private DNS zones are linked to each private endpoint, via private DNS zone groups and virtual network links, to ensure that private link DNS records are automatically created and updated. - -For network and subnet topology details, see the [Azure sample template](https://github.com/Azure-Samples/highly-available-zone-redundant-webapp) for this architecture. - -## Alternatives - -- Either Microsoft Entra ID or Microsoft Entra ID B2C can be used as an identity provider in this scenario. Microsoft Entra ID is designed for internal applications and business-to-business (B2B) scenarios, while Microsoft Entra ID B2C is designed for business-to-consumer (B2C) scenarios. -- You can choose to bring your own DNS provider or use Azure-managed DNS, which is recommended. -- Azure Application Gateway can be used solely instead of Azure Front Door when most users are located close to the Azure region that hosts your workload, and when content caching isn't required. Azure DDoS Network Protection is recommended for protecting internet-facing Application Gateway services. - -## Scenario details - -The scenario describes a secure baseline that allows you to have a protect environment and a good starting point for designing your solution. -Defense in depth is a security strategy that involves implementing multiple layers of defense at different points within a network or system. The idea is that if one layer of defense is breached, the next layer will be able to prevent an attacker from gaining access to sensitive information or critical systems. -This approach is a key point that drives the architecture decisions -> - -- Use isolated network layers for the different components. -- Use protected AD based access via Managed Identity (where possible). -- Use private endpoints for Azure services. -- Use Network Security Groups to control inbound and outbound traffic in the subnet level. -- Enable Standard DDoS Protection for the SPOKE VNET. - -## Potential use cases - -- Public website hosting -- Intranet portal -- Mobile app hosting -- E-commerce -- Media streaming -- Machine learning workloads - -## Recommendations - -- Private endpoints are mostly available on Premium Azure service SKUs. Private endpoints incur hourly and bandwidth (data) charges. For more information, see [Private Link pricing](https://azure.microsoft.com/en-us/pricing/details/private-link/). -- Govern your access and follow [Role Based Access Control](https://learn.microsoft.com/en-us/azure/role-based-access-control/overview) to have a fine-grained access management to Azure resources. -- Review your data classification and determine the protection level you have to enforce in terms of Encryption, Protection, Access and Detection. - -### Front Door -Azure Front Door is a global service, always available across all Azure geographies and resilient to zone-wide outages and region-wide outages. - -- Use [Azure managed certificates](https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/how-to-configure-https-custom-domain#azure-managed-certificates) on all front ends to prevent certificate mis-configuration and expiration issues. -- Enable [caching](https://learn.microsoft.com/en-us/azure/frontdoor/front-door-caching?pivots=front-door-standard-premium) on routes where appropriate to improve availability. Front Door's cache distributes your content to the Azure PoP (point of presence) edge nodes. In addition to improving your performance, caching reduces the load on your origin servers. -- Deploy Azure Front Door Premium and configure a [WAF policy](https://learn.microsoft.com/en-us/azure/web-application-firewall/afds/afds-overview) with a Microsoft-managed ruleset. Apply the policy to all custom domains. Use Prevention mode to mitigate web attacks that might cause an origin service to become unavailable. -- Deployments with higher security requirements could also use [Private Link in Azure Front Door Premium](https://learn.microsoft.com/en-us/azure/frontdoor/private-link) to secure connectivity to Azure App Service. -For more recommendations and information, see [Best practices for Front Door](https://learn.microsoft.com/en-us/azure/frontdoor/best-practices). - -### App Service - -- Access restrictions on Azure App Service should be configured to only allow Front Door traffic. Access restrictions ensure that requests aren't able to bypass the Azure Front Door WAF, see [App Service access restrictions](https://learn.microsoft.com/en-us/azure/app-service/app-service-ip-restrictions#restrict-access-to-a-specific-azure-front-door-instance). -- All service-to-service communication in Azure is TLS (transport layer security) encrypted by default. Azure Front Door and Azure App Services should be configured to accept HTTPS traffic only, and the minimum TLS version set. -- Managed identities are used for authenticating Azure service-to-service communication, where available. For more information about managed identities, see [What are managed identities for Azure resources?](https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview) - -Check out [Defender for App Service](https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-app-service-introduction) for secure and detect operations to protect your Azure App Service web apps and APIs. - -### SQL Database - -- Get to know the risks and [Common SQL threats](https://learn.microsoft.com/en-us/sql/relational-databases/security/sql-server-security-best-practices?view=sql-server-ver16#common-sql-threats) and plan how to [Protect](https://learn.microsoft.com/en-us/security/benchmark/azure/baselines/sql-database-security-baseline?toc=%2Fazure%2Fazure-sql%2Ftoc.json&view=azuresql). -- Define your encryption policy - you either use a Microsoft managed key or [BYOK](https://learn.microsoft.com/en-us/azure/azure-sql/database/transparent-data-encryption-byok-overview?view=azuresql). -- Verify your needs to protect and [detect](https://learn.microsoft.com/en-us/azure/azure-sql/database/threat-detection-configure?view=azuresql) any malfunction activity within your environment -- Check out [Defender for Azure SQL](https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-sql-introduction) to improve your vulnerabilities assessment and threat protection processes. - -For more recommendations and information, see [Azure SQL Security Baseline](https://learn.microsoft.com/en-us/security/benchmark/azure/baselines/sql-database-security-baseline?toc=%2Fazure%2Fazure-sql%2Ftoc.json&view=azuresql) - -### Cache for Redis - -- Set the publicNetworkAccess flag to Disabled to disable the public endpoint. -- To connect to a clustered cache, there can only be one private endpoint connection. - -For more recommendations and information, see [Azure Redis Cache Security Baseline](https://learn.microsoft.com/en-us/security/benchmark/azure/baselines/azure-cache-for-redis-security-baseline) - -## Deploy this scenario - -Deploy this reference architecture using this [Azure sample on GitHub](/scenarios/secure-baseline-multitenant/README.md). - -- Microsoft Entra ID, Microsoft Entra ID B2C, and Azure DNS aren't deployed by this sample. -- Custom domain names and TLS/SSL certificates aren't created and configured. Default frontend DNS names are used instead. -- The scripts are modular so you if you already have an existing environment, you can pick and choose the relevant section or adjust the relevant pieces according to your needs (deploy only SPOKE, replace SQL DB with PostgreSQL and etc.). - -## Considerations for Azure Government cloud - -Azure Front Door Premium is not available in Azure Government cloud. The reference implementation will deploy an Azure Application Gateway instead. - -## Next - -Pick one of the IaC options below and follow the instructions to deploy the App Service reference implementation. - -:arrow_forward: [Terraform](./terraform/README.md) - -:arrow_forward: [Bicep](./bicep/README.md) - -:arrow_forward: [ARM](./azure-resource-manager/README.md) diff --git a/scenarios/secure-baseline-multitenant/azure.yaml b/scenarios/secure-baseline-multitenant/azure.yaml deleted file mode 100644 index 6625b882..00000000 --- a/scenarios/secure-baseline-multitenant/azure.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json - -name: secure-baseline-multitenant -metadata: - template: LZA-app-service-secure-baseline-multitenant@0.0.1-beta -infra: - provider: "bicep" - path: "bicep" - module: "main" From 4d89652476a1c09a434daf6a42ad2a2ccb3cf128 Mon Sep 17 00:00:00 2001 From: Jin Lee Date: Mon, 21 Oct 2024 10:56:46 -0500 Subject: [PATCH 49/49] minor cleanup for gha --- .github/workflows/.template.bicep.yml | 4 ++-- .github/workflows/.template.terraform.yml | 2 +- .../terraform-modules/sql-database/README.md | 18 ++++++++++-------- .../sql-database/variables.tf | 6 ++++-- .../terraform-modules/windows-vm-ext/README.md | 8 ++++---- .../terraform-modules/windows-vm-ext/main.tf | 4 ++-- 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/.github/workflows/.template.bicep.yml b/.github/workflows/.template.bicep.yml index f6e0e979..076bf5a1 100644 --- a/.github/workflows/.template.bicep.yml +++ b/.github/workflows/.template.bicep.yml @@ -46,7 +46,7 @@ jobs: uses: actions/checkout@v4 # Log into Azure via OIDC - - uses: azure/login@v1 + - uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -74,7 +74,7 @@ jobs: uses: actions/checkout@main # Log into Azure via OIDC - - uses: azure/login@v1 + - uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} diff --git a/.github/workflows/.template.terraform.yml b/.github/workflows/.template.terraform.yml index 8afc1580..0b2ee65c 100644 --- a/.github/workflows/.template.terraform.yml +++ b/.github/workflows/.template.terraform.yml @@ -7,7 +7,7 @@ on: type: string description: 'Terraform version' required: true - default: '1.3.9' + default: '1.9.7' modulePath: type: string description: 'Path to the Terraform module' diff --git a/scenarios/shared/terraform-modules/sql-database/README.md b/scenarios/shared/terraform-modules/sql-database/README.md index 5e82cdc3..018f0719 100644 --- a/scenarios/shared/terraform-modules/sql-database/README.md +++ b/scenarios/shared/terraform-modules/sql-database/README.md @@ -1,17 +1,19 @@ # sql-database - + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azuread](#provider\_azuread) | 2.47.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.27 | -| [azurerm](#provider\_azurerm) | 3.92.0 | +| [azuread](#provider\_azuread) | 3.0.2 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules @@ -34,8 +36,8 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [application\_name](#input\_application\_name) | The name of your application | `string` | n/a | yes | -| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | n/a | `string` | n/a | yes | -| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | n/a | `string` | n/a | yes | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | n/a | `string` | `null` | no | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | n/a | `string` | `null` | no | | [environment](#input\_environment) | The environment (dev, test, prod...) | `string` | `"dev"` | no | | [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | @@ -54,4 +56,4 @@ No modules. | [sql\_db\_connection\_string](#output\_sql\_db\_connection\_string) | n/a | | [sql\_db\_name](#output\_sql\_db\_name) | n/a | | [sql\_server\_name](#output\_sql\_server\_name) | n/a | - + diff --git a/scenarios/shared/terraform-modules/sql-database/variables.tf b/scenarios/shared/terraform-modules/sql-database/variables.tf index 26c4a181..cc5c2667 100644 --- a/scenarios/shared/terraform-modules/sql-database/variables.tf +++ b/scenarios/shared/terraform-modules/sql-database/variables.tf @@ -31,11 +31,13 @@ variable "tenant_id" { } variable "entra_admin_group_object_id" { - type = string + type = string + default = null } variable "entra_admin_group_name" { - type = string + type = string + default = null } variable "private_link_subnet_id" { diff --git a/scenarios/shared/terraform-modules/windows-vm-ext/README.md b/scenarios/shared/terraform-modules/windows-vm-ext/README.md index 55f0d3b0..02b3068f 100644 --- a/scenarios/shared/terraform-modules/windows-vm-ext/README.md +++ b/scenarios/shared/terraform-modules/windows-vm-ext/README.md @@ -1,6 +1,6 @@ # windows-vm-ext - + ## Requirements No requirements. @@ -9,7 +9,7 @@ No requirements. | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurerm](#provider\_azurerm) | n/a | ## Modules @@ -19,7 +19,7 @@ No modules. | Name | Type | |------|------| -| [azurerm_virtual_machine_extension.entra](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | +| [azurerm_virtual_machine_extension.aad](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | | [azurerm_virtual_machine_extension.install_ssms](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | ## Inputs @@ -36,4 +36,4 @@ No modules. ## Outputs No outputs. - + diff --git a/scenarios/shared/terraform-modules/windows-vm-ext/main.tf b/scenarios/shared/terraform-modules/windows-vm-ext/main.tf index 2d0bf743..86dc4d1c 100644 --- a/scenarios/shared/terraform-modules/windows-vm-ext/main.tf +++ b/scenarios/shared/terraform-modules/windows-vm-ext/main.tf @@ -1,5 +1,5 @@ -resource "azurerm_virtual_machine_extension" "entra" { - count = var.enable_microsoft_entra_join ? 1 : 0 +resource "azurerm_virtual_machine_extension" "aad" { + count = var.enable_azure_ad_join ? 1 : 0 name = "aad-login-for-windows" publisher = "Microsoft.Azure.ActiveDirectory"