You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:45:56