3. Bicep语言基础与模块化
Bicep 是 Azure 官方提供的声明式基础设施即代码(IaC)语言,它最终会被编译成 ARM JSON 模板再提交给 Azure Resource Manager 执行。理解这一点非常关键:Bicep 不是一种新的部署引擎,而是 ARM 模板的“源码层”。它保留了 ARM 的全部能力(依赖关系、幂等部署、增量/完整模式、what-if 预演),同时用更接近编程语言的语法消除了大量 JSON 样板。本节围绕语法、模块、参数文件、与 ARM 的对比以及迁移策略展开。
一、Bicep 的核心语法结构
一个 Bicep 文件由参数、变量、资源、模块、输出五类声明构成,声明之间用换行分隔,不使用逗号,也不需要外层大括号包裹整个模板。下面是一个存储账户的最小示例:
@description('部署环境')
@allowed(['dev', 'prod'])
param env string = 'dev'
@secure()
param adminPassword string
var storageName = 'st${env}${uniqueString(resourceGroup().id)}'
resource stg 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageName
location: resourceGroup().location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
}
output storageId string = stg.id
几个要点值得注意:param 支持装饰器(@description、@allowed、@secure、@minLength 等)来约束输入;var 用于定义可复用的表达式;字符串插值使用 ${};uniqueString() 是 ARM 内置函数,用于生成确定性哈希,保证同名资源在不同订阅/资源组下不冲突。资源符号名(如 stg)是 Bicep 特有的引用句柄,通过 stg.id、stg.name 即可拿到运行时属性,无需再写 reference() 或 resourceId()。
依赖关系在 Bicep 中大多自动推导:只要在属性里引用了另一个资源的符号,编译器就会生成 dependsOn。只有在没有属性引用但存在隐式顺序要求时,才需要显式写 dependsOn: [otherResource]。循环使用 [for x in list: {...}],条件部署使用 if (condition),两者可以组合成 [for ... : condition ? {...} : null] 的形式。
二、模块引用:把模板拆成可复用单元
模块(module)是 Bicep 实现复用的主要手段。一个 .bicep 文件既可以独立部署,也可以被其他文件当作模块引用。引用时通过 module 关键字声明,并指定 params 传参:
module storage './modules/storage.bicep' = {
name: 'storageDeploy'
params: {
env: env
adminPassword: adminPassword
}
}
module network './modules/network.bicep' = {
name: 'networkDeploy'
params: { env: env }
}
output storageEndpoint string = storage.outputs.blobEndpoint
模块的 name 是部署记录名,在订阅级或资源组级部署中必须唯一;同一模块在循环中多次实例化时,需要用 name: 'mod-${i}' 保证唯一。模块的输出通过 moduleSymbol.outputs.xxx 访问,这构成了父子模板之间的数据通道。模块可以来自本地相对路径、Bicep 注册表(br: 前缀)或模板规格(ts: 前缀),后两者适合跨团队共享标准化组件。
三、参数文件与部署入口
参数文件用于把环境差异从模板中剥离。Bicep 参数文件(.bicepparam)比 JSON 参数文件更简洁,且支持表达式:
using './main.bicep'
param env = 'prod'
param adminPassword = readEnvironmentVariable('STG_ADMIN_PWD')
部署命令相应简化为 az deployment group create --resource-group rg-app --parameters prod.bicepparam。相比 JSON 参数文件,.bicepparam 可以调用 readEnvironmentVariable() 读取环境变量,避免把密钥写进文件;同时它通过 using 语句与模板强绑定,参数名写错会在编译期报错,而不是等到部署时才失败。
四、与 ARM 模板的对比
从上面的对照可以看出,Bicep 的优势集中在可读性、类型安全和工具链。ARM JSON 中一个存储账户往往需要 40 行以上,而 Bicep 只需 10 行左右;ARM 中引用其他资源要写 [resourceId('Microsoft.Storage/storageAccounts', variables('name'))],Bicep 直接写 stg.id。更重要的是,Bicep 在编译期就能发现参数类型不匹配、属性名拼写错误、缺少必填参数等问题,而 ARM 模板通常要等到部署请求提交后才报错。
但两者并非替代关系。Bicep 编译产物就是 ARM JSON,因此任何 ARM 能力在 Bicep 中都能表达;反过来,已有的 ARM 模板可以通过 bicep decompile 反编译为 Bicep。需要注意的是,反编译结果往往带有 reference()、concat() 等“翻译痕迹”,需要人工重构为符号引用和插值,才能发挥 Bicep 的可读性优势。
五、迁移策略
迁移不建议一次性重写全部模板。推荐按以下节奏推进:
- 先建基线:对现有 ARM 模板执行
az deployment group what-if,记录当前资源状态,作为迁移后的比对基准。 - 反编译起步:用
bicep decompile main.json生成初始 Bicep,先保证能编译通过,不急于优化。 - 重构为模块:把网络、存储、计算等按职责拆成
modules/下的独立文件,入口文件只负责编排和传参。 - 参数外置:把环境差异迁移到
.bicepparam,密钥类参数改用readEnvironmentVariable()或 Key Vault 引用。 - 灰度验证:先在 dev 资源组用增量模式部署,确认 what-if 无意外删除后,再推进到 prod。
掌握语法、模块、参数文件这三块,再配合 what-if 做等价性验证,就能把 Bicep 平稳引入现有的 Azure 运维体系,为后续的模块化治理和策略即代码打下基础。