使用 Bicep 佈建 Linux 虛擬機器

已完成

Bicep 範本的核心元素是資源,可指定 Azure 資源。 每個資源都包含一組泛型和資源特定的屬性。 例如,下列範例中使用的範本描述 Azure 虛擬網路。 雖然名稱和位置屬性是通用,但 addressPrefix 是資源專用。 資源旁的 Microsoft.Network/virtualNetworks@2021-05-01 字串會指定其 API 版本,而 virtualNetwork 項目代表其符號名稱,讓您可以參考範本中的資源。

除了資源元素之外,下列範例範本也包含參數元素,可讓您在部署期間將名稱指派給虛擬網路。 如果您當時未指派名稱,則會改為套用預設值 lnx-bcp-vnet。 描述元素是裝飾項目範例,如前置 @ 字元所指示。 其用途是描述參數的角色,當您使用 Azure 入口網站 來檢閱或部署對應的 Azure Resource Manager 範本時,其輸出會出現在參數的文字方塊中。 使用下列程式碼範例,使用 Bicep 佈建 Linux VM:

@description('Name of the virtual network')
param virtualNetworkName string = 'lnx-bcp-vnet'

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-05-01' = {
  name: virtualNetworkName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        addressPrefix
      ]
    }
  }
}

使用 Bicep 範本部署 Linux VM

使用 Bicep 會涉及到撰寫和部署範本。 若要簡化並增強撰寫體驗,請使用 Visual Studio Code 搭配 Bicep 延伸模組。 相同的延伸模組也支援以 Bicep 為基礎的部署。 如果您想要從命令列觸發部署,或作為指令碼工作的一部分,您可以安裝並使用 Bicep CLI 作為獨立公用程式,或直接從 Azure CLI 工作階段內使用它。 Azure CLI 會在第一次叫用任何 az bicep 命令期間自動安裝 Bicep CLI。 不過,若要執行 Bicep 的手動安裝,請執行 az bicep install

實際上,使用 Bicep 佈建執行 Linux 的 Azure VM 的流程通常涉及下列高階步驟:

  • 識別適當的 VM 映像。
  • 識別適當的 VM 大小。
  • 撰寫 Bicep 範本。
  • 起始 Bicep 範本的部署。

當您部署 Bicep 範本時,稱為轉譯的工作會自動將它們轉換成對等的 Azure Resource Manager 範本。 您也可以分別執行 bicep buildbicep decompile 命令,在 Bicep 和 Azure Resource Manager 格式之間執行轉換。

若要識別適當的 VM 映像和大小,請遵循本課程模組z之前的單元中所述的步驟。 本單元著重於 Bicep 特定的工作。

撰寫 Bicep 範本

若要撰寫 Bicep 範本,請從啟動已安裝 Bicep 延伸模組的 Visual Studio Code 工作階段開始。 接著,建立名為 main.bicep 的檔案。 將以下內容新增到文件中,然後儲存變更:

注意

您可以任意為 Bicep 檔案選擇檔案名稱,然而最好選擇反映檔案內容或用途的名稱,而且您應該使用 “.bicep” 作為副檔名。

@description('The name of your virtual machine')
param vmName string = 'lnx-bcp-vm'

@description('Username for the virtual machine')
param adminUsername string

@description('Type of authentication to use on the virtual machine')
@allowed([
  'sshPublicKey'
  'password'
])
param authenticationType string = 'password'

@description('SSH Key or password for the virtual machine')
@secure()
param adminPasswordOrKey string

@description('Unique DNS Name for the Public IP used to access the virtual machine')
param dnsLabelPrefix string = toLower('${vmName}-${uniqueString(resourceGroup().id)}')

@description('The allowed Linux distribution and version for the VM')
@allowed([
  'Ubuntu-2204'
])
param ubuntuOSVersion string = 'Ubuntu-2204'

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

@description('The size of the VM')
param vmSize string = 'Standard_F4s'

@description('Name of the virtual network')
param virtualNetworkName string = 'lnx-bcp-vnet'

@description('Name of the subnet in the virtual network')
param subnetName string = 'subnet0'

@description('Name of the network security group')
param networkSecurityGroupName string = 'lnx-bcp-nsg'

var imageReference = {
  'Ubuntu-2204': {
    publisher: 'Canonical'
    offer: '0001-com-ubuntu-server-jammy'
    sku: '22_04-lts-gen2'
    version: 'latest'
  }
}
var publicIPAddressName = '${vmName}-pip'
var networkInterfaceName = '${vmName}-nic'
var osDiskType = 'Standard_LRS'
var subnetAddressPrefix = '10.3.0.0/24'
var addressPrefix = '10.3.0.0/16'
var linuxConfiguration = {
  disablePasswordAuthentication: true
  ssh: {
    publicKeys: [
      {
        path: '/home/${adminUsername}/.ssh/authorized_keys'
        keyData: adminPasswordOrKey
      }
    ]
  }
}

resource networkInterface 'Microsoft.Network/networkInterfaces@2021-05-01' = {
  name: networkInterfaceName
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: subnet.id
          }
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress: {
            id: publicIPAddress.id
          }
        }
      }
    ]
    networkSecurityGroup: {
      id: networkSecurityGroup.id
    }
  }
}

resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2021-05-01' = {
  name: networkSecurityGroupName
  location: location
  properties: {
    securityRules: [
      {
        name: 'ssh'
        properties: {
          priority: 1000
          protocol: 'Tcp'
          access: 'Allow'
          direction: 'Inbound'
          sourceAddressPrefix: '*'
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '22'
        }
      }
    ]
  }
}

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-05-01' = {
  name: virtualNetworkName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        addressPrefix
      ]
    }
  }
}

resource subnet 'Microsoft.Network/virtualNetworks/subnets@2021-05-01' = {
  parent: virtualNetwork
  name: subnetName
  properties: {
    addressPrefix: subnetAddressPrefix
    privateEndpointNetworkPolicies: 'Enabled'
    privateLinkServiceNetworkPolicies: 'Enabled'
  }
}

resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2021-05-01' = {
  name: publicIPAddressName
  location: location
  sku: {
    name: 'Basic'
  }
  properties: {
    publicIPAllocationMethod: 'Dynamic'
    publicIPAddressVersion: 'IPv4'
    dnsSettings: {
      domainNameLabel: dnsLabelPrefix
    }
    idleTimeoutInMinutes: 4
  }
}

resource vm 'Microsoft.Compute/virtualMachines@2021-11-01' = {
  name: vmName
  location: location
  properties: {
    hardwareProfile: {
      vmSize: vmSize
    }
    storageProfile: {
      osDisk: {
        createOption: 'FromImage'
        managedDisk: {
          storageAccountType: osDiskType
        }
      }
      imageReference: imageReference[ubuntuOSVersion]
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: networkInterface.id
        }
      ]
    }
    osProfile: {
      computerName: vmName
      adminUsername: adminUsername
      adminPassword: adminPasswordOrKey
      linuxConfiguration: ((authenticationType == 'password') ? null : linuxConfiguration)
    }
    securityProfile: json('null')
  }
}

output adminUsername string = adminUsername
output fqdn string = publicIPAddress.properties.dnsSettings.fqdn
output sshCommand string = 'ssh ${adminUsername}@${publicIPAddress.properties.dnsSettings.fqdn}'

注意

此範本是以 GitHub 存放庫 Azure 快速入門範本的內容為基礎。

起始 Bicep 範本的部署

儲存 main.bicep 檔案之後,您可以繼續進行以範本為基礎的部署。 首先,在您的本機電腦上啟動 Azure CLI 工作階段,然後執行 az login 以驗證。 您必須提供具有足夠權限的使用者認證,才能在 Azure 訂用帳戶中佈建資源。 接下來,將目前目錄變更為 main.bicep 檔案所在的目錄。 或者,您也可以啟動 Azure Cloud Shell Bash 工作階段,並將該檔案上傳至 Azure Cloud Shell 環境中的主目錄。

接下來,從已驗證的 Azure CLI 工作階段執行下列命令來建立資源群組,其中包含屬於後續部署一部分的所有資源:

az group create --name rg-lnx-bcp --location eastus

在您繼續進行之前,建議您執行下列命令,以確保您使用的是最新版的 Bicep CLI:

az bicep upgrade

最後,執行下列命令來起始部署:

az deployment group create --resource-group rg-lnx-bcp --template-file main.bicep --parameters adminUsername=azureuser

注意

此命令包含 --parameters 切換,在此案例中,會設定您要部署之 Azure VM 的本機系統管理員名稱。 Azure CLI 會提示您提供對應的密碼,因為未設定 adminPasswordOrKey 參數的預設值。

Azure VM 應該會在幾分鐘內開始執行。 若要與其連線,請檢閱部署所產生的輸出,以識別與網路介面相關聯的完整網域名稱 (FQDN)。 或者,您可以使用 shCommand 值。 出現提示時,請提供新設定的密碼,以在建立 SSH 連線時進行驗證。

如果您未記錄 Bicep 部署的輸出值,您可以執行下列命令來再次顯示這些值:

az deployment group show \
  --resource-group rg-lnx-bcp \
  --name main \
  --query properties.outputs

JSON 格式化的輪出應類似於以下內容:

{
  "adminUsername": {
    "type": "String",
    "value": "azureuser"
  },
  "fqdn": {
    "type": "String",
    "value": "lnx-bcp-vm-example.eastus.cloudapp.azure.com"
  },
  "sshCommand": {
    "type": "String",
    "value": "ssh azureuser@lnx-bcp-vm-example.eastus.cloudapp.azure.com"
  }
}