练习 - 重构 Bicep 文件

已完成

在上一练习中,你创建了一个初始 Bicep 文件,其中包含玩具卡车虚拟机和关联的资源。 但是,Bicep 模板没有遵循最佳做法,较难看懂。 在此单元中,你将重构改文件。

在重构过程中,你将:

  • 更新资源和参数的符号名称。
  • 删除冗余的参数、资源和属性。
  • 添加变量和参数以使 Bicep 文件可重复使用。

更新资源符号名称

  1. 在 Visual Studio Code 中,打开 main.bicep 文件。

  2. 选择网络安全组资源的符号名称,即 networkSecurityGroups_ToyTruckServer_nsg_name_resource 或类似名称。

    重命名符号名称。 可以选择 F2 或者右键单击,然后选择“重命名符号”

    输入名称 networkSecurityGroup,然后按 Enter。 Visual Studio Code 会更新文件中的名称和所有引用。

  3. 对每个资源重复此过程。 重命名资源,如下表所示。

    注意

    部署中资源的名称与表中的名称略有不同。 查找其名称与这些名称相近的资源。

    资源类型 当前符号名称 新符号名称
    公共 IP 地址 publicIPAddresses_ToyTruckServer_ip_name_resource publicIPAddress
    虚拟机 virtualMachines_ToyTruckServer_name_resource virtualMachine
    虚拟网络 virtualNetworks_ToyTruck_vnet_name_resource virtualNetwork
    子网 virtualNetworks_ToyTruck_vnet_name_default defaultSubnet
    Linux networkInterfaces_toytruckserver890_name_resource networkInterface

删除冗余的子网资源

虚拟网络的子网当前定义了两次。 它在 virtualNetwork 资源中定义了一次,并在第二次定义为它自己的子资源,名为 defaultSubnet。 定义子网两次没有意义。

  1. 删除 defaultSubnet 资源。

    请注意,networkInterface 资源现在显示一个问题,因为它引用了默认子网的资源 ID:

    Screenshot of Visual Studio Code that shows the network interface resource definition. The error is highlighted.

  2. 更新 virtualNetwork 资源以包含对子网的 existing 引用。 如果添加 existing 引用,则可以在 Bicep 代码中再次引用子网,而无需再次定义:

    resource virtualNetwork 'Microsoft.Network/virtualNetworks@2020-11-01' = {
      name: virtualNetworks_ToyTruck_vnet_name
      location: 'westus'
      properties: {
        addressSpace: {
          addressPrefixes: [
            '10.0.0.0/16'
          ]
        }
        subnets: [
          {
            name: 'default'
            properties: {
              addressPrefix: '10.0.0.0/24'
              delegations: []
              privateEndpointNetworkPolicies: 'Enabled'
              privateLinkServiceNetworkPolicies: 'Enabled'
            }
          }
        ]
        virtualNetworkPeerings: []
        enableDdosProtection: false
      }
    
      resource defaultSubnet 'subnets' existing = {
        name: 'default'
      }
    }
    
  3. 更新 networkInterface 资源以引用子网的资源 ID:

    resource networkInterface 'Microsoft.Network/networkInterfaces@2022-05-01' = {
      name: networkInterfaces_toytruckserver890_name
      location: 'westus3'
      properties: {
        ipConfigurations: [
          {
            name: 'ipconfig1'
            properties: {
              privateIPAddress: '10.0.0.4'
              privateIPAllocationMethod: 'Dynamic'
              publicIPAddress: {
                id: publicIPAddress.id
              }
              subnet: {
                id: virtualNetwork::defaultSubnet.id
              }
              primary: true
              privateIPAddressVersion: 'IPv4'
            }
          }
        ]
        dnsSettings: {
          dnsServers: []
        }
        enableAcceleratedNetworking: true
        enableIPForwarding: false
        disableTcpStateTracking: false
        networkSecurityGroup: {
          id: networkSecurityGroup.id
        }
        nicType: 'Standard'
      }
    }
    

    你会注意到有关表达式在循环中涉及的错误。 你将在下一步中修复该问题。

  4. 转到 virtualNetwork 资源的 subnets 属性并删除 id: defaultSubnet.id 以解决错误。

将参数更改为变量

模板中的参数不一定是参数。 现在将参数重命名为更有意义的名称,并将其转换为变量。

  1. 选择 virtualNetworks_ToyTruck_vnet_name 参数的符号名称。 将其重命名为 virtualNetworkName

  2. 将参数更改为变量。 请记住删除类型,因为变量定义不包括类型:

    var virtualNetworkName = 'ToyTruck-vnet'
    
  3. 对每个参数重复该过程。 重命名参数,如下表所示。

    请注意,networkInterfaceName 的值包含一个三位数。 不同部署的数字不同。 确保从引用模板复制变量的值。

    当前参数名称 新变量名称
    virtualMachines_ToyTruckServer_name virtualMachineName
    networkInterfaces_toytruckserver890_name networkInterfaceName
    publicIPAddresses_ToyTruckServer_ip_name publicIPAddressName
    networkSecurityGroups_ToyTruckServer_nsg_name networkSecurityGroupName
  4. 验证变量声明是否如以下示例所示:

    var virtualNetworkName = 'ToyTruck-vnet'
    var virtualMachineName = 'ToyTruckServer'
    var networkInterfaceName = 'YOUR-NETWORK-INTERFACE-NAME'
    var publicIPAddressName = 'ToyTruckServer-ip'
    var networkSecurityGroupName = 'ToyTruckServer-nsg'
    

更新资源位置

当前,所有资源都使用硬编码的位置。 你现在将添加一个参数,以便模板变得更可重复使用。

  1. 在文件顶部,添加一个新的参数和描述修饰器,以明确该参数的用途:

    @description('The location where resources are deployed.')
    param location string = resourceGroup().location
    
  2. 更新每个资源以使用 location 参数而不是硬编码的 westus3 位置。

添加参数和变量

你的模板具有一些硬编码值,其中的参数或变量更适合。 现在将为可能在部署之间更改的属性添加参数,并为不会更改的值添加变量。

  1. 在 main.bicep 文件顶部 location 参数的下面,添加以下参数:

    @description('The name of the size of the virtual machine to deploy.')
    param virtualMachineSizeName string = 'Standard_D2s_v3'
    
    @description('The name of the storage account SKU to use for the virtual machine\'s managed disk.')
    param virtualMachineManagedDiskStorageAccountType string = 'Premium_LRS'
    
    @description('The administrator username for the virtual machine.')
    param virtualMachineAdminUsername string = 'toytruckadmin'
    
    @description('The administrator password for the virtual machine.')
    @secure()
    param virtualMachineAdminPassword string
    
    @description('The name of the SKU of the public IP address to deploy.')
    param publicIPAddressSkuName string = 'Standard'
    
    @description('The virtual network address range.')
    param virtualNetworkAddressPrefix string
    
    @description('The default subnet address range within the virtual network')
    param virtualNetworkDefaultSubnetAddressPrefix string
    

    某些参数具有默认值,而其他参数则没有。 稍后,你将创建一个参数文件来设置其中的大多数值。

  2. networkSecurityGroupName 变量下方添加以下新变量声明:

    var virtualNetworkDefaultSubnetName = 'default'
    var virtualMachineImageReference = {
      publisher: 'canonical'
      offer: '0001-com-ubuntu-server-focal'
      sku: '20_04-lts-gen2'
      version: 'latest'
    }
    
  3. 添加以下变量声明。 将这些值替换为自己的引用模板中的 OS 磁盘名称。

    var virtualMachineOSDiskName = 'YOUR-OS-DISK-NAME'
    

    virtualMachineOSDiskName 的值是唯一的。 每个部署的值都是不同的。 确保从引用模板复制变量的值。

    警告

    请确保复制 virtualMachineOSDiskNamenetworkInterfaceName 变量的正确的值。 否则,Azure 将无法检测到你声明的现有资源,并且可能会尝试创建新资源。

    变量声明现在应如以下示例所示:

    var virtualNetworkName = 'ToyTruck-vnet'
    var virtualMachineName = 'ToyTruckServer'
    var networkInterfaceName = 'YOUR-NETWORK-INTERFACE-NAME'
    var publicIPAddressName = 'ToyTruckServer-ip'
    var networkSecurityGroupName = 'ToyTruckServer-nsg'
    var virtualNetworkDefaultSubnetName = 'default'
    var virtualMachineImageReference = {
      publisher: 'canonical'
      offer: '0001-com-ubuntu-server-focal'
      sku: '20_04-lts-gen2'
      version: 'latest'
    }
    var virtualMachineOSDiskName = 'YOUR-OS-DISK-NAME'
    
  4. 更新 publicIPAddress 资源以引用参数:

    properties 参数
    sku.name publicIPAddressSkuName
  5. 更新 virtualMachine 资源以引用参数和变量:

    属性 参数或变量
    hardwareProfile.vmSize virtualMachineSizeName
    storageProfile.imageReference virtualMachineImageReference
    使用变量名称替换对象的值,包括大括号。
    storageProfile.osDisk.name virtualMachineOSDiskName
    storageProfile.osDisk.managedDisk.storageAccountType virtualMachineManagedDiskStorageAccountType
    osProfile.adminUsername virtualMachineAdminUsername
    osProfile.adminPassword
    osProfile.adminUsername 下方添加此属性
    virtualMachineAdminPassword
  6. 更新 virtualNetwork 资源以引用参数和变量:

    属性 参数或变量
    addressSpace.addressPrefixes virtualNetworkAddressPrefix
    subnets.name virtualNetworkDefaultSubnetName
    subnets.addressPrefix virtualNetworkDefaultSubnetAddressPrefix
  7. 更新 virtualNetwork 资源的嵌套资源 defaultSubnet

    properties 变量
    name virtualNetworkDefaultSubnetName

删除不必要的属性

导出过程中,会将冗余属性添加到多个资源。 使用以下步骤删除不需要的属性。

  1. networkSecurityGroup 资源中,删除 properties,因为 securityRules 属性为空。

  2. publicIPAddress 资源中,删除以下属性:

    • ipAddress 属性,因为它由 Azure 自动设置
    • ipTags 属性,因为它为空
  3. virtualMachine 资源中,删除以下属性:

    • storageProfile.osDisk.managedDisk.id 属性,因为 Azure 会在部署虚拟机时自动确定该属性

      重要

      如果不删除此属性,模板将无法正确部署。

    • storageProfile.dataDisks 属性,因为它为空

    • osProfile.secrets 属性,因为它为空

    • osProfile.requireGuestProvisionSignal 属性,因为 Azure 会自动设置此属性

  4. virtualNetwork 资源中,删除以下属性:

    • delegationsvirtualNetworkPeerings 属性,因为它们为空。
    • type: 'Microsoft.Network/virtualNetworks/subnets' 的行
  5. networkInterface 资源中,删除以下属性:

    • kind 属性

    • ipConfigurations 中删除:idetagtypeprivateIPAddress,因为它由 Azure 自动设置,分配方法为“动态”

    • ipConfigurations.properties

      • provisioningState
    • publicIPAddressnamepropertiestypesku 中删除

    • dnsSettings,因为 dnsServers 属性为空

提示

使用自己的模板时,需要确定是否有任何属性应该被删除,正如你在这里所做的那样。

在 Visual Studio Code 中,Bicep 扩展可帮助你设置资源的最小属性。 在资源定义中的等于号后添加空格时,Visual Studio Code 会提示你选择必需属性

Screenshot of Visual Studio Code that shows the required-properties option.

选择“必需属性”时,Visual Studio Code 会使用必需的属性预先填充资源定义。 可以参考“required-properties”来确定转换后的模板中的属性是否都需要存在。

Azure 快速入门模板存储库也有助于完成此任务。 找到一个与你尝试执行的操作大致相同的快速入门模板,然后查看它在资源上设置的属性。

创建参数文件

当前,参数在模板中定义为默认值。 为了使模板在各种环境中都能很好地工作,最好创建一个参数文件,并删除需要为每个环境更改的参数的默认值。

  1. 创建名为 main.parameters.production.json 的新文件。

  2. 将以下 JSON 粘贴到 main.parameters.production.json 文件中

    {
      "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": {
        "virtualMachineSizeName": {
          "value": "Standard_D2s_v3"
        },
        "virtualMachineManagedDiskStorageAccountType": {
            "value": "Premium_LRS"
        },
        "virtualMachineAdminUsername": {
            "value": "toytruckadmin"
        },
        "virtualNetworkAddressPrefix": {
            "value": "YOUR-VIRTUAL-NETWORK-ADDRESS-PREFIX"
        },
        "virtualNetworkDefaultSubnetAddressPrefix": {
            "value": "YOUR-SUBNET-ADDRESS-PREFIX"
        }
      }
    }
    
  3. 更新 virtualNetworkAddressPrefixvirtualNetworkDefaultSubnetAddressPrefix 参数的值,以匹配引用模板的虚拟网络资源中指定的 IP 地址范围。

    例如,以下是在参考模板中指定值的方式。 你的 IP 地址可能与此示例中使用的 IP 地址不同。

    resource virtualNetworks_ToyTruck_vnet_name_resource 'Microsoft.Network/virtualNetworks@2022-05-01' = {
      name: virtualNetworks_ToyTruck_vnet_name
      location: 'westus3'
      properties: {
        addressSpace: {
          addressPrefixes: [
            '10.0.0.0/16'
          ]
        }
        subnets: [
          {
            name: 'default'
            id: virtualNetworks_ToyTruck_vnet_name_default.id
            properties: {
              addressPrefix: '10.0.0.0/24'
              delegations: []
              privateEndpointNetworkPolicies: 'Disabled'
              privateLinkServiceNetworkPolicies: 'Enabled'
            }
            type: 'Microsoft.Network/virtualNetworks/subnets'
          }
        ]
        virtualNetworkPeerings: []
        enableDdosProtection: false
      }
    }
    
  4. 更新 main.bicep 文件以删除在参数文件中指定的参数的默认值。

    • virtualMachineSizeName
    • virtualMachineManagedDiskStorageAccountType
    • virtualMachineAdminUsername

不更改 locationpublicIPAddressSkuName 参数的默认值,因为这些值可能在所有环境中都相同。

验证模板

  1. 在重构阶段结束时,main.bicep 文件应如以下示例所示:

    @description('The location where resources are deployed.')
    param location string = resourceGroup().location
    
    @description('The name of the size of the virtual machine to deploy.')
    param virtualMachineSizeName string
    
    @description('The name of the storage account SKU to use for the virtual machine\'s managed disk.')
    param virtualMachineManagedDiskStorageAccountType string
    
    @description('The administrator username for the virtual machine.')
    param virtualMachineAdminUsername string
    
    @description('The administrator password for the virtual machine.')
    @secure()
    param virtualMachineAdminPassword string
    
    @description('The name of the SKU of the public IP address to deploy.')
    param publicIPAddressSkuName string = 'Standard'
    
    @description('The virtual network address range.')
    param virtualNetworkAddressPrefix string
    
    @description('The default subnet address range within the virtual network')
    param virtualNetworkDefaultSubnetAddressPrefix string
    
    var virtualNetworkName = 'ToyTruck-vnet'
    var virtualMachineName = 'ToyTruckServer'
    var networkInterfaceName = 'YOUR-NETWORK-INTERFACE-NAME'
    var publicIPAddressName = 'ToyTruckServer-ip'
    var networkSecurityGroupName = 'ToyTruckServer-nsg'
    var virtualNetworkDefaultSubnetName = 'default'
    var virtualMachineImageReference = {
      publisher: 'canonical'
      offer: '0001-com-ubuntu-server-focal'
      sku: '20_04-lts-gen2'
      version: 'latest'
    }
    var virtualMachineOSDiskName = 'YOUR-OS-DISK-NAME'
    
    resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-05-01' = {
      name: networkSecurityGroupName
      location: location
    }
    
    resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2022-05-01' = {
      name: publicIPAddressName
      location: location
      sku: {
        name: publicIPAddressSkuName
        tier: 'Regional'
      }
      properties: {
        publicIPAddressVersion: 'IPv4'
        publicIPAllocationMethod: 'Static'
        idleTimeoutInMinutes: 4
      }
    }
    
    resource virtualMachine 'Microsoft.Compute/virtualMachines@2022-08-01' = {
      name: virtualMachineName
      location: location
      properties: {
        hardwareProfile: {
          vmSize: virtualMachineSizeName
        }
        storageProfile: {
          imageReference: virtualMachineImageReference
          osDisk: {
            osType: 'Linux'
            name: virtualMachineOSDiskName
            createOption: 'FromImage'
            caching: 'ReadWrite'
            managedDisk: {
              storageAccountType: virtualMachineManagedDiskStorageAccountType
            }
            deleteOption: 'Delete'
            diskSizeGB: 30
          }
        }
        osProfile: {
          computerName: virtualMachineName
          adminUsername: virtualMachineAdminUsername
          adminPassword: virtualMachineAdminPassword
          linuxConfiguration: {
            disablePasswordAuthentication: false
            provisionVMAgent: true
            patchSettings: {
              patchMode: 'ImageDefault'
              assessmentMode: 'ImageDefault'
            }
            enableVMAgentPlatformUpdates: false
          }
          allowExtensionOperations: true
        }
        networkProfile: {
          networkInterfaces: [
            {
              id: networkInterface.id
              properties: {
                deleteOption: 'Detach'
              }
            }
          ]
        }
        diagnosticsProfile: {
          bootDiagnostics: {
            enabled: true
          }
        }
      }
    }
    
    resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-05-01' = {
      name: virtualNetworkName
      location: location
      properties: {
        addressSpace: {
          addressPrefixes: [
            virtualNetworkAddressPrefix
          ]
        }
        subnets: [
          {
            name: virtualNetworkDefaultSubnetName
            properties: {
              addressPrefix: virtualNetworkDefaultSubnetAddressPrefix
              privateEndpointNetworkPolicies: 'Disabled'
              privateLinkServiceNetworkPolicies: 'Enabled'
            }
          }
        ]
        enableDdosProtection: false
      }
    
      resource defaultSubnet 'subnets' existing = {
        name: virtualNetworkDefaultSubnetName
      }
    }
    
    resource networkInterface 'Microsoft.Network/networkInterfaces@2022-05-01' = {
      name: networkInterfaceName
      location: location
      properties: {
        ipConfigurations: [
          {
            name: 'ipconfig1'
            properties: {
              privateIPAllocationMethod: 'Dynamic'
              publicIPAddress: {
                id: publicIPAddress.id
              }
              subnet: {
                id: virtualNetwork::defaultSubnet.id
              }
              primary: true
              privateIPAddressVersion: 'IPv4'
            }
          }
        ]
        enableAcceleratedNetworking: true
        enableIPForwarding: false
        disableTcpStateTracking: false
        networkSecurityGroup: {
          id: networkSecurityGroup.id
        }
        nicType: 'Standard'
      }
    }
    

    main.parameters.production.json 文件应如以下文件所示,不过你可能会列出不同的 IP 地址范围:

    {
      "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": {
        "virtualMachineSizeName": {
          "value": "Standard_D2s_v3"
        },
        "virtualMachineManagedDiskStorageAccountType": {
            "value": "Premium_LRS"
        },
        "virtualMachineAdminUsername": {
            "value": "toytruckadmin"
        },
        "virtualNetworkAddressPrefix": {
            "value": "10.0.0.0/16"
        },
        "virtualNetworkDefaultSubnetAddressPrefix": {
            "value": "10.0.0.0/24"
        }
      }
    }
    
  2. 选择“查看”>“问题”以显示“问题”窗格。

    未显示有任何问题。

提示

当使用自己的模板时,你可能会对要参数化的属性和其他自定义做出不同的选择。 在此模块中,我们提供了可帮助你入门的常规指导,但在决定如何重构自己的 Bicep 文件时,你需要考虑自己的环境以及你希望如何重用模板。