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