如何在同一Terraform部署中创建并修改Azure应用服务计划
背景概述
近年来,Azure推出了多款V3应用服务计划(App Service plans)的改进产品,但这些新SKU并非在区域内所有部署单元都可用。微软因此建议,若计划使用这些新层级,应:
创建应用服务计划时选择Premium V3层级,确保计划部署在支持Premium V3的部署单元中。之后将应用代码重新部署到新创建的应用中。即使为节约成本将计划缩容至更低层级,也可随时扩容回Premium V3,因为部署单元支持该层级。
问题场景
我正使用Terraform构建Azure环境,为确保未来可扩容至V3层级,需实现以下需求:
- 定义一个接受目标应用服务计划层级的变量;
- 使用最新的
P0v3层级创建应用服务计划,确保所有当前V3层级可用; - 将步骤2创建的同一应用服务计划调整至目标层级。
有BICEP使用经验的我,尝试通过重新定义资源并依赖初始版本来实现,但未达预期:
variables.tf代码:
variable "asp_tier" { description = "应用服务计划层级" default = "S1" } variable "region" { description = "部署目标区域" default = "westus2" }
app-service-plans.tf代码:
resource "azurerm_resource_group" "rg_example" { name = "Example-Resources" location = var.region } # 创建基于Premium V3 SKU的应用服务计划,确保部署单元支持全V3层级 resource "azurerm_service_plan" "fix_asp" { name = "asp-example-01" resource_group_name = azurerm_resource_group.rg_example.name location = azurerm_resource_group.rg_example.location sku_name = "P0v3" worker_count = 1 os_type = "Windows" } # 该资源执行时报错 resource "azurerm_service_plan" "target_asp" { depends_on = [azurerm_service_plan.fix_asp] name = "asp-example-01" resource_group_name = azurerm_resource_group.rg_example.name location = azurerm_resource_group.rg_example.location sku_name = var.asp_tier worker_count = 1 os_type = "Windows" }
执行terraform apply时出现错误:
Error: ID为"/subscriptions/.../resourceGroups/rg-example/providers/Microsoft.Web/serverfarms/asp-example-01"的资源已存在,若要通过Terraform管理需将其导入状态。请查看"azurerm_service_plan"资源文档了解详情。
错误指向target_asp资源:
│ with azurerm_service_plan.target_asp, │ on app-service-plans.tf line xx, in resource "azurerm_service_plan" "target_asp": │ xx: resource "azurerm_service_plan" "target_asp" {
请问如何在同一Terraform部署中完成资源的创建与后续修改操作?
Terraform是声明式工具,不允许在配置中重复定义同一名称的Azure资源(每个资源块对应唯一的基础设施对象),你之前的写法相当于尝试创建两个同名的应用服务计划,自然会报错。要实现“先以P0v3创建,再调整至目标层级”的需求,可采用以下两种方式:
方法一:用null_resource配合Azure CLI完成同部署流程内的修改
这种方式可以在单次terraform apply中完成创建与层级调整:
修改后的app-service-plans.tf:
resource "azurerm_resource_group" "rg_example" { name = "Example-Resources" location = var.region } # 第一步:以P0v3创建应用服务计划,锁定支持V3的部署单元 resource "azurerm_service_plan" "asp" { name = "asp-example-01" resource_group_name = azurerm_resource_group.rg_example.name location = azurerm_resource_group.rg_example.location sku_name = "P0v3" worker_count = 1 os_type = "Windows" } # 第二步:仅当目标层级与P0v3不同时,通过Azure CLI调整层级 resource "null_resource" "adjust_asp_tier" { depends_on = [azurerm_service_plan.asp] # 目标层级为P0v3时不执行此操作 count = var.asp_tier != "P0v3" ? 1 : 0 provisioner "local-exec" { command = <<EOT az appservice plan update \ --name "${azurerm_service_plan.asp.name}" \ --resource-group "${azurerm_resource_group.rg_example.name}" \ --sku "${var.asp_tier}" EOT } # 跟踪层级变化,仅当目标层级修改时重新执行调整命令 triggers = { current_tier = var.asp_tier } }
注意事项:
- 执行Terraform的环境需已安装并配置好Azure CLI,且账号拥有修改应用服务计划的权限;
triggers字段避免了每次部署都重复执行调整命令,仅当var.asp_tier变更时才触发。
方法二:分两次部署(更符合Terraform声明式设计)
如果可以接受分两步执行部署,这是更规范的方案:
- 第一次部署:临时将
var.asp_tier设置为P0v3,执行terraform apply创建应用服务计划; - 第二次部署:将
var.asp_tier改回目标层级(比如S1),再次执行terraform apply,Terraform会自动调整现有应用服务计划的层级。
修改后的app-service-plans.tf只需保留单个资源定义:
resource "azurerm_resource_group" "rg_example" { name = "Example-Resources" location = var.region } resource "azurerm_service_plan" "asp" { name = "asp-example-01" resource_group_name = azurerm_resource_group.rg_example.name location = azurerm_resource_group.rg_example.location sku_name = var.asp_tier worker_count = 1 os_type = "Windows" }
说明:
- 第一次部署后,应用服务计划已部署在支持V3的单元中,后续无论如何调整层级,都能随时扩容回V3系列SKU;
- 完全遵循Terraform的声明式模型,无需额外脚本操作。
内容的提问来源于stack exchange,提问作者Zhaph - Ben Duguid

