用Bicep模板改进ARM模板语法(附实例)

77 阅读5分钟

Bicep模板是缓解ARM模板问题的一个好方法。除了解决现有ARM模板语法的弊端外,Bicep模板还带有其他改进。让我们一起来看看吧!

引用资源、参数和变量

这些改进之一是在引用其他资源和获取其属性方面。为了看到其中的区别,我们首先来看看使用ARM模板引用一个属性。

 [listKeys(resourceId('Microsoft.Storage/storageAccounts',         [CA] parameters('storageAccountName')), 2019-04-01').key1]"

还记得你最初是如何使用函数的类型和名称为其建立完整的resourceId的吗?有了Bicep,对于在同一模板中声明的资源,不再需要。现在你可以使用你之前看到的资源的引用名,直接访问它的任何属性,像这样。

 listKeys(myStorageAccount.id, '2019-04-01').key1

在这里,你看到listKeys(...) 函数被再次调用,使用两个参数。但与其使用两个函数,你可以直接使用资源的reference-name ,然后获取其属性id 。这产生的结果与使用resourceId(...) 函数手动建立该id的结果相同。仅仅比较这两个代码例子就可以看出可读性的提高。

引用参数和变量的方法与引用资源的方法相同。如果你想引用一个参数来设置一个值,你可以像这样直接使用它。

 Parameter storageAccountName string
  
 resource myStorageAccount 'Microsoft.Storage/storageAccounts@2020-08-01-preview' = {
     name: storageAccountName
     ...
 }

这个例子声明了一个存储账户。存储账户name 没有被指定为一个字面,而是引用了参数storageAccountName 的值。有一点需要注意的是,参数、变量和资源共享一个命名空间。共享一个名字空间意味着你不能为参数、变量或资源使用相同的名字--所有名字都必须是唯一的。

唯一的例外是输出。输出可以与参数、变量或资源有相同的名称。这使你可以做这样的事情。

 var storageAccountName = 'myStorageAccount'
  
 resource myStorageAccount 'Microsoft.Storage/storageAccounts
         [CA] @2020-08-01-preview' = {
     name: storageAccountName
     location: resourceGroup().location
     kind: 'StorageV2'
     sku: {
         name: 'Premium_LRS'
         tier: 'Premium'
     }
 }
  
 output storageAccountName string = storageAccountName

在这个例子中,你看到一个典型的场景,资源的名字是用一个变量值生成的,并作为一个输出返回。在变量和输出中重复使用相同的名字,比起使用不同的名字来表达相同的东西,有更好的可读性。

在变量和输出中使用引用

在ARM模板中,不可能在变量中使用reference(...) 函数。原因是ARM计算部署任何资源的变量值,在这个阶段它不能评估reference(...) 函数。在Bicep模板中,这个问题不复存在。为了了解原因,请看下面的Bicep模板。

 resource myStorageAccount 'Microsoft.Storage/storageAccounts
         [CA] @2020-08-01-preview' = {
     name: 'myStorageAccount'
     location: resourceGroup().location
     kind: 'StorageV2'
     sku: {
         name: 'Premium_LRS'
         tier: 'Premium'
     }
 }
  
 var storageAccountName = myStorageAccount.name
 var accessTier = myStorageAccount.properties.accessTier
  
 output message string = '${storageAccountName} with tier ${accessTier}'

在上面的例子中,两个变量都引用了一个存储账户。在这里,Bicep转码器做了一些聪明的事情。它没有留下这些变量并在输出中使用变量值,而是完全删除了这个变量。相反,它将产生参数结果的完整表达式复制到输出中。就像这样。

 {
   "$schema": "https://schema.management.azure.com/schemas/
           [CA] 2019-04-01/deploymentTemplate.json#",
     "variables": {
         "storageAccountName": "myStorageAccount"
     },
     "resources": [
         {
           "type": "Microsoft.Storage/storageAccounts",
           ...
         }
     ],
     "outputs": {
         "message": {
         "type": "string",
         "value": "[format('{0} was deployed with accessTier {1}', 
               [CA] variables('storageAccountName'), reference(resourceId('Microsoft.
               [CA] Storage/storageAccounts', 'myStorageAccount')).accessTier)]"
       }
     }
 }

在上面的例子中,你可以看到输出的值是如何建立起来的,而没有使用变量accessTier。相反,reference(...) 函数移到了输出的值计算中,在那里工作。

引用现有资源

当你想多次引用一个现有资源时,你可以重复使用reference(...) 函数。但作为一个更好的选择,你也可以让该资源在你的模板中以一个参考名称出现。要做到这一点,你可以使用现有的关键字,如下所示。

 resource existingStorageAccount 'Microsoft.Storage/storageAccounts
         [CA] @2020-08-01-preview' = existing {                         #A
     name: 'preexistingstorageaccountname'
 }

#A 现有关键字表示一个预先存在的资源,不应该用这个模板创建。

这个例子根本没有声明一个新的资源,相反,它只是用提供的名称建立了一个对现有资源的引用。请注意,当你部署这个Bicep模板时,如果该资源当时还不存在,部署将导致一个错误。

依赖性管理

随着Bicep的到来,另一项改进是取消了必须声明的依赖关系。正如你可能知道的,你需要声明你的资源之间的所有依赖关系,即使它们已经通过父子关系隐含地声明。

有了Bicep,这就不再需要了。当您将Bicep文件转译为ARM模板时,所有隐含的依赖关系都会被自动检测出来,并自动生成正确的dependOn声明。隐式依赖关系是指父子关系和相互引用的资源。例如,当你的应用程序设置引用一个存储账户来存储其密钥作为设置时,不再需要手动声明依赖关系。

如果你想控制资源部署的顺序,并且没有隐含的依赖关系,你仍然可以使用dependOn属性声明你自己的依赖关系。dependsOn属性的作用仍然与ARM模板中的作用完全相同。

字符串插值

构建较大的ARM模板时的另一个麻烦是缺乏对字符串插值的内置支持。字符串插值是一种用于从字面符号、参数、变量和表达式中构建文本字符串的技术。例如,请看下面这种基于环境名称为资源生成名称的常用方法。

    "parameters": {
         "environmentName": {
             "type": "string",
             "allowed": [ "dev", "tst", "prd" ]
         }
     },
     "variables": {
         "mainStorageAccountName": "[concat('stor', parameters('environmentName'), 
             [CA] 'main')]",
         "otherStorageAccountName": "[concat('stor', parameters('environmentName'),
             [CA] 'other')]",
         "somethingWebAppName": "[concat('webappp', parameters('environmentName'),
             [CA] 'something')]"
     },

虽然上面的方法效果很好,但在阅读模板时,需要几秒钟的时间在头脑中 "计算 "出实际的资源名称。而这只是针对这个相对简单的例子。试想一下,当列出几十种资源时,这将如何工作。

在Bicep模板中,不再需要使用concat(...) 函数来串联数值。相反,你可以在字符串字面的任何地方使用${...} 语法,大括号之间的值会被自动解释为一个表达式。下面是一个例子。

 param environmentName string {
     allowed: [ 'dev', 'tst', 'prd']
 }
  
 var mainStorageAccountName = 'stor${environmentName}main'
 var otherStorageAccountName = 'stor${environmentName}other'
 var somethingWebAppName = 'webappp${environmentName}something'

正如你所看到的,这三个变量的值现在更容易阅读了。由于不再需要结合${...} 语法来调用parameters(...) 函数,因此可读性大大增强。作为奖励,当你使用VS Code的Bicep扩展时--语法高亮也会显示使用字符串插值的地方,以进一步增强可读性。

没有强制分组

使用JSON作为ARM模板的基础的后果之一是参数和变量的对象符号。通过这种选择,所有的参数和变量都必须被分组在模板的一个位置--通常是在开头。请看下面的例子:

 {
     "$schema": "https://schema.management.azure.com/schemas/
         [CA] 2019-04-01/deploymentTemplate.json#",
     "contentVersion": "1.0.0.0",
     "parameters": {
         "environment": {                                        #B
             "type": "string"
         },
                                                                 #A
     },
     "variables": {
         "webAppPlanName": "[format('myAppPlan-{0}', parameters('environment'))]",
         "anotherVariable": "someValue"
     },
     "resources": [
                                                                 #A
         {
             "type": "Microsoft.Web/serverfarms",
             "apiVersion": "2020-06-01",
             "name": "[variables('webAppPlanName')]",            #C
             "location": "westeurope"
         }
     ]
 }

#A想象一下,在这些位置有几十行或几百行的内容

#B 一个变量的声明

#C 同一变量的使用

特别是在较大的模板中,这使得上下滚动的次数很多,因为参数都是在顶部定义的,而在文件中使用的则更多。JSON没有提供解决这个问题的方法,而且你不能多次声明参数或变量的键。有了新的Bicep语法,参数在顶部的分组就不再需要了,你可以这样做:

 param environment string = 'tst'
  
                                                                       #A
  
 var webAppPlanName = 'myAppPlan-${environment}'                       #B
  
 resource myWebApp 'Microsoft.Web/serverfarms@2020-06-01' = {
     name: webAppPlanName                                              #C
     location: 'westeurope'
 }
  
                                                                       #A
  
 var anotherVariable = 'someValue'
  
                                                                       #A

#A想象一下,在这些位置有几十或几百行的内容

#B 一个变量的声明

#C 同一变量的使用

在这个例子中,你可以看到,参数和变量现在是在使用之前就被声明了。其他变量可以在它们被使用的地方被声明,不会有问题。严格来说,你甚至可以在使用变量之前使用它们,因为在转译到ARM模板时,所有的东西最终都会被分组。

所有这些都大大增强了模板的可读性和可理解性。

评论

Bicep语言的最后一项改进是引入了注释。虽然ARM模板已经支持注释,但这有一个缺点:它使模板不再符合JSON标准。这导致许多文本编辑器将注释作为一个错误来指出。幸运的是,VS Code是一个例外,因为它能理解ARM模板而不是 "仅仅 "理解JSON。有了Bicep,所有这些都不再适用,你现在可以使用注释,如下所示:

 var webAppPlanName = 'myAppPlan' // Everything after a double slash is a comment
  
 resource myWebAppPlan 'Microsoft.Web/serverfarms@2020-06-01' = {
     name: webAppPlanName
     location: 'westeurope'
     /*
         Using slash star you can start a multi-line comment
         Star slash closes the comment
     */
 }

在这个例子中,有两种类型的注释。一个单行注释是在每一行的(结尾)使用// 。一个多行注释是用/**/ 围起来的。没有必要把/**/ 放在自己的一行,但这是惯例。