快速入门:使用 Bicep 模板创建虚拟网络

本快速入门介绍如何创建包含两个虚拟机的虚拟网络,然后使用 Bicep 模板在虚拟网络上部署 Azure Bastion。 然后,使用 Bastion 安全地从 Internet 连接到虚拟机,并在 VM 之间开始私密通信。

虚拟网络是 Azure 中专用网络的基本构建块。 Azure 虚拟网络能让 Azure 资源(例如 VM)互相安全通信以及与 Internet 通信。

在虚拟网络快速入门中创建的资源的示意图。

Bicep 是一种特定于域的语言 (DSL),使用声明性语法来部署 Azure 资源。 它提供简明的语法、可靠的类型安全性以及对代码重用的支持。 Bicep 会针对你的 Azure 基础结构即代码解决方案提供最佳创作体验。

先决条件


创建虚拟网络和虚拟机

本快速入门使用 Azure 资源管理器快速启动模板中的 VNET 中的两个虚拟机 Bicep 模板来创建虚拟网络、资源子网和虚拟机。 Bicep 模板定义以下 Azure 资源:

查阅 Bicep 文件:

@description('Admin username')
param adminUsername string

@description('Admin password')
@secure()
param adminPassword string

@description('Prefix to use for VM names')
param vmNamePrefix string = 'BackendVM'

@description('Location for all resources.')
param location string = resourceGroup().location

@description('Size of the virtual machines')
param vmSize string = 'Standard_D2s_v3'

var availabilitySetName = 'AvSet'
var storageAccountType = 'Standard_LRS'
var storageAccountName = uniqueString(resourceGroup().id)
var virtualNetworkName = 'vNet'
var subnetName = 'backendSubnet'
var loadBalancerName = 'ilb'
var networkInterfaceName = 'nic'
var subnetRef = resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetworkName, subnetName)
var numberOfInstances = 2

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: storageAccountType
  }
  kind: 'StorageV2'
}

resource availabilitySet 'Microsoft.Compute/availabilitySets@2023-09-01' = {
  name: availabilitySetName
  location: location
  sku: {
    name: 'Aligned'
  }
  properties: {
    platformUpdateDomainCount: 2
    platformFaultDomainCount: 2
  }
}

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-09-01' = {
  name: virtualNetworkName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: subnetName
        properties: {
          addressPrefix: '10.0.2.0/24'
        }
      }
    ]
  }
}

resource networkInterface 'Microsoft.Network/networkInterfaces@2023-09-01' = [for i in range(0, numberOfInstances): {
  name: '${networkInterfaceName}${i}'
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          subnet: {
            id: subnetRef
          }
          loadBalancerBackendAddressPools: [
            {
              id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, 'BackendPool1')
            }
          ]
        }
      }
    ]
  }
  dependsOn: [
    virtualNetwork
    loadBalancer
  ]
}]

resource loadBalancer 'Microsoft.Network/loadBalancers@2023-09-01' = {
  name: loadBalancerName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    frontendIPConfigurations: [
      {
        properties: {
          subnet: {
            id: subnetRef
          }
          privateIPAddress: '10.0.2.6'
          privateIPAllocationMethod: 'Static'
        }
        name: 'LoadBalancerFrontend'
      }
    ]
    backendAddressPools: [
      {
        name: 'BackendPool1'
      }
    ]
    loadBalancingRules: [
      {
        properties: {
          frontendIPConfiguration: {
            id: resourceId('Microsoft.Network/loadBalancers/frontendIpConfigurations', loadBalancerName, 'LoadBalancerFrontend')
          }
          backendAddressPool: {
            id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, 'BackendPool1')
          }
          probe: {
            id: resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'lbprobe')
          }
          protocol: 'Tcp'
          frontendPort: 80
          backendPort: 80
          idleTimeoutInMinutes: 15
        }
        name: 'lbrule'
      }
    ]
    probes: [
      {
        properties: {
          protocol: 'Tcp'
          port: 80
          intervalInSeconds: 15
          numberOfProbes: 2
        }
        name: 'lbprobe'
      }
    ]
  }
  dependsOn: [
    virtualNetwork
  ]
}

resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = [for i in range(0, numberOfInstances): {
  name: '${vmNamePrefix}${i}'
  location: location
  properties: {
    availabilitySet: {
      id: availabilitySet.id
    }
    hardwareProfile: {
      vmSize: vmSize
    }
    osProfile: {
      computerName: '${vmNamePrefix}${i}'
      adminUsername: adminUsername
      adminPassword: adminPassword
    }
    storageProfile: {
      imageReference: {
        publisher: 'MicrosoftWindowsServer'
        offer: 'WindowsServer'
        sku: '2019-Datacenter'
        version: 'latest'
      }
      osDisk: {
        createOption: 'FromImage'
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: networkInterface[i].id
        }
      ]
    }
    diagnosticsProfile: {
      bootDiagnostics: {
        enabled: true
        storageUri: storageAccount.properties.primaryEndpoints.blob
      }
    }
  }
}]

output location string = location
output name string = loadBalancer.name
output resourceGroupName string = resourceGroup().name
output resourceId string = loadBalancer.id

部署 Bicep 模板

  1. 将该 Bicep 文件另存为本地计算机上的 main.bicep

  2. 使用 Azure CLI 或 Azure PowerShell 来部署 Bicep 文件:

    az group create --name TestRG --location chinanorth3
    az deployment group create --resource-group TestRG --template-file main.bicep
    

部署完成后,会显示一条指示部署成功的消息。

部署 Azure Bastion

Bastion 使用浏览器通过安全外壳 (SSH) 或远程桌面协议 (RDP),通过使用其专用 IP 地址连接虚拟网络中的虚拟机。 虚拟机不需要公共 IP 地址、客户端软件或特殊配置。 有关 Bastion 的详细信息,请参阅《什么是 Azure Bastion?》。

注意

每小时定价从部署 Bastion 的时刻开始计算,无论出站数据的使用情况如何。 有关详细信息,请参阅定价SKU。 如果要将 Bastion 部署为教程或测试的一部分,建议在使用完此资源后将其删除。

使用 Azure 资源管理器快速启动模板中的 Azure Bastion 即服务 Bicep 模板,在虚拟网络中部署和配置 Azure Bastion。 该 Bicep 模板定义以下 Azure 资源:

查阅 Bicep 文件:

@description('Name of new or existing vnet to which Azure Bastion should be deployed')
param vnetName string = 'vnet01'

@description('IP prefix for available addresses in vnet address space')
param vnetIpPrefix string = '10.1.0.0/16'

@description('Specify whether to provision new vnet or deploy to existing vnet')
@allowed([
  'new'
  'existing'
])
param vnetNewOrExisting string = 'new'

@description('Bastion subnet IP prefix MUST be within vnet IP prefix address space')
param bastionSubnetIpPrefix string = '10.1.1.0/26'

@description('Name of Azure Bastion resource')
param bastionHostName string

@description('Azure region for Bastion and virtual network')
param location string = resourceGroup().location

var publicIpAddressName = '${bastionHostName}-pip'
var bastionSubnetName = 'AzureBastionSubnet'

resource publicIp 'Microsoft.Network/publicIPAddresses@2022-01-01' = {
  name: publicIpAddressName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

// if vnetNewOrExisting == 'new', create a new vnet and subnet
resource newVirtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = if (vnetNewOrExisting == 'new') {
  name: vnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        vnetIpPrefix
      ]
    }
    subnets: [
      {
        name: bastionSubnetName
        properties: {
          addressPrefix: bastionSubnetIpPrefix
        }
      }
    ]
  }
}

// if vnetNewOrExisting == 'existing', reference an existing vnet and create a new subnet under it
resource existingVirtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' existing = if (vnetNewOrExisting == 'existing') {
  name: vnetName
}
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' = if (vnetNewOrExisting == 'existing') {
  parent: existingVirtualNetwork
  name: bastionSubnetName
  properties: {
    addressPrefix: bastionSubnetIpPrefix
  }
}

resource bastionHost 'Microsoft.Network/bastionHosts@2022-01-01' = {
  name: bastionHostName
  location: location
  dependsOn: [
    newVirtualNetwork
    existingVirtualNetwork
  ]
  properties: {
    ipConfigurations: [
      {
        name: 'IpConf'
        properties: {
          subnet: {
            id: subnet.id
          }
          publicIPAddress: {
            id: publicIp.id
          }
        }
      }
    ]
  }
}

部署 Bicep 模板

  1. 将该 Bicep 文件另存为本地计算机上的 bastion.bicep

  2. 使用文本或代码编辑器在文件中进行以下更改:

    • 第 2 行:将 param vnetName string'vnet01' 更改为 'VNet'
    • 第 5 行:将 param vnetIpPrefix string'10.1.0.0/16' 更改为 '10.0.0.0/16'
    • 第 12 行:将 param vnetNewOrExisting string'new' 更改为 'existing'
    • 第 15 行:将 param bastionSubnetIpPrefix string'10.1.1.0/26' 更改为 '10.0.1.0/26'
    • 第 18 行:将 param bastionHostName string 更改为 param bastionHostName = 'VNet-bastion'

    更改后,Bicep 文件的前 18 行应如下示例所示:

    @description('Name of new or existing vnet to which Azure Bastion should be deployed')
    param vnetName string = 'VNet'
    
    @description('IP prefix for available addresses in vnet address space')
    param vnetIpPrefix string = '10.0.0.0/16'
    
    @description('Specify whether to provision new vnet or deploy to existing vnet')
    @allowed([
      'new'
      'existing'
    ])
    param vnetNewOrExisting string = 'existing'
    
    @description('Bastion subnet IP prefix MUST be within vnet IP prefix address space')
    param bastionSubnetIpPrefix string = '10.0.1.0/26'
    
    @description('Name of Azure Bastion resource')
    param bastionHostName = 'VNet-bastion'
    
    
  3. 保存 bastion.bicep 文件。

  4. 使用 Azure CLI 或 Azure PowerShell 来部署 Bicep 文件:

    az deployment group create --resource-group TestRG --template-file bastion.bicep
    

部署完成后,会显示一条指示部署成功的消息。

注意

虚拟网络中具有 Bastion 主机的虚拟机不需要公共 IP 地址。 Bastion 提供公共 IP,虚拟机使用专用 IP 在网络中进行通信。 可以从 Bastion 托管的虚拟网络的任何虚拟机中删除公共 IP。 有关详细信息,请参阅将公共 IP 地址与 Azure VM 取消关联

查看已部署的资源

使用 Azure CLI、Azure PowerShell 或 Azure 门户来查看部署的资源。

az resource list --resource-group TestRG

连接到 VM

  1. 在门户中,搜索并选择“虚拟机”。

  2. 在“虚拟机”页上,选择 BackendVM1

  3. 在“BackendVM1”页顶部,选择“连接”旁边的下拉箭头,然后选择“Bastion”。

    使用 Azure Bastion 连接到第一个虚拟机的屏幕截图。

  4. 在“Bastion”页面上,输入你为虚拟机创建的用户名和密码,然后选择“连接”。

VM 之间进行通信

  1. 从 BackendVM1 的桌面上,打开 PowerShell。

  2. 输入 ping BackendVM0。 你会收到类似于以下消息的回复:

    PS C:\Users\BackendVM1> ping BackendVM0
    
    Pinging BackendVM0.ovvzzdcazhbu5iczfvonhg2zrb.bx.internal.chinacloudapp.cn with 32 bytes of data
    Request timed out.
    Request timed out.
    Request timed out.
    Request timed out.
    
    Ping statistics for 10.0.0.5:
        Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),
    

    由于它使用 Internet 控制消息协议(ICMP),因此执行 ping 操作失败。 默认情况下,不允许 ICMP 通过 Windows 防火墙。

  3. 要允许 ICMP 通过此 VM 上的 Windows 防火墙入站,请输入以下命令:

    New-NetFirewallRule �DisplayName "Allow ICMPv4-In" �Protocol ICMPv4
    
  4. 关闭 Bastion 与 BackendVM1 的连接。

  5. 重复连接到 VM 中的步骤以连接到 BackendVM0。

  6. 在 BackendVM0 上的 PowerShell 中,输入 ping BackendVM1

    这一次,你会收到类似于以下消息的成功回复,因为你已允许 ICMP 通过 BackendVM1 上的防火墙。

    PS C:\Users\BackendVM0> ping BackendVM1
    
    Pinging BackendVM1.e5p2dibbrqtejhq04lqrusvd4g.bx.internal.chinacloudapp.cn [10.0.0.4] with 32 bytes of data:
    Reply from 10.0.0.4: bytes=32 time=2ms TTL=128
    Reply from 10.0.0.4: bytes=32 time<1ms TTL=128
    Reply from 10.0.0.4: bytes=32 time<1ms TTL=128
    Reply from 10.0.0.4: bytes=32 time<1ms TTL=128
    
    Ping statistics for 10.0.0.4:
        Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
    Approximate round trip times in milli-seconds:
        Minimum = 0ms, Maximum = 2ms, Average = 0ms
    
  7. 关闭 Bastion 与 BackendVM0 的连接。

清理资源

完成虚拟网络后,请使用 Azure CLI、Azure PowerShell 或 Azure 门户删除资源组及其所有资源:

az group delete --name TestRG

后续步骤

在本快速入门中,你创建了具有两个子网的虚拟网络,其中一个子网包含两个 VM,另一个子网用于 Bastion。 你部署了 Bastion,并使用它连接到了 VM 并在 VM 之间开始通信。 若要详细了解虚拟网络设置,请参阅创建、更改或删除虚拟网络

VM 之间的专用通信在虚拟网络中不受限制。 若要详细了解如何在虚拟网络中配置各种类型的 VM 通信,请继续查看下一篇文章: