快速入门:使用 MSBuild 将 Bicep 转换为 JSON

了解利用 MSBuild 将 Bicep 文件转换为 Azure 资源管理器 JSON 模板(ARM 模板)的过程。 此外,MSBuild 还可用于将 Bicep 参数文件转换为具有 NuGet 包版本 0.23.x 或更高版本的 Azure 资源管理器参数文件。 本文提供的示例演示如何使用命令行中的 MSBuild 和 C# 项目文件进行转换。 这些项目文件可用作在 MSBuild 持续集成 (CI) 管道中使用的示例。

先决条件

需要以下软件的最新版本:

  • Visual StudioVisual Studio Code。 Visual Studio 社区版本将免费安装 .NET 6.0、.NET Core 3.1、.NET SDK、MSBuild、.NET Framework 4.8、NuGet 包管理器和 C# 编译器。 从安装程序中选择“工作负载”>“.NET 桌面开发”。 使用 Visual Studio Code 时,还需要 BicepAzure 资源管理器 (ARM) 工具的扩展
  • 适用于操作系统的 PowerShell 或命令行 shell。

如果你的环境未将 nuget.org 配置为包源,则根据 nuget.config 的配置方式,你可能需要运行以下命令:

dotnet nuget add source  https://api.nuget.org/v3/index.json -n nuget.org

在某些环境中,使用单个包源有助于避免具有相同 ID 和版本的包在不同源中包含不同内容而引起的问题。

MSBuild 任务和 Bicep 包

对于持续集成 (CI) 管道,可以使用 MSBuild 任务和 CLI 包将 Bicep 文件和 Bicep 参数文件转换为 JSON。 此功能依赖于以下 NuGet 包:

包名称 说明
Azure.Bicep.MSBuild 跨平台 MSBuild 任务,用于调用 Bicep CLI,并将 Bicep 文件编译为 ARM JSON 模板。
Azure.Bicep.CommandLine.win-x64 适用于 Windows 的 Bicep CLI。
Azure.Bicep.CommandLine.linux-x64 适用于 Linux 的 Bicep CLI。
Azure.Bicep.CommandLine.osx-x64 适用于 macOS 的 Bicep CLI。

可以从这些页面找到最新版本。 例如:

Screenshot showing how to find the latest Bicep NuGet package version.

最新的 NuGet 包版本与最新的 Bicep CLI 版本匹配。

  • Azure.Bicep.MSBuild

    当包含在项目文件的 PackageReference 属性中时,Azure.Bicep.MSBuild 包会导入用于调用 Bicep CLI 的 Bicep 任务。

    <ItemGroup>
      <PackageReference Include="Azure.Bicep.MSBuild" Version="0.24.24" />
      ...
    </ItemGroup>
    
    

    此包将 Bicep CLI 的输出转换为 MSBuild 错误,并导入 BicepCompile 目标以简化 Bicep 任务的使用。 默认情况下,BicepCompileBuild 目标之后运行,编译所有 @(Bicep) 项和 @(BicepParam) 项。 然后,它将输出以相同的文件名和 .json 扩展名存储到 $(OutputPath)

    以下示例显示了用于在项目文件所在的目录中编译 main.bicep 和 main.bicepparam 文件的项目文件设置,并将编译后的 main.json 和 main.parameters.json 放在 $(OutputPath) 目录中

    <ItemGroup>
      <Bicep Include="main.bicep" />
      <BicepParam Include="main.bicepparam" />
    </ItemGroup>
    

    可使用 Bicep 项的 OutputFile 元数据替代每个文件的输出路径。 以下示例将以递归方式查找所有 main.bicep 文件,并将编译后的 .json 文件放在 $(OutputPath) 中同名的子目录下的 $(OutputPath)

    <ItemGroup>
      <Bicep Include="**\main.bicep" OutputFile="$(OutputPath)\%(RecursiveDir)\%(FileName).json" />
      <BicepParam Include="**\main.bicepparam" OutputFile="$(OutputPath)\%(RecursiveDir)\%(FileName).parameters.json" />
    </ItemGroup>
    

    可通过在项目中将以下属性之一设置为 PropertyGroup 来执行更多自定义:

    属性名称 默认值 说明
    BicepCompileAfterTargets Build 用作 BicepCompile 目标的 AfterTargets 值。 更改该值以替代项目中 BicepCompile 目标的计划。
    BicepCompileDependsOn 用作 BicepCompile 目标的 DependsOnTargets 值。 可将此属性设置为希望 BicepCompile 目标依赖的目标。
    BicepCompileBeforeTargets 用作 BicepCompile 目标的 BeforeTargets 值。
    BicepOutputPath $(OutputPath) 设置此属性以替代编译的 ARM 模板的默认输出路径。 Bicep 项上的 OutputFile 元数据优先于此值。

    若要运行 Azure.Bicep.MSBuild,必须设置名为 BicepPath 的环境变量。 有关如何配置 BicepPath,请查看下一个项目符号项。

  • Azure.Bicep.CommandLine

    Azure.Bicep.CommandLine.* 包适用于 Windows、Linux 和 macOS。 以下示例引用适用于 Windows 的包。

    <ItemGroup>
      <PackageReference Include="Azure.Bicep.CommandLine.win-x64" Version="__LATEST_VERSION__" />
      ...
    </ItemGroup>  
    

    在项目文件中引用时,Azure.Bicep.CommandLine.* 包将 BicepPath 属性自动设置为该平台的 Bicep 可执行文件的完整路径。 如果通过其他方式安装 Bicep CLI,则可以省略对此包的引用。 在这种情况下,可以配置一个名为 BicepPath 的环境变量或将 BicepPath 添加到 PropertyGroup,而不是引用 Azure.Bicep.Commandline 包,例如在 Windows 上:

    <PropertyGroup>
      <BicepPath>c:\users\john\.Azure\bin\bicep.exe</BicepPath>
      ...
    </PropertyGroup>
    

    在 Linux 上:

    <PropertyGroup>
      <BicepPath>/usr/local/bin/bicep</BicepPath>
      ...
    </PropertyGroup>
    

项目文件示例

以下示例演示如何配置 C# 控制台应用程序项目文件,以便将 Bicep 文件和 Bicep 参数文件转换为 JSON。 将以下示例中的 __LATEST_VERSION__ 替换为最新版本的 Bicep NuGet 包。 要查找最新版本,请参阅 MSBuild 任务和 Bicep 包

基于 SDK 的示例

.NET Core 3.1 和 .NET 6 示例类似。 但 .NET 6 使用不同格式的 Program.cs 文件。 有关详细信息,请参阅 .NET 6 C# 控制台应用模板生成顶级语句

  • .NET 6

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <RootNamespace>net6-sdk-project-name</RootNamespace>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Azure.Bicep.CommandLine.win-x64" Version="__LATEST_VERSION__" />
        <PackageReference Include="Azure.Bicep.MSBuild" Version="__LATEST_VERSION__" />
      </ItemGroup>
    
      <ItemGroup>
        <Bicep Include="**\main.bicep" OutputFile="$(OutputPath)\%(RecursiveDir)\%(FileName).json" />
        <BicepParam Include="**\main.bicepparam" OutputFile="$(OutputPath)\%(RecursiveDir)\%(FileName).parameters.json" />
      </ItemGroup>
    </Project>
    

    RootNamespace 属性包含占位符值。 创建项目文件时,该值与项目的名称匹配。

  • .NET Core 3.1

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Azure.Bicep.CommandLine.win-x64" Version="__LATEST_VERSION__" />
        <PackageReference Include="Azure.Bicep.MSBuild" Version="__LATEST_VERSION__" />
      </ItemGroup>
    
      <ItemGroup>
        <Bicep Include="**\main.bicep" OutputFile="$(OutputPath)\%(RecursiveDir)\%(FileName).json" />
        <BicepParam Include="**\main.bicepparam" OutputFile="$(OutputPath)\%(RecursiveDir)\%(FileName).parameters.json" />
      </ItemGroup>
    </Project>
    

NoTargets SDK 示例

Microsoft.Build.NoTargets MSBuild 项目 SDK 允许项目树所有者定义不编译程序集的项目。 利用此 SDK 可创建仅编译 Bicep 文件的独立项目。

<Project Sdk="Microsoft.Build.NoTargets/__LATEST_MICROSOFT.BUILD.NOTARGETS.VERSION__">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Azure.Bicep.CommandLine.win-x64" Version="__LATEST_VERSION__" />
    <PackageReference Include="Azure.Bicep.MSBuild" Version="__LATEST_VERSION__" />
  </ItemGroup>

  <ItemGroup>
    <Bicep Include="main.bicep"/>
    <BicepParam Include="main.bicepparam"/>
  </ItemGroup>
</Project>

可在 https://www.nuget.org/packages/Microsoft.Build.NoTargets 找到最新的 Microsoft.Build.NoTargets 版本。 在 Microsoft.Build.NoTargets 中,请指定版本,如 Microsoft.Build.NoTargets/3.7.56

<Project Sdk="Microsoft.Build.NoTargets/3.7.56">
  ...
</Project>

经典框架示例

仅当前面的示例不适用时,才使用经典示例。 在此示例中,ProjectGuidRootNamespaceAssemblyName 属性包含占位符值。 创建项目文件时,将创建唯一 GUID,并且名称值与项目的名称匹配。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{11111111-1111-1111-1111-111111111111}</ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>ClassicFramework</RootNamespace>
    <AssemblyName>ClassicFramework</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
    <Bicep Include="main.bicep" />
    <BicepParam Include="main.bicepparam" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Azure.Bicep.CommandLine.win-x64">
      <Version>__LATEST_VERSION__</Version>
    </PackageReference>
    <PackageReference Include="Azure.Bicep.MSBuild">
      <Version>__LATEST_VERSION__</Version>
    </PackageReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

将 Bicep 转换为 JSON

这些示例演示如何使用 MSBuild 将 Bicep 文件和 Bicep 参数文件转换为 JSON。 首先为 .NET、.NET Core 3.1 或经典框架创建项目文件。 然后,在运行 MSBuild 之前生成 Bicep 文件和 Bicep 参数文件。

创建项目

使用 dotnet CLI 在 .NET 中生成项目。

  1. 打开 Visual Studio Code 并选择“终端”>“新建终端”以启动 PowerShell 会话

  2. 创建名为 msBuildDemo 的目录并转到该目录。 此示例使用 C:\msBuildDemo

    Set-Location -Path C:\
    New-Item -Name .\msBuildDemo -ItemType Directory
    Set-Location -Path .\msBuildDemo
    
  3. 运行 dotnet 命令以使用 .NET 6 框架创建新控制台。

    dotnet new console --framework net6.0
    

    该命令使用与目录同名的项目文件 msBuildDemo.csproj。 若要详细了解如何从 Visual Studio Code 创建控制台应用程序,请参阅教程

  4. 使用编辑器打开 msBuildDemo.csproj,将内容替换为 .NET 6NoTargets SDK 示例,并将 __LATEST_VERSION__ 替换为最新版本的 Bicep NuGet 包

  5. 保存文件。

创建 Bicep 文件

需要将 Bicep 文件和 BicepParam 文件转换为 JSON。

  1. 在项目文件所在的文件夹中创建 main.bicep 文件,例如:C:\msBuildDemo 目录,内容如下

    @allowed([
      'Premium_LRS'
      'Premium_ZRS'
      'Standard_GRS'
      'Standard_GZRS'
      'Standard_LRS'
      'Standard_RAGRS'
      'Standard_RAGZRS'
      'Standard_ZRS'
    ])
    @description('Storage account type.')
    param storageAccountType string = 'Standard_LRS'
    
    @description('Location for all resources.')
    param location string = resourceGroup().location
    
    var storageAccountName = 'storage${uniqueString(resourceGroup().id)}'
    
    resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
      name: storageAccountName
      location: location
      sku: {
        name: storageAccountType
      }
      kind: 'StorageV2'
    }
    
    output storageAccountNameOutput string = storageAccount.name
    
  2. 在 C:\msBuildDemo 目录中创建 main.bicepparam 文件,其中包含以下内容

    using './main.bicep'
    
    param prefix = '{prefix}'
    

    {prefix} 替换为用作存储帐户名称前缀的字符串值。

运行 MSBuild

运行 MSBuild,将 Bicep 文件和 Bicep 参数文件转换为 JSON。

  1. 打开 Visual Studio Code 终端会话。

  2. 在 PowerShell 会话中,转到包含项目文件的文件夹。 例如,C:\msBuildDemo 目录

  3. 运行 MSBuild。

    MSBuild.exe -restore .\msBuildDemo.csproj
    

    在初始生成期间,restore 参数创建编译 Bicep 文件所需的依赖项。 在初始生成后,该参数是可选的。

    使用 .NET Core:

    dotnet build .\msBuildDemo.csproj
    

    dotnet restore .\msBuildDemo.csproj
    
  4. 转到输出目录,并打开应类似于以下示例的 main.json 文件

    MSBuild 基于 SDK 或框架版本创建输出目录:

    • .NET 6:\bin\Debug\net6.0
    • .NET Core 3.1:\bin\Debug\netcoreapp3.1
    • NoTargets SDK:\bin\Debug\net48
    • 经典框架:\bin\Debug
    {
      "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
      "contentVersion": "1.0.0.0",
      "metadata": {
        "_generator": {
          "name": "bicep",
          "version": "0.8.9.13224",
          "templateHash": "12345678901234567890"
        }
      },
      "parameters": {
        "storageAccountType": {
          "type": "string",
          "defaultValue": "Standard_LRS",
          "metadata": {
            "description": "Storage account type."
          },
          "allowedValues": [
            "Premium_LRS",
            "Premium_ZRS",
            "Standard_GRS",
            "Standard_GZRS",
            "Standard_LRS",
            "Standard_RAGRS",
            "Standard_RAGZRS",
            "Standard_ZRS"
          ]
        },
        "location": {
          "type": "string",
          "defaultValue": "[resourceGroup().location]",
          "metadata": {
            "description": "Location for all resources."
          }
        }
      },
      "variables": {
        "storageAccountName": "[format('storage{0}', uniqueString(resourceGroup().id))]"
      },
      "resources": [
        {
          "type": "Microsoft.Storage/storageAccounts",
          "apiVersion": "2022-05-01",
          "name": "[variables('storageAccountName')]",
          "location": "[parameters('location')]",
          "sku": {
            "name": "[parameters('storageAccountType')]"
          },
          "kind": "StorageV2"
        }
      ],
      "outputs": {
        "storageAccountNameOutput": {
          "type": "string",
          "value": "[variables('storageAccountName')]"
        }
      }
    }
    
  5. main.parameters.json 文件应如下所示

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "prefix": {
      "value": "mystore"
    }
  }
}

如果进行更改或想要重新运行生成,请删除输出目录,以便可以创建新文件。

清理资源

完成对文件的操作后,请删除目录。 在本示例中,请删除 C:\msBuildDemo

Remove-Item -Path "C:\msBuildDemo" -Recurse

后续步骤