3. Bicep语言基础
ARM 模板(JSON)能描述完整的 Azure 资源拓扑,但它的书写体验长期被诟病:大量花括号、字符串拼接、concat() 函数、dependsOn 手工维护,以及无法在编辑器中获得可靠的类型提示。Bicep 是 ARM 模板的领域特定语言(DSL),它最终会编译成标准 ARM JSON,因此部署时走的仍是 Azure Resource Manager 的同一条通道,但源码层面被大幅简化。理解 Bicep 的关键,不是把它当成一门新语言去背语法,而是把它看作“ARM 模板的可读写法”:声明式语义不变,表达方式变短、变清晰、可静态检查。
从 ARM 到 Bicep:同一份部署,两种写法
先看一个最小对比。ARM 中声明一个存储账户,需要 $schema、contentVersion、resources 数组、type/apiVersion/name/location/sku/kind 等字段,引用参数要写 [parameters('storageName')]。Bicep 中同样的资源写成:
param location string = resourceGroup().location
param storageName string
param skuName string = 'Standard_LRS'
resource stg 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageName
location: location
sku: {
name: skuName
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
}
output storageId string = stg.id
可以看到几个直接收益:resource 关键字后跟符号名 stg,后续用 stg.id 即可引用属性,无需 reference();参数类型写在名字后面,默认值用 =;字符串插值用 '${var}' 而不是 concat();dependsOn 在多数情况下由编译器根据符号引用自动推导。编译后生成的 JSON 与手写 ARM 等价,但源码可读性和可维护性完全不同。
参数、变量与类型约束
Bicep 的参数(param)用于接收部署时输入,变量(var)用于在模板内部复用表达式。参数应尽量带类型和约束,让错误在编译期暴露,而不是等到部署失败。常用装饰器包括:
@allowed([...]):限定取值集合,例如环境名只允许dev/test/prod。@minLength/@maxLength:约束字符串长度,存储账户名 3–24 字符。@minValue/@maxValue:约束数值范围,如副本数 1–10。@secure():标记敏感参数,部署日志中不回显,适合管理员密码、连接字符串。@description('...'):为参数提供说明,生成文档和门户表单时可见。
变量则适合承载“由参数推导出的中间结果”,例如根据环境拼接命名前缀:
@allowed(['dev', 'test', 'prod'])
param env string
var namePrefix = 'app-${env}'
var isProd = env == 'prod'
var storageName = 'st${replace(namePrefix, '-', '')}${uniqueString(resourceGroup().id)}'
这里 uniqueString() 保证全局唯一,replace() 去掉连字符以满足存储账户命名限制。变量不会出现在部署参数中,只在编译期展开,因此适合放这类派生逻辑。
循环与条件:批量与可选资源
Bicep 用 for 表达式表达循环,用三元表达式或 if 条件表达可选资源。循环常见于按数组批量创建资源,例如为多个区域各建一个存储账户:
param locations array = [
'eastasia'
'southeastasia'
]
resource stg 'Microsoft.Storage/storageAccounts@2023-01-01' = [for loc in locations: {
name: 'st${replace(loc, '-', '')}${uniqueString(resourceGroup().id)}'
location: loc
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}]
output stgNames array = [for (loc, i) in locations: stg[i].name]
注意 stg[i] 的索引访问:循环资源会生成一个数组,按声明顺序对应。条件资源则写成 resource x '...' = if (condition) { ... },例如只在生产环境创建诊断设置。循环与条件可以组合,但要注意:条件为 false 时该资源不会出现在部署中,引用它的 output 需要用 ? 安全访问或加判断,否则编译报错。
Bicep CLI:编译、验证与部署
Bicep 源码不能直接提交给 ARM,必须先编译为 JSON。常用命令如下:
# 安装(Windows 可用 winget,跨平台可用 az bicep install)
az bicep install
# 编译为 ARM JSON
bicep build main.bicep --outfile main.json
# 仅做类型与语法检查,不产出文件
bicep build main.bicep --stdout > /dev/null
# 查看将生成的资源变更(What-If)
az deployment group what-if \
--resource-group rg-iac-demo \
--template-file main.bicep \
--parameters env=dev
# 直接部署 Bicep(CLI 会自动编译)
az deployment group create \
--resource-group rg-iac-demo \
--template-file main.bicep \
--parameters env=dev storageName=stiacdemo001
实际工作中,bicep build 应纳入 CI 的第一道关卡:语法错误、类型不匹配、未定义符号都会在此阶段失败,避免把问题带到部署环节。az deployment group what-if 则用于预览变更,它会列出将创建、修改、删除的资源,是评审 IaC 变更的核心手段。部署时若直接传 --template-file main.bicep,Azure CLI 会调用 Bicep CLI 自动编译,但生产流水线更推荐显式 bicep build 后再部署 JSON,便于审计和缓存。
编译期与部署期的分工
把上面的链路拆开看,Bicep 的价值集中在“编译期”。bicep build 会做符号解析、类型检查、循环展开和依赖推导,任何拼写错误、类型不匹配、循环索引越界都会在这一步失败,而不是等到 ARM 返回一个含糊的 InvalidTemplate。部署期则由 ARM 负责资源提供程序调用、依赖排序、幂等执行和最终状态收敛。两者边界清晰:能在编译期发现的,不要留到部署期。
一个常见的实践是:在 CI 中先跑 bicep build 生成 JSON 并归档,再对 JSON 执行 what-if,最后才 create。这样每次变更都有可审计的编译产物,评审时看的是 JSON 差异和 what-if 输出,而不是直接看 Bicep 源码猜测行为。对于多环境,参数文件(.bicepparam 或 JSON 参数文件)与模板分离,同一份 main.bicep 通过不同参数部署到 dev/test/prod,避免复制模板。
uniqueString() 结果由输入决定,同一资源组 ID 在同一订阅下稳定,因此适合生成全局唯一名称;但它不是随机数,不要用它生成密码或密钥,敏感值应使用 @secure() 参数或 Key Vault 引用。