Terraform在GitHub Actions中拟销毁存储账户的问题及解决
Terraform在GitHub Actions中误报销毁Azure存储资源的解决方法
问题场景
我通过Terraform管理Azure存储账户,在个人虚拟机(VM)上执行terraform plan和terraform apply时一切正常,资源状态保持同步。但将流程迁移到GitHub Actions自动化执行时,首次运行plan操作显示会销毁已创建的存储容器及所有子模块。我最初怀疑是运行机器不同导致的问题,不想让现有资源被误删,想了解是否可以通过terraform import导入资源解决,或者有其他可行方案。
根本原因
问题核心并非运行机器差异,而是状态文件不共享:本地VM使用的是本地存储的terraform.tfstate文件,而GitHub Actions的运行器是临时环境,默认生成全新的本地状态文件。Terraform对比新状态与远程Azure资源时,会判定现有资源不在当前状态管理范围内,因此计划销毁这些资源后重新创建。
解决方法
使用Terraform远程后端集中存储状态文件,让本地环境和GitHub Actions共享同一状态源,确保Terraform能识别已存在的资源。这里选择Azure Blob Storage作为后端(azurerm类型),实现状态文件的集中管理和版本控制。
配置代码示例
main.tf(添加Backend配置)
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "~>3.0" } } # 配置Azure远程后端存储状态文件 backend "azurerm" { resource_group_name = "resource-group" storage_account_name = "xxxxxxx" container_name = "terraform-state" key = "terraform.tfstate" } } provider "azurerm" { features {} skip_provider_registration = true } # 存储账户模块配置 locals { storageAccount = fileexists("${var.storageAccountfilepath}/storageAccount.json") ? jsondecode(file("${var.storageAccountfilepath}/storageAccount.json")) : null } module "storageAccount" { for_each = { for element in coalesce(local.storageAccount, []) : element.name => element } source = "./storageAccount" storageAccountName = each.value.name resource_group_name = var.resource_group_name filepath = "${var.storageAccountfilepath}${each.value.name}" }
storageAccount.tf(存储账户资源定义)
resource "azurerm_storage_account" "storageAccount" { name = var.storageAccountName resource_group_name = var.resource_group_name location = var.location account_tier = "Standard" account_replication_type = "LRS" table_encryption_key_type = "Account" queue_encryption_key_type = "Account" infrastructure_encryption_enabled = "false" tags = { env = "sandbox-test" owner = "som" CreatedBy = "soms" } blob_properties { change_feed_enabled = false container_delete_retention_policy { days = 7 } delete_retention_policy { days = 7 } } # 防止资源被意外销毁 lifecycle { prevent_destroy = true } }
GitHub Actions工作流配置
name: Terraform Workflow on: push: branches: - develop # pull_request: # branches: # - main # OIDC认证所需权限 permissions: id-token: write contents: read pull-requests: write env: TERRAFORM_PATH: './terraform' jobs: terraform-plan: name: 'Terraform Plan' runs-on: [Windows,NonSOX] env: ARM_SKIP_PROVIDER_REGISTRATION: true outputs: tfplanExitCode: ${{ steps.tf-plan.outputs.exitcode }} steps: - name: 拉取代码 uses: actions/checkout@v3 - name: 安装Terraform uses: hashicorp/setup-terraform@v2 with: terraform_wrapper: false - name: 从Vault获取密钥 uses: hashicorp/vault-action@v2.7.0 id: get_secrets with: exportToken: true method: jwt path: github-actions-oidc url: xxxxxxx role: actions-runner-finance tlsSkipVerify: true secrets: | /finance/data/terraform/AZURE_CLIENT_ID AZURE_CLIENT_ID | AZURE_CLIENT_ID ; /finance/data/terraform/AZURE_SUBSCRIPTION_ID AZURE_SUBSCRIPTION_ID | AZURE_SUBSCRIPTION_ID ; /finance/data/terraform/AZURE_TENANT_ID AZURE_TENANT_ID | AZURE_TENANT_ID ; /finance/data/terraform/ARM_CLIENT_SECRET ARM_CLIENT_SECRET | ARM_CLIENT_SECRET ; - name: Terraform初始化 run: | pushd './${{ env.TERRAFORM_PATH }}' terraform init - name: 检查Terraform代码格式 run: | terraform fmt -check - name: 执行Terraform Plan id: tf-plan env: ARM_CLIENT_ID: ${{ steps.get_secrets.outputs.AZURE_CLIENT_ID }} ARM_SUBSCRIPTION_ID: ${{ steps.get_secrets.outputs.AZURE_SUBSCRIPTION_ID }} ARM_TENANT_ID: ${{ steps.get_secrets.outputs.AZURE_TENANT_ID }} ARM_CLIENT_SECRET: ${{ steps.get_secrets.outputs.ARM_CLIENT_SECRET }} run: | pushd './${{ env.TERRAFORM_PATH }}' terraform plan -detailed-exitcode -no-color -out tfplan echo "exitcode=$LASTEXITCODE" >> $GITHUB_ENV if ($LASTEXITCODE -eq 1) { echo "Terraform Plan执行失败!" exit 1 } else { exit 0 }
额外说明
- 配置远程后端后,本地执行
terraform init会将本地状态文件迁移到Azure Blob Storage,后续所有操作都基于远程状态。 - 建议在资源定义中添加
lifecycle { prevent_destroy = true },即使出现误操作也能阻止资源被销毁。 - 如果之前已经有未纳入远程状态的资源,可以使用
terraform import命令将现有资源导入到远程状态中,但优先推荐使用远程后端来避免此类问题。
内容的提问来源于stack exchange,提问作者Somsubhra Mukherjee
相关产品推荐
相关产品推荐

