你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

如何使用 Azure CLI 大规模创建资源

作为 Azure 资源管理器,在配置新环境时,经常必须创建多个 Azure 资源。 在从脚本自动创建 Azure 资源时,你可能还具有最适合的 Azure 资源审批过程。

在本文中,你将了解以下内容:

  • 从从带分隔符的 CSV 文件收到的参数创建多个 Azure 资源。
  • 使用 IF.。用于创建依赖 Azure 资源的 THEN 语句。
  • 将脚本进度记录到本地 TXT 文件。

此示例脚本已在 Bash 和 PowerShell 环境和 PowerShell 7 的 Azure Cloud Shell进行测试。 在 Azure-samples/azure-cli-samples 中找到 CSV 和完整脚本。

准备环境

按照以下步骤准备环境以运行示例脚本:

  • 在 Azure Cloud Shell打开 Bash 或 PowerShell 环境。 有关详细信息,请参阅 Azure Cloud Shell 中的 Bash 快速入门

  • 下载并保存到本地目录的以下 CSV 文件。 将第三行替换为 myExistingResourceGroupName 实际的资源组名称。

    resourceNo,location,createRG,exstingRgName,createVnet,vnetAddressPrefix,subnetAddressPrefixes,vmImage,publicIpSku,Adminuser
    1,eastus,TRUE,,TRUE,10.0.0.0/16,10.0.0.0/24,Ubuntu2204,standard,
    2,eastus2,TRUE,,FALSE,,,Debian11,standard,alex-smith
    3,southcentralus,FALSE,myExistingResourceGroupName,FALSE,,,Ubuntu2204,standard,jan-smith
    [empty line for Bash]
    

    注意

    要成为正确的 Unix 文本文件并可由 Bash 读取,CSV 文件需要最后一行数据行末尾的换行符。 这将导致文件末尾的空白行。 空行不需要说 [empty line] ,因为仅提供此文本以显示空行存在。 PowerShell 环境没有此换行符要求。

  • 将修改后的 CSV 文件上传到 Azure Cloud Shell 博客存储帐户。 执行此操作的最简单方法是使用 Azure Cloud Shell 主菜单上的“管理文件 ”下拉列表。 有关 Cloud Shell 存储的详细信息,请参阅 Azure Cloud Shell 中的持久保存文件。

脚本概述

本文将单个大型脚本分为四个部分,以便可以解释每个步骤。

  • 变量设置
  • 数据验证
  • 循环验证
  • Azure 资源创建

还提供了两个脚本:一个用于 Bash,另一个用于 PowerShell。 这两个脚本使用相同的 Azure CLI 命令。 它是不同的环境或终端配置文件。 例如,Bash 使用 do...doneif...then...fi。 在 PowerShell 环境中,使用等效 foreach 项和 if (something is true)...{do this}。 在 Azure Cloud Shell 中,可以使用 “切换到 PowerShell ”或 “切换到 Bash ”按钮在 Azure Cloud Shell 主菜单中切换环境。

如果愿意,请直接转到本文在 Azure-samples/azure-cli-samples 中使用的 CSV 和脚本文件。

设置变量

首先创建脚本所需的变量。 以下三个变量需要环境的实际值:

  • subscriptionID:这是 Azure 订阅 ID。
  • csvFileLocation:这是 CSV 输入文件的位置和文件名。
  • logFileLocation:这是脚本用于创建日志文件的位置和文件名。 无需创建或上传此文件。

具有 msdocs- 前缀的变量可以替换为所选的前缀。 所有空 ("") 变量都使用 CSV 输入文件中的值。 这些空变量是脚本所需的占位符。

# Variable block

# Replace these three variable values with actual values
subscriptionID=00000000-0000-0000-0000-00000000
csvFileLocation="myFilePath\myFileName.csv"
logFileLocation="myFilePath\myLogName.txt"

# Variable values that contain a prefix can be replaced with the prefix of your choice.
#   These prefixes have a random ID appended to them in the script.
# Variable values without a prefix will be overwritten by the contents of your CSV file.
location=""
createRG=""
newRgName="msdocs-rg-"
existingRgName=""

createVnet=""
vnetName="msdocs-vnet-"
subnetName="msdocs-subnet-"
vnetAddressPrefix=""
subnetAddressPrefixes=""

vmName="msdocs-vm-"
vmImage=""
publicIpSku=""
adminUser=""
adminPassword="msdocs-PW-@"

# Set your Azure subscription 
az account set --subscription $subscriptionID

验证 CSV 文件值

在开始测试创建脚本之前,请确保 CSV 文件的格式正确,并将为变量分配正确的值。 此脚本使用 IF.。THEN 语句,以便一次查看一个方案/CSV 行。

# Verify CSV columns are being read correctly

# Take a look at the CSV contents
cat $csvFileLocation

# Validate select CSV row values
while IFS=, read -r resourceNo location createRG existingRgName createVnet vnetAddressPrefix subnetAddressPrefixes vmImage publicIpSku adminUser
do
  # Generate a random ID
  let "randomIdentifier=$RANDOM*$RANDOM"

  # Return the values for the first data row
  # Change the $resourceNo to check different scenarios in your CSV
  if [ "$resourceNo" = "1" ]; then
    echo "resourceNo = $resourceNo"
    echo "location = $location"
    echo "randomIdentifier = $randomIdentifier"
    echo ""
    
    echo "RESOURCE GROUP INFORMATION:"
    echo "createRG = $createRG"
    if [ "$createRG" = "TRUE" ]; then 
      echo "newRGName = $newRgName$randomIdentifier"
    else
      echo "exsitingRgName = $existingRgName"
    fi
    echo ""

    echo "VNET INFORMATION:"
    echo "createVnet = $createVnet"
    if [ "$createVnet" = "TRUE" ]; then 
      echo "vnetName = $vnetName$randomIdentifier"
      echo "subnetName = $subnetName$randomIdentifier"
      echo "vnetAddressPrefix = $vnetAddressPrefix"
      echo "subnetAddressPrefixes = $subnetAddressPrefixes"
    fi
    echo ""

    echo "VM INFORMATION:"
    echo "vmName = $vmName$randomIdentifier"
    echo "vmImage = $vmImage"
    echo "vmSku = $publicIpSku"
    if [ `expr length "$adminUser"` == "1" ]; then
      echo "SSH keys will be generated."
    else
      echo "vmAdminUser = $adminUser"
      echo "vmAdminPassword = $adminPassword$randomIdentifier"        
    fi
  fi  
# skip the header line
done < <(tail -n +2 $csvFileLocation)

使用本文中提供的 CSV,验证输出如下所示:(每个测试的 00000001 随机 ID 将有所不同)。

resourceNo = 1
location = eastus

RESOURCE GROUP INFORMATION:
createRG = TRUE
newRGName = msdocs-rg-00000001

VNET INFORMATION:
createVnet = TRUE
vnetName = msdocs-vnet-00000001
subnetName = msdocs-subnet-00000001
vnetAddressPrefix = 10.0.0.0/16
subnetAddressPrefix = 10.0.0.0/24

VM INFORMATION:
vmName = msdocs-vm-00000001
vmImage = Ubuntu2204
vmSku = standard
SSH keys will be created

验证脚本逻辑

如果你对脚本功能有信心,则可以跳过此步骤。 但是,由于此脚本旨在大规模创建 Azure 资源,因此循环访问包含 echowrite-host 语句的脚本可以节省时间和意外的计费 Azure 资源。

可通过多种方式使用 Bash 循环访问 CSV 文件。 此示例与 IFS a while loop.

# Validate script logic

# Create the log file
echo "SCRIPT LOGIC VALIDATION.">$logFileLocation

# Loop through each row in the CSV file
while IFS=, read -r resourceNo location createRG existingRgName createVnet vnetAddressPrefix subnetAddressPrefixes vmImage publicIpSku adminUser
do
  # Generate a random ID
  let "randomIdentifier=$RANDOM*$RANDOM"
    
  # Log resource number and random ID
  echo "resourceNo = $resourceNo">>$logFileLocation
  echo "randomIdentifier = $randomIdentifier">>$logFileLocation

  # Check if a new resource group should be created
  if [ "$createRG" == "TRUE" ]; then
    echo "Will create RG $newRgName$randomIdentifier.">>$logFileLocation
    existingRgName=$newRgName$randomIdentifier
  fi

  # Check if a new virtual network should be created, then create the VM
  if [ "$createVnet" == "TRUE" ]; then
    echo "Will create VNet $vnetName$randomIdentifier in RG $existingRgName.">>$logFileLocation
    echo "Will create VM $vmName$randomIdentifier in Vnet $vnetName$randomIdentifier in RG $existingRgName.">>$logFileLocation
  else
    echo "Will create VM $vmName$randomIdentifier in RG $existingRgName.">>$logFileLocation
  fi
# Skip the header line.
done < <(tail -n +2 $csvFileLocation)

# Clear the console and display the log file
Clear
cat $logFileLocation

使用本文中提供的 CSV,验证输出如下所示:( 00000001, 2, 3 每个测试的随机 ID 将有所不同,但每个 resourceNo 资源应共享相同的随机 ID。

resourceNo = 1
createRG = TRUE
createVnet = TRUE
Will create RG msdocs-rg-00000001
Will create VNet msdocs-vnet-00000001 in RG msdocs-rg-00000001
Will create VM msdocs-vm-00000001 within Vnet msdocs-vnet-00000001 in RG msdocs-rg-00000001

resourceNo = 2
createRG = TRUE
createVnet = FALSE
Will create RG msdocs-rg-00000002
Will create VM msdocs-vm-00000002 without Vnet in RG msdocs-rg-00000002

resourceNo = 3
createRG = FALSE
createVnet = FALSE
Will create VM msdocs-vm-00000003 without Vnet in RG <myExistingResourceGroup>

创建 Azure 资源

现已创建变量块、验证 CSV 值,并已完成测试 echo 运行,或者 write-host。 执行脚本的第四部分和最后一部分,以创建 CSV 输入文件中定义的 Azure 资源。

# Create Azure resources

# Create the log file
echo "CREATE AZURE RESOURCES.">$logFileLocation

# Loop through each CSV row
while IFS=, read -r resourceNo location createRG existingRgName createVnet vnetAddressPrefix subnetAddressPrefixes vmImage publicIpSku adminUser
do
  # Generate a random ID
  let "randomIdentifier=$RANDOM*$RANDOM"

  # Log resource number, random ID and display start time
  echo "resourceNo = $resourceNo">>$logFileLocation
  echo "randomIdentifier = $randomIdentifier">>$logFileLocation
  echo "Starting creation of resourceNo $resourceNo at $(date +"%Y-%m-%d %T")."

  # Check if a new resource group should be created
  if [ "$createRG" == "TRUE" ]; then
    echo "Creating RG $newRgName$randomIdentifier at $(date +"%Y-%m-%d %T").">>$logFileLocation
    az group create --location $location --name $newRgName$randomIdentifier >>$logFileLocation
    existingRgName=$newRgName$randomIdentifier
    echo "  RG $newRgName$randomIdentifier creation complete"
  fi

  # Check if a new virtual network should be created, then create the VM
  if [ "$createVnet" == "TRUE" ]; then
    echo "Creating VNet $vnetName$randomIdentifier in RG $existingRgName at $(date +"%Y-%m-%d %T").">>$logFileLocation
    az network vnet create \
        --name $vnetName$randomIdentifier \
        --resource-group $existingRgName \
        --address-prefix $vnetAddressPrefix \
        --subnet-name $subnetName$randomIdentifier \
        --subnet-prefixes $subnetAddressPrefixes >>$logFileLocation
    echo "  VNet $vnetName$randomIdentifier creation complete"
    
    echo "Creating VM $vmName$randomIdentifier in Vnet $vnetName$randomIdentifier in RG $existingRgName at $(date +"%Y-%m-%d %T").">>$logFileLocation
    az vm create \
        --resource-group $existingRgName \
        --name $vmName$randomIdentifier \
        --image $vmImage \
        --vnet-name $vnetName$randomIdentifier \
        --subnet $subnetName$randomIdentifier \
        --public-ip-sku $publicIpSku \
        --generate-ssh-keys >>$logFileLocation
    echo "  VM $vmName$randomIdentifier creation complete"
  else
    echo "Creating VM $vmName$randomIdentifier in RG $existingRgName at $(date +"%Y-%m-%d %T").">>$logFileLocation
    az vm create \
        --resource-group $existingRgName \
        --name $vmName$randomIdentifier \
        --image $vmImage \
        --public-ip-sku $publicIpSku \
        --admin-username $adminUser\
        --admin-password $adminPassword$randomIdentifier >>$logFileLocation
    echo "  VM $vmName$randomIdentifier creation complete"    
  fi
# skip the header line
done < <(tail -n +2 $csvFileLocation)

# Clear the console (optional) and display the log file
# clear
cat $logFileLocation

在控制台输出中,是否缺少 CSV 文件中的最后一行? 这可能是最后一行之后缺少的行延续字符造成的。 在 CSV 文件末尾添加一个空白行来解决此问题。

日志文件读取前的控制台输出

Starting creation of resourceNo 1 at YYYY-MM-DD HH:MM:SS.
  RG msdocs-rg-00000001 creation complete
  VNet msdocs-vnet-00000001 creation complete
  VM msdocs-vm-00000001 creation complete

Starting creation of resourceNo 2 at YYYY-MM-DD HH:MM:SS.
  RG msdocs-rg-00000002 creation complete
  VM msdocs-vm-00000002 creation complete

Starting creation of resourceNo 3 at YYYY-MM-DD HH:MM:SS.
  VM msdocs-vm-00000003 creation complete

日志文件内容应如下所示:

Starting creation of resourceNo 1 at YYYY-MM-DD HH:MM:SS.
  Creating RG msdocs-rg-00000001 at YYYY-MM-DD HH:MM:SS.
  {
  Resource group create output
  }
  Creating VNet msdocs-vnet-00000001 in RG msdocs-rg-000000001 at YYYY-MM-DD HH:MM:SS.
  {
  VNet create output
  }  
  Creating VM msdocs-vm-00000001 in RG msdocs-rg-00000001 at YYYY-MM-DD HH:MM:SS.
  {
  VM create output
  }

Starting creation of resourceNo 2 at YYYY-MM-DD HH:MM:SS.
  Creating RG msdocs-rg-00000002 at YYYY-MM-DD HH:MM:SS.
  {
  Resource group create output
  }
  Creating VM msdocs-vm-00000002 in RG msdocs-rg-00000002 at YYYY-MM-DD HH:MM:SS.
  {
  VM create output
  }

Starting creation of resourceNo 3 at YYYY-MM-DD HH:MM:SS.
  Creating msdocs-vm-00000003 creation complete
  {
  VM create output
  }

故障排除

在 Bash 中,“创建 Azure 资源”步骤在步骤 1 后停止

在 Ubuntu 22.04.3 LTS 和 Debian 版本 12(bookworm) 中,验证脚本逻辑 适用于所有三个资源的预期返回结果。 但是, 创建 Azure 资源在第一个资源 后停止。 此问题的一个可能原因是在步骤 #1 中创建 VNet 需要几秒钟。 Ubuntu 和 Debian 都继续执行第二个资源,而无需等待 VNet 的完成。 你可以在等待中 阅读有关此内容的详细信息,不会等待 while 循环中的进程完成 ,或者 等待任何进程在 bash 脚本中完成。

Bash 脚本忽略 IF 语句

Bash 区分大小写。 单词 true 不等于 TRUE。 也是greater than,不是>,也是equals==,不是=-gt 请确保 CSV 列值中没有排版错误或前导/尾随空格。

变量值不会随每个循环而更改

这通常是由 CSV 文件中的额外空间引起的。 CSV 文件中的行将如下所示: column1,column2,column3 或者 column1,,column3,根据习惯,可以轻松创建一个测试文件,其中包含每个逗号 column1, column2, column3后面的空格。 在 CSV 中有前导或尾随空格时,列值实际上是 <space>columnValue。 脚本逻辑 if [ "$columnName" = "columnValue" ] 返回“false”。 删除 CSV 行中的所有前导空格和尾随空格以解决此问题。

CIDR 表示法无效

将错误地址前缀传递给 az network vnet create/> 时,会收到 InvalidCIDRNotation 错误。 在视觉上,地址前缀在语句中 echo 返回时看起来正确,这很困难。 若要排查从 CSV 读取的实际值问题,请尝试以下脚本:

while IFS=, read -r resourceNo location createRG existingRgName createVnet vnetAddressPrefix subnetAddressPrefixes vmImage publicIpSku adminUser
do
    echo "resourceNo = $resourceNo"

    if [ "$createVnet" == "TRUE" ]; then
      startTest="abc"
      endTest="xyz"
      echo $startTest$vnetAddressPrefix$endTest
    fi
done < <(tail -n +2 $setupFileLocation)

如果结果看起来 xzy10.0.0.0 与预期不相同 abc10.0.0.0/24xyz,则 CSV 文件中可能存在隐藏字符或额外的逗号。 添加具有相同前缀值的测试列、重新排列 CSV 列,并在简单的记事本编辑器中复制/粘贴 CSV 内容。 在本文中,CSV 列的重新排列最终修复了错误。

参数是预期或必需

如果尚未提供必需的参数,或者出现拼写错误,导致 Azure CLI 错误分析引用命令,则会收到此错误。 使用脚本时,当以下任一项为 true 时,也会收到此错误:

  • 缺少或不正确的行继续符。
  • 行继续符右侧有尾随空格。
  • 变量名称包含特殊字符,如短划线(-)。

InvalidTemplateDeployment

尝试在不提供该资源的位置创建 Azure 资源时,收到类似于以下错误:“容量限制的以下 SKU 失败:Standard_DS1_v2”当前在位置“westus”不可用。

下面是完整的错误示例:

{"error":{"code":"InvalidTemplateDeployment","message":"The template deployment 'vm_deploy_<32 character ID>'
is not valid according to the validation procedure. The tracking id is '<36 character ID>'.
See inner errors for details.","details":[{"code":"SkuNotAvailable","message":"The requested VM size for resource
'Following SKUs have failed for Capacity Restrictions: Standard_DS1_v2' is currently not available
in location '<your specified location>'. Please try another size or deploy to a different location
or different zone. See https://aka.ms/azureskunotavailable for details."}]}}

若要更正错误,请更改位置或选择为所需位置提供的其他参数值。

另请参阅