使用 Bicep 预配 Linux 虚拟机

已完成

Bicep 模板的核心元素是 resource,它指定 Azure 资源。 每个 resource 都包含一组泛型属性和特定于资源的属性。 例如,以下示例中使用的模板描述了一个 Azure 虚拟网络。 虽然 name 和 location 属性是通用的,但 addressPrefix 是特定于资源的属性。 资源旁边的 Microsoft.Network/virtualNetworks@2021-05-01 字符串指定其 API 版本,virtualNetwork 条目表示其符号名称,这提供了一种在模板中引用资源的方法。

除了 resource 元素,以下示例模板还包括一个参数元素,使你能够在部署期间向虚拟网络分配名称。 如果当时未分配名称,则改为应用默认值 lnx-bcp-vnet。 description 元素是修饰器的示例,由前导 @ 字符指示。 其用途是描述参数的角色,并且在你使用 Azure 门户查看或部署相应的 Azure 资源管理器模板时,其输出将显示在参数的文本框旁边。 使用以下代码示例通过使用 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 涉及到创作和部署模板。 若要简化和增强创作体验,请使用具有 Bicep 扩展的 Visual Studio Code。 同一扩展还支持基于 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 资源管理器模板。 还可以通过分别运行 bicep buildbicep decompile 命令来执行 Bicep 和 Azure 资源管理器格式之间的转换。

若要确定合适的 VM 映像和大小,请遵循本模块前面单元中所述的步骤。 本单元重点介绍特定于 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"
  }
}