Azure Bicep依赖模块时序问题:函数应用IP规则未合并
问题场景
需求是给现有Function App添加固定IP限制规则,当前部署逻辑:
main.bicep中调用ipSecurityRestrictions模块,目的是把现有IP规则和新增的代理IP规则合并functionAppDeploy模块负责部署Function App,同时会添加一条测试笔记本的IP允许规则- 实际部署后发现:代理IP规则只合并了部署前就存在的规则,
functionAppDeploy里新增的测试IP规则完全丢失
问题根源:ipRestrictions模块里用reference获取的是部署前的sites/config配置,哪怕加了dependsOn依赖functionAppDeploy,ARM也没法实时获取到刚部署的测试IP规则——因为reference在部署阶段就会解析旧值,而不是等待依赖模块完成后的新值。
要求:解决方案不能破坏ipSecurityRestrictions模块的单一职责(也就是它只负责添加代理IP规则,不耦合其他业务规则)
可行解决方案
方案1:将额外IP规则作为参数传入代理IP模块
核心思路:把functionAppDeploy要加的测试IP规则从模块里抽出来,在main.bicep中统一传递给ipRestrictions模块,由它负责合并所有规则,避免多个模块修改同一个sites/config资源。
- 修改
ipSecurityRestrictions模块,新增可选参数接收额外IP规则:
param appSvcName string param existingIpSecurityRestrictions array = [] param additionalIpSecurityRestrictions array = [] // 新增参数,用于接收其他模块需要添加的规则 resource appSvc 'Microsoft.Web/sites@2021-02-01' existing = { name: appSvcName } var proxyIpAddresses = ['xxx.xxx.xxx.250/32','xxx.xxx.xxx.245/32','xxx.xxx.xxx.251/32'] var proxyIpRestrictions = [for (ip,i) in proxyIpAddresses: { ipAddress: ip action: 'Allow' tag: 'Default' priority: 900 + i name: 'ProxyIp_${i}' description: 'Allow request from proxy ${i}' }] resource sitesConfig 'Microsoft.Web/sites/config@2021-02-01' = { name: 'web' parent: appSvc properties: { // 按顺序合并:原有规则 → 额外规则 → 代理IP规则 ipSecurityRestrictions: concat(existingIpSecurityRestrictions, additionalIpSecurityRestrictions, proxyIpRestrictions) } }
- 修改
main.bicep,把测试IP规则作为参数传入ipRestrictions模块:
module ipRestrictions 'common.appSvc.ipSecurityRestrictions.bicep' = { scope: resourceGroup(utrnRg) name: 'ipRestrictionsDeploy' params: { appSvcName: functionAppName existingIpSecurityRestrictions: reference(resourceId('Microsoft.Web/sites/config', functionAppName, 'web'), '2021-02-01').ipSecurityRestrictions // 传入测试笔记本的IP规则 additionalIpSecurityRestrictions: [ { ipAddress: '${pcPublicIp}/32' action: 'Allow' tag: 'Default' priority: 101 name: 'laptop ip' description: 'Allow requests from test laptop' } ] } dependsOn: [ functionAppDeploy ] }
- 修改
functionAppDeploy模块,移除其中的ipSecurityRestrictions配置,避免多个模块修改同一资源导致冲突:
resource sitesConfig 'Microsoft.Web/sites/config@2021-02-01' = { name: 'web' parent: functionApp properties: { // 删掉原有的ipSecurityRestrictions配置,统一由ipRestrictions模块管理 } }
这个方案完全保留了ipSecurityRestrictions模块的单一职责——它只负责添加代理IP规则,额外规则由外部传入,不耦合任何业务逻辑。
方案2:让Function App模块输出其IP规则(避免重复定义)
如果不想在main.bicep中重复写测试IP规则,可以让functionAppDeploy模块输出自己定义的规则,再传递给ipRestrictions模块。
- 修改
utrngen.functionApp.bicep,添加输出:
// 保留原有的IP规则配置,但后续会由ipRestrictions模块统一合并 resource sitesConfig 'Microsoft.Web/sites/config@2021-02-01' = { name: 'web' parent: functionApp properties: { ipSecurityRestrictions: [ { ipAddress: '${pcPublicIp}/32' action: 'Allow' tag: 'Default' priority: 101 name: 'laptop ip' description: 'Allow requests from test laptop' } ] } } // 输出模块定义的IP规则 output appIpRestrictions array = sitesConfig.properties.ipSecurityRestrictions
修改
ipSecurityRestrictions模块,新增additionalIpSecurityRestrictions参数(同方案1)。修改
main.bicep,引用functionAppDeploy的输出作为参数:
module ipRestrictions 'common.appSvc.ipSecurityRestrictions.bicep' = { scope: resourceGroup(utrnRg) name: 'ipRestrictionsDeploy' params: { appSvcName: functionAppName existingIpSecurityRestrictions: reference(resourceId('Microsoft.Web/sites/config', functionAppName, 'web'), '2021-02-01').ipSecurityRestrictions additionalIpSecurityRestrictions: functionAppDeploy.outputs.appIpRestrictions } dependsOn: [ functionAppDeploy ] }
- 关键调整:修改
functionAppDeploy的sites/config,让它合并原有规则和自身规则,避免被ipRestrictions模块覆盖:
resource functionApp 'Microsoft.Web/sites@2021-02-01' existing = { name: functionAppName } resource sitesConfig 'Microsoft.Web/sites/config@2021-02-01' = { name: 'web' parent: functionApp properties: { ipSecurityRestrictions: concat( reference(resourceId('Microsoft.Web/sites/config', functionAppName, 'web'), '2021-02-01').ipSecurityRestrictions, [ { ipAddress: '${pcPublicIp}/32' action: 'Allow' tag: 'Default' priority: 101 name: 'laptop ip' description: 'Allow requests from test laptop' } ] ) } }
这个方案适合不想重复写规则的场景,但要注意部署顺序的稳定性,相比方案1,它还是有两个模块修改同一个资源的风险,所以优先推荐方案1。
方案3:依赖部署顺序(不推荐)
ARM默认是增量部署,但如果多个模块修改同一个sites/config,最后部署的模块会覆盖之前的配置。可以调整main.bicep中模块的部署顺序,但这种方式完全依赖部署顺序,一旦模块依赖关系变化就会出问题,稳定性差,不建议使用。
内容的提问来源于stack exchange,提问作者Rob Bowman

