diff --git a/solutions/secure-hybrid-network/images/dmz-private.png b/solutions/secure-hybrid-network/images/dmz-private.png index 4dcb7b35..ee1b2393 100644 Binary files a/solutions/secure-hybrid-network/images/dmz-private.png and b/solutions/secure-hybrid-network/images/dmz-private.png differ diff --git a/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.bicep b/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.bicep index 31837d6d..5d08c7c8 100644 --- a/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.bicep +++ b/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.bicep @@ -4,7 +4,7 @@ param adminUserName string = 'azureadmin' param adminPassword string @description('The count of Windows virtual machines to create.') -param windowsVMCount int = 2 +param webServerInstanceCount int = 2 param vmSize string = 'Standard_A4_v2' param configureSitetosite bool = true param hubNetwork object = { @@ -54,8 +54,7 @@ param internalLoadBalancer object = { param location string = resourceGroup().location var logAnalyticsWorkspaceName = 'la-${uniqueString(subscription().subscriptionId, resourceGroup().id)}' -var nicNameWebName = 'nic-web-server' -var vmNameWebName = 'vm-web-server' +var vmssName = 'vmss-web-server' var windowsOSVersion = '2016-Datacenter' resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2025-02-01' = { @@ -318,6 +317,35 @@ resource spokeNetworkResource 'Microsoft.Network/virtualNetworks@2024-05-01' = { } } +resource hubToSpokePeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-05-01' = { + parent: hubNetworkResource + name: 'hub-to-spoke' + properties: { + remoteVirtualNetwork: { + id: spokeNetworkResource.id + } + allowForwardedTraffic: true + allowGatewayTransit: true + allowVirtualNetworkAccess: true + } +} + +resource spokeToHubPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-05-01' = { + parent: spokeNetworkResource + name: 'spoke-to-hub' + dependsOn: [ + vpnGatewayResource + ] + properties: { + remoteVirtualNetwork: { + id: hubNetworkResource.id + } + allowForwardedTraffic: true + useRemoteGateways: configureSitetosite + allowVirtualNetworkAccess: true + } +} + resource bastionHost_nsgName_Microsoft_Insights_default_logAnalyticsWorkspace 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { scope: bastionHost_nsg name: logAnalyticsWorkspaceName @@ -394,6 +422,7 @@ resource vpnGateway_publicIPAddress 'Microsoft.Network/publicIPAddresses@2024-05 name: 'Standard' tier: 'Regional' } + zones: pickZones('Microsoft.Network', 'publicIPAddresses', location, 3) properties: { publicIPAllocationMethod: 'Static' } @@ -418,8 +447,8 @@ resource vpnGatewayResource 'Microsoft.Network/virtualNetworkGateways@2024-05-01 } ] sku: { - name: 'VpnGw2' - tier: 'VpnGw2' + name: 'VpnGw2AZ' + tier: 'VpnGw2AZ' } gatewayType: 'Vpn' vpnType: 'RouteBased' @@ -667,111 +696,112 @@ resource spokeRoutes_tableName_spokeRoutes_routeNameFirewall 'Microsoft.Network/ } } -resource nicNameWeb 'Microsoft.Network/networkInterfaces@2024-05-01' = [for i in range(0, windowsVMCount): { - name: '${nicNameWebName}${i}' - location: location - properties: { - ipConfigurations: [ - { - name: 'ipconfig' - properties: { - privateIPAllocationMethod: 'Dynamic' - subnet: { - id: resourceId('Microsoft.Network/virtualNetworks/subnets', spokeNetworkResource.name, spokeNetwork.subnetName) - } - loadBalancerBackendAddressPools: [ - { - id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', internalLoadBalancer.name, internalLoadBalancer.backendName) - } - ] - } - } - ] - } - dependsOn:[ - internalLoadBalancerResource] -}] - -resource vmNameWeb 'Microsoft.Compute/virtualMachines@2024-11-01' = [for i in range(0, windowsVMCount): { - name: '${vmNameWebName}${i}' +resource vmssWeb 'Microsoft.Compute/virtualMachineScaleSets@2024-11-01' = { + name: vmssName location: location identity: { - // It is required by the Guest Configuration extension. + // Required by the Guest Configuration extension. type: 'SystemAssigned' } + sku: { + name: vmSize + tier: 'Standard' + capacity: webServerInstanceCount + } properties: { - hardwareProfile: { - vmSize: vmSize + orchestrationMode: 'Uniform' + overprovision: false + // Manual upgrade policy keeps instance upgrades explicit. Use 'Rolling' with a + // health probe for automated rollouts in production workloads. + upgradePolicy: { + mode: 'Manual' } - osProfile: { - computerName: '${vmNameWebName}${i}' - adminUsername: adminUserName - adminPassword: adminPassword - windowsConfiguration: { - enableAutomaticUpdates: true - patchSettings: { - //Machines should be configured to periodically check for missing system updates - assessmentMode: 'AutomaticByPlatform' - patchMode: 'AutomaticByPlatform' + virtualMachineProfile: { + osProfile: { + computerNamePrefix: 'websvr' + adminUsername: adminUserName + adminPassword: adminPassword + windowsConfiguration: { + enableAutomaticUpdates: true } } - } - storageProfile: { - imageReference: { - publisher: 'MicrosoftWindowsServer' - offer: 'WindowsServer' - sku: windowsOSVersion - version: 'latest' + storageProfile: { + imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: windowsOSVersion + version: 'latest' + } + osDisk: { + createOption: 'FromImage' + } } - osDisk: { - createOption: 'FromImage' + networkProfile: { + networkInterfaceConfigurations: [ + { + name: '${vmssName}-nic' + properties: { + primary: true + ipConfigurations: [ + { + name: 'ipconfig' + properties: { + primary: true + subnet: { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', spokeNetworkResource.name, spokeNetwork.subnetName) + } + loadBalancerBackendAddressPools: [ + { + id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', internalLoadBalancer.name, internalLoadBalancer.backendName) + } + ] + } + } + ] + } + } + ] + } + securityProfile: { + // We recommend enabling encryption at host for virtual machines and virtual machine scale sets to harden security. + encryptionAtHost: false + } + extensionProfile: { + extensions: [ + { + name: 'installIIS' + properties: { + publisher: 'Microsoft.Compute' + type: 'CustomScriptExtension' + typeHandlerVersion: '1.7' + autoUpgradeMinorVersion: true + settings: { + commandToExecute: 'powershell.exe Install-WindowsFeature -name Web-Server -IncludeManagementTools' + } + } + } + { + name: 'AzurePolicyforWindows' + properties: { + publisher: 'Microsoft.GuestConfiguration' + type: 'ConfigurationforWindows' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true + settings: {} + protectedSettings: {} + } + } + ] } } - networkProfile: { - networkInterfaces: [ - { - id: nicNameWeb[i].id - } - ] - } - securityProfile: { - // We recommend enabling encryption at host for virtual machines and virtual machine scale sets to harden security. - encryptionAtHost: false - } - }}] - -resource vmNameWeb_installIIS 'Microsoft.Compute/virtualMachines/extensions@2024-11-01' = [for i in range(0, windowsVMCount): { - parent: vmNameWeb [i] - name: 'installIIS' - location: location - properties: { - publisher: 'Microsoft.Compute' - type: 'CustomScriptExtension' - typeHandlerVersion: '1.7' - autoUpgradeMinorVersion: true - settings: { - commandToExecute: 'powershell.exe Install-WindowsFeature -name Web-Server -IncludeManagementTools' - } - } -}] - -resource guestConfigExtensionWindows 'Microsoft.Compute/virtualMachines/extensions@2024-11-01' = [for i in range(0, windowsVMCount): { - parent: vmNameWeb[i] - name: 'AzurePolicyforWindows${vmNameWeb[i].name}' - location: location - properties: { - publisher: 'Microsoft.GuestConfiguration' - type: 'ConfigurationforWindows' - typeHandlerVersion: '1.0' - autoUpgradeMinorVersion: true - enableAutomaticUpgrade: true - settings: {} - protectedSettings: {} - } } -] + dependsOn: [ + internalLoadBalancerResource + ] +} -output vpnIp string = vpnGatewayResource.properties.bgpSettings.bgpPeeringAddresses[0].tunnelIpAddresses[0] +output vpnIp string = vpnGatewayResource!.properties.bgpSettings.bgpPeeringAddresses[0].tunnelIpAddresses[0] output mocOnpremNetwork string = hubNetwork.addressPrefix output spokeNetworkAddressPrefix string = spokeNetwork.addressPrefix output azureGatewayName string = vpnGateway.name diff --git a/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.json b/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.json index 57784085..ac009c00 100644 --- a/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.json +++ b/solutions/secure-hybrid-network/nestedtemplates/azure-network-azuredeploy.json @@ -98,10 +98,9 @@ }, "variables": { "logAnalyticsWorkspace": "[concat('la-', uniqueString(subscription().subscriptionId, resourceGroup().id))]", + "vmssName": "vmss-web-server", "peering-name-hub-to-spoke": "hub-to-spoke", "peering-name-spoke-to-hub": "spoke-to-hub", - "nicNameWeb": "nic-web-server", - "vmNameWeb": "vm-web-server", "windowsOSVersion": "2022-datacenter-g2" }, "resources": [ @@ -734,98 +733,106 @@ ] }, { - "type": "Microsoft.Network/networkInterfaces", - "apiVersion": "2020-05-01", - "name": "[concat(variables('nicNameWeb'), copyIndex())]", + "type": "Microsoft.Compute/virtualMachineScaleSets", + "apiVersion": "2024-11-01", + "name": "[variables('vmssName')]", "location": "[parameters('location')]", "dependsOn": [ "[resourceId('Microsoft.Network/virtualNetworks', parameters('spokeNetwork').name)]", "[resourceId('Microsoft.Network/loadBalancers', parameters('internalLoadBalancer').name)]" ], - "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('spokeNetwork').name, parameters('spokeNetwork').subnetName)]" - }, - "loadBalancerBackendAddressPools": [ - { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('internalLoadBalancer').name, parameters('internalLoadBalancer').backendName)]" - } - ] - } - } - ] + "identity": { + "type": "SystemAssigned" + }, + "sku": { + "name": "[parameters('vmSize')]", + "tier": "Standard", + "capacity": "[parameters('windowsVMCount')]" }, - "copy": { - "name": "niccopy", - "count": "[parameters('windowsVMCount')]" - } - }, - { - "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2019-07-01", - "name": "[concat(variables('vmNameWeb'), copyIndex())]", - "location": "[parameters('location')]", - "dependsOn": [ - "[concat(variables('nicNameWeb'), copyIndex())]" - ], "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" + "orchestrationMode": "Uniform", + "overprovision": false, + "upgradePolicy": { + "mode": "Manual" }, - "osProfile": { - "computerName": "[concat(variables('vmNameWeb'), copyIndex())]", - "adminUsername": "[parameters('adminUsername')]", - "adminPassword": "[parameters('adminPassword')]" - }, - "storageProfile": { - "imageReference": { - "publisher": "MicrosoftWindowsServer", - "offer": "WindowsServer", - "sku": "[variables('windowsOSVersion')]", - "version": "latest" - }, - "osDisk": { - "createOption": "FromImage" - } - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('nicNameWeb'), copyIndex()))]" + "virtualMachineProfile": { + "osProfile": { + "computerNamePrefix": "websvr", + "adminUsername": "[parameters('adminUsername')]", + "adminPassword": "[parameters('adminPassword')]", + "windowsConfiguration": { + "enableAutomaticUpdates": true } - ] - } - }, - "resources": [ - { - - "type": "extensions", - "apiVersion": "2019-12-01", - "name": "installIIS", - "location": "[parameters('location')]", - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', concat(variables('vmNameWeb'), copyIndex()))]" - ], - "properties": { - "publisher": "Microsoft.Compute", - "type": "CustomScriptExtension", - "typeHandlerVersion": "1.7", - "autoUpgradeMinorVersion": true, - "settings": { - "commandToExecute": "powershell.exe Install-WindowsFeature -name Web-Server -IncludeManagementTools" + }, + "storageProfile": { + "imageReference": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "[variables('windowsOSVersion')]", + "version": "latest" + }, + "osDisk": { + "createOption": "FromImage" } + }, + "networkProfile": { + "networkInterfaceConfigurations": [ + { + "name": "[concat(variables('vmssName'), '-nic')]", + "properties": { + "primary": true, + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "primary": true, + "subnet": { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('spokeNetwork').name, parameters('spokeNetwork').subnetName)]" + }, + "loadBalancerBackendAddressPools": [ + { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('internalLoadBalancer').name, parameters('internalLoadBalancer').backendName)]" + } + ] + } + } + ] + } + } + ] + }, + "securityProfile": { + "encryptionAtHost": false + }, + "extensionProfile": { + "extensions": [ + { + "name": "installIIS", + "properties": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.7", + "autoUpgradeMinorVersion": true, + "settings": { + "commandToExecute": "powershell.exe Install-WindowsFeature -name Web-Server -IncludeManagementTools" + } + } + }, + { + "name": "AzurePolicyforWindows", + "properties": { + "publisher": "Microsoft.GuestConfiguration", + "type": "ConfigurationforWindows", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true, + "settings": {}, + "protectedSettings": {} + } + } + ] } - } - ], - "copy": { - "name": "vmcopy", - "count": "[parameters('windowsVMCount')]" } }, { diff --git a/solutions/secure-hybrid-network/nestedtemplates/azure-network-local-gateway.bicep b/solutions/secure-hybrid-network/nestedtemplates/azure-network-local-gateway.bicep index 7d760529..206f0c6d 100644 --- a/solutions/secure-hybrid-network/nestedtemplates/azure-network-local-gateway.bicep +++ b/solutions/secure-hybrid-network/nestedtemplates/azure-network-local-gateway.bicep @@ -28,9 +28,11 @@ resource connection 'Microsoft.Network/connections@2024-05-01' = { properties: { virtualNetworkGateway1: { id: resourceId('Microsoft.Network/virtualNetworkGateways', azureNetworkGatewayName) + properties: {} } localNetworkGateway2: { id: localNetworkGateway.id + properties: {} } connectionType: 'IPsec' connectionProtocol: 'IKEv2' diff --git a/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-azuredeploy.bicep b/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-azuredeploy.bicep index 2a80b697..d0c18ee5 100644 --- a/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-azuredeploy.bicep +++ b/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-azuredeploy.bicep @@ -70,6 +70,7 @@ resource mocOnpremGateway_publicIPAddress 'Microsoft.Network/publicIPAddresses@2 name: 'Standard' tier: 'Regional' } + zones: pickZones('Microsoft.Network', 'publicIPAddresses', location, 3) properties: { publicIPAllocationMethod: 'Static' } @@ -94,8 +95,8 @@ resource mocOnpremGatewayResource 'Microsoft.Network/virtualNetworkGateways@2024 } ] sku: { - name: 'VpnGw2' - tier: 'VpnGw2' + name: 'VpnGw2AZ' + tier: 'VpnGw2AZ' } gatewayType: 'Vpn' vpnType: 'RouteBased' @@ -329,6 +330,6 @@ resource guestConfigExtensionWindows 'Microsoft.Compute/virtualMachines/extensio } } -output vpnIp string = mocOnpremGatewayResource.properties.bgpSettings.bgpPeeringAddresses[0].tunnelIpAddresses[0] +output vpnIp string = mocOnpremGatewayResource!.properties.bgpSettings.bgpPeeringAddresses[0].tunnelIpAddresses[0] output mocOnpremNetworkPrefix string = mocOnpremNetwork.addressPrefix output mocOnpremGatewayName string = mocOnpremGateway.name diff --git a/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-local-gateway.bicep b/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-local-gateway.bicep index 61fca398..96bdcfa0 100644 --- a/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-local-gateway.bicep +++ b/solutions/secure-hybrid-network/nestedtemplates/mock-onprem-local-gateway.bicep @@ -30,9 +30,11 @@ resource connection 'Microsoft.Network/connections@2024-05-01' = { properties: { virtualNetworkGateway1: { id: resourceId('Microsoft.Network/virtualNetworkGateways', mocOnpremGatewayName) + properties: {} } localNetworkGateway2: { id: localNetworkGateway_resource.id + properties: {} } connectionType: 'IPsec' connectionProtocol: 'IKEv2'