targetScope = 'resourceGroup' @description('Short prefix used for every Azure resource.') @minLength(3) @maxLength(18) param deploymentPrefix string = 'sgu-lab' @description('Azure region for the virtual network, gateway, and VM.') param location string = resourceGroup().location @description('Windows Server VM administrator name. This must not be Administrator.') @minLength(1) @maxLength(20) param administratorUsername string @secure() @description('Windows Server VM administrator password.') param administratorPassword string @description('Windows Server computer name; Active Directory limits this to 15 characters.') @minLength(1) @maxLength(15) param computerName string = 'SGU-DC01' @description('VM size for the Windows Server 2025 domain controller.') param vmSize string = 'Standard_D2s_v5' @description('Address space assigned to the Azure virtual network.') param virtualNetworkAddressPrefix string = '10.77.0.0/16' @description('Subnet that contains the domain controller.') param domainControllerSubnetPrefix string = '10.77.0.0/24' @description('Reserved Azure VPN Gateway subnet. Use /27 or larger.') param gatewaySubnetPrefix string = '10.77.255.0/27' @description('Static private IP reserved on the Azure NIC for AD DS and DNS.') param domainControllerPrivateIp string = '10.77.0.4' @description('Point-to-site client pool. It must not overlap the VNet or local Hyper-V networks.') param vpnClientAddressPoolPrefix string = '172.30.0.0/24' @description('Name presented for the trusted P2S root certificate.') param p2sRootCertificateName string = 'SGU-P2S-Root' @description('Base64 DER bytes of the trusted P2S root certificate, without PEM markers.') param p2sRootCertificateData string @description('Optional public CIDR allowed to RDP to the VM public IP, for example 203.0.113.10/32. Leave empty to expose no management port.') param administratorSourceAddressPrefix string = '' var virtualNetworkName = '${deploymentPrefix}-vnet' var domainControllerSubnetName = 'DomainControllers' var gatewaySubnetName = 'GatewaySubnet' var networkSecurityGroupName = '${deploymentPrefix}-dc-nsg' var domainControllerPublicIpName = '${deploymentPrefix}-dc-pip' var gatewayPublicIpName = '${deploymentPrefix}-vpngw-pip' var networkInterfaceName = '${deploymentPrefix}-dc-nic' var virtualMachineName = '${deploymentPrefix}-dc' var virtualNetworkGatewayName = '${deploymentPrefix}-vpngw' resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2024-05-01' = { name: networkSecurityGroupName location: location properties: { securityRules: concat([ { name: 'Allow-SGU-P2S-clients' properties: { priority: 100 access: 'Allow' direction: 'Inbound' protocol: '*' sourcePortRange: '*' destinationPortRange: '*' sourceAddressPrefix: vpnClientAddressPoolPrefix destinationAddressPrefix: domainControllerPrivateIp description: 'AD, DNS, broker, monitoring, and RustDesk are reachable only through the authenticated P2S address pool.' } } ], empty(administratorSourceAddressPrefix) ? [] : [ { name: 'Allow-RDP-from-administrator' properties: { priority: 110 access: 'Allow' direction: 'Inbound' protocol: 'Tcp' sourcePortRange: '*' destinationPortRange: '3389' sourceAddressPrefix: administratorSourceAddressPrefix destinationAddressPrefix: domainControllerPrivateIp description: 'Optional bootstrap-only RDP access from one explicitly supplied public CIDR.' } } ]) } } resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { name: virtualNetworkName location: location properties: { addressSpace: { addressPrefixes: [ virtualNetworkAddressPrefix ] } subnets: [ { name: domainControllerSubnetName properties: { addressPrefix: domainControllerSubnetPrefix networkSecurityGroup: { id: networkSecurityGroup.id } } } { name: gatewaySubnetName properties: { addressPrefix: gatewaySubnetPrefix } } ] } } resource domainControllerPublicIp 'Microsoft.Network/publicIPAddresses@2024-05-01' = { name: domainControllerPublicIpName location: location sku: { name: 'Standard' } properties: { publicIPAllocationMethod: 'Static' publicIPAddressVersion: 'IPv4' idleTimeoutInMinutes: 30 } } resource gatewayPublicIp 'Microsoft.Network/publicIPAddresses@2024-05-01' = { name: gatewayPublicIpName location: location sku: { name: 'Standard' } properties: { publicIPAllocationMethod: 'Static' publicIPAddressVersion: 'IPv4' } } resource networkInterface 'Microsoft.Network/networkInterfaces@2024-05-01' = { name: networkInterfaceName location: location properties: { enableAcceleratedNetworking: true dnsSettings: { dnsServers: [ domainControllerPrivateIp ] } ipConfigurations: [ { name: 'ipconfig1' properties: { privateIPAllocationMethod: 'Static' privateIPAddressVersion: 'IPv4' privateIPAddress: domainControllerPrivateIp subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetworkName, domainControllerSubnetName) } publicIPAddress: { id: domainControllerPublicIp.id } } } ] } dependsOn: [ virtualNetwork ] } resource virtualMachine 'Microsoft.Compute/virtualMachines@2024-07-01' = { name: virtualMachineName location: location identity: { type: 'SystemAssigned' } properties: { hardwareProfile: { vmSize: vmSize } securityProfile: { securityType: 'TrustedLaunch' uefiSettings: { secureBootEnabled: true vTpmEnabled: true } } osProfile: { computerName: computerName adminUsername: administratorUsername adminPassword: administratorPassword windowsConfiguration: { provisionVMAgent: true enableAutomaticUpdates: true patchSettings: { patchMode: 'AutomaticByPlatform' assessmentMode: 'AutomaticByPlatform' enableHotpatching: false } } } storageProfile: { imageReference: { publisher: 'MicrosoftWindowsServer' offer: 'WindowsServer' sku: '2025-datacenter-azure-edition' version: 'latest' } osDisk: { createOption: 'FromImage' managedDisk: { storageAccountType: 'Premium_LRS' } deleteOption: 'Delete' } } networkProfile: { networkInterfaces: [ { id: networkInterface.id properties: { primary: true deleteOption: 'Delete' } } ] } diagnosticsProfile: { bootDiagnostics: { enabled: true } } } } resource virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2024-05-01' = { name: virtualNetworkGatewayName location: location properties: { gatewayType: 'Vpn' vpnType: 'RouteBased' activeActive: false enableBgp: false ipConfigurations: [ { name: 'gateway-ipconfig' properties: { privateIPAllocationMethod: 'Dynamic' subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetworkName, gatewaySubnetName) } publicIPAddress: { id: gatewayPublicIp.id } } } ] sku: { name: 'VpnGw1' tier: 'VpnGw1' } vpnClientConfiguration: { vpnClientAddressPool: { addressPrefixes: [ vpnClientAddressPoolPrefix ] } vpnClientProtocols: [ 'IkeV2' 'SSTP' ] vpnAuthenticationTypes: [ 'Certificate' ] vpnClientRootCertificates: [ { name: p2sRootCertificateName properties: { publicCertData: p2sRootCertificateData } } ] } } dependsOn: [ virtualNetwork ] } output domainControllerName string = virtualMachine.name output domainControllerPrivateIp string = domainControllerPrivateIp output domainControllerPublicIp string = domainControllerPublicIp.properties.ipAddress output virtualNetworkName string = virtualNetwork.name output virtualNetworkAddressPrefix string = virtualNetworkAddressPrefix output vpnGatewayName string = virtualNetworkGateway.name output vpnClientAddressPoolPrefix string = vpnClientAddressPoolPrefix output serverBootstrapArguments array = [ '-ServerIPv4Address' domainControllerPrivateIp '-PrefixLength' last(split(domainControllerSubnetPrefix, '/')) '-NetworkConfigurationMode' 'PlatformManaged' '-TrustedClientNetworks' vpnClientAddressPoolPrefix '-DnsForwarders' '168.63.129.16' ]