基于GitHub Actions+Terraform Cloud+Azure的多目录部署问题求解
解决方案一:多目录Terraform部署标准最优方案
针对多目录(app1、app2等)的基础设施部署场景,推荐结合GitHub Actions矩阵策略+路径触发+Terraform Workspace隔离的方案,既符合最佳实践,又能高效实现目录级别的独立部署。
核心思路
- 路径触发:仅当指定目录或工作流文件变更时触发部署流程,避免不必要的运行。
- 矩阵策略:复用工作流步骤,对每个目标目录单独执行Terraform操作。
- 状态隔离:每个目录对应一个Terraform Cloud Workspace,确保基础设施状态互不干扰。
完整工作流示例
name: Terraform Multi-Directory Deployment on: push: paths: - 'app1/**' - 'app2/**' - '.github/workflows/terraform.yml' pull_request: paths: - 'app1/**' - 'app2/**' - '.github/workflows/terraform.yml' jobs: terraform-deploy: runs-on: ubuntu-latest strategy: matrix: target_dir: ['app1', 'app2'] # 新增目录只需添加新条目 steps: - name: 检出代码 uses: actions/checkout@v4 - name: 筛选变更目录 id: filter run: | # 根据事件类型获取变更目录 if [ "${{ github.event_name }}" = "push" ]; then CHANGED=$(git diff --name-only HEAD^ HEAD | cut -d'/' -f1 | sort -u) else CHANGED=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | cut -d'/' -f1 | sort -u) fi # 判断当前矩阵目录是否在变更列表中 if echo "$CHANGED" | grep -q "${{ matrix.target_dir }}"; then echo "deploy=true" >> $GITHUB_OUTPUT else echo "deploy=false" >> $GITHUB_OUTPUT fi - name: 配置Terraform环境 if: steps.filter.outputs.deploy == 'true' uses: hashicorp/setup-terraform@v3 with: cli_config_credentials_token: ${{ secrets.TF_CLOUD_TOKEN }} - name: Terraform初始化 if: steps.filter.outputs.deploy == 'true' working-directory: ${{ matrix.target_dir }} run: terraform init -reconfigure - name: Terraform Plan if: steps.filter.outputs.deploy == 'true' working-directory: ${{ matrix.target_dir }} run: terraform plan -out=tfplan env: ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - name: Terraform Apply if: steps.filter.outputs.deploy == 'true' && github.event_name == 'push' working-directory: ${{ matrix.target_dir }} run: terraform apply tfplan env: ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
方案优势
- 扩展性强:新增目录只需在
matrix.target_dir中添加条目。 - 资源高效:仅变更目录会执行部署操作,节省CI/CD资源。
- 状态安全:每个目录的Terraform状态独立存储,避免互相影响。
解决方案二:修复现有脚本在GitHub Actions中的运行问题
你遇到的HEAD^报错,本质是GitHub Actions默认采用浅克隆(仅拉取最新1次提交),导致找不到HEAD^的历史提交;同时PR场景下HEAD^的引用逻辑也不适用。以下是具体修复步骤:
1. 修改代码检出步骤,获取完整提交历史
在工作流的checkout步骤中添加fetch-depth: 0参数,拉取完整仓库历史:
- name: 检出代码 uses: actions/checkout@v4 with: fetch-depth: 0 # 获取所有提交历史,支持HEAD^等引用
2. 优化变更目录检测脚本,兼容不同事件类型
修改脚本,区分push和pull_request事件,使用正确的提交对比范围:
#!/bin/bash # 根据事件类型确定提交对比范围 if [ "${{ github.event_name }}" = "push" ]; then # Push事件:对比当前提交与上一次提交 DIFF_REF="HEAD^ HEAD" elif [ "${{ github.event_name }}" = "pull_request" ]; then # PR事件:对比PR基准分支与当前分支的提交 DIFF_REF="${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}" else echo "不支持的事件类型" exit 1 fi # 获取变更的包含main.tf的目录 CHANGED_DIRS=$(git diff --name-only $DIFF_REF | xargs dirname | grep -E '^(app1|app2)' | sort -u) # 遍历目录执行Terraform Plan for dir in $CHANGED_DIRS; do echo "=== 处理目录: $dir ===" cd "$dir" || continue terraform init -reconfigure terraform plan cd .. done
修复原理
fetch-depth: 0解决了浅克隆导致的历史提交缺失问题,让HEAD^在push场景下可用。- PR场景下改用基准分支SHA与当前分支SHA对比,避免
HEAD^引用错误。
内容的提问来源于stack exchange,提问作者Fuat Ulugay
相关产品推荐
相关产品推荐

