練習 - 重構 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
    網路介面 networkInterfaces_toytruckserver890_name_resource networkInterface

移除多餘的子網路資源

虛擬網路的子網路目前定義兩次。 在 virtualNetwork 資源中定義一次,並再次定義為其本身名為 defaultSubnet 的子資源。 定義子網路兩次沒有意義。

  1. 刪除 defaultSubnet 資源。

    請注意,networkInterface 資源現在出現問題,因為參考預設子網路的資源識別碼:

    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@2024-05-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 資源更新為參考子網路的資源識別碼:

    resource networkInterface 'Microsoft.Network/networkInterfaces@2024-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 資源以參考參數:

    屬性 參數
    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

    屬性 變數
    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 屬性

    • ipConfigurationsidetagtypeprivateIPAddress,因為它是由 Azure 自動設定的,而且配置方法是 [動態]

    • ipConfigurations.properties:

      • provisioningState
    • publicIPAddressnamepropertiestypesku

    • dnsSettings,因為 dnsServers 屬性是空的

提示

使用您自己的範本時,您必須判斷是否有任何應該移除的屬性,就像您在這裡所做的一樣。

在 Visual Studio Code 中,Bicep 擴充功能可協助您為資源設定最少屬性。 當您在資源定義中的等號之後新增空格時,Visual Studio Code 會提示您選取 required-properties

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

選取 [required-properties] 時,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@2024-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@2024-05-01' = {
      name: networkSecurityGroupName
      location: location
    }
    
    resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2024-05-01' = {
      name: publicIPAddressName
      location: location
      sku: {
        name: publicIPAddressSkuName
        tier: 'Regional'
      }
      properties: {
        publicIPAddressVersion: 'IPv4'
        publicIPAllocationMethod: 'Static'
        idleTimeoutInMinutes: 4
      }
    }
    
    resource virtualMachine 'Microsoft.Compute/virtualMachines@2024-07-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@2024-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@2024-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'
      }
    }
    

    儘管列出的 IP 位址範圍可能不同,但是 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": "10.0.0.0/16"
        },
        "virtualNetworkDefaultSubnetAddressPrefix": {
            "value": "10.0.0.0/24"
        }
      }
    }
    
  2. 選取 [檢視] > [問題] 來顯示 [問題] 窗格。

    未指出任何問題。

提示

使用您自己的範本時,對於要參數化的屬性和其他自訂,您可能有不同的選擇。 在整個本課程模組中,我們提供一般指引來協助您開始使用,但在決定如何重構您自己的 Bicep 檔案時,您必須考慮自己的環境及如何重複使用範本。