Terraform CI/CD 集成 #

CI/CD 概述 #

将 Terraform 集成到 CI/CD 流水线可以实现自动化部署、代码质量检查、安全扫描和合规验证。

text
┌─────────────────────────────────────────────────────────────┐
│                    CI/CD 流程                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  代码提交                                                   │
│      │                                                      │
│      ▼                                                      │
│  格式检查 → 语法验证 → 安全扫描                             │
│      │                                                      │
│      ▼                                                      │
│  计划生成                                                   │
│      │                                                      │
│      ▼                                                      │
│  人工审批                                                   │
│      │                                                      │
│      ▼                                                      │
│  执行部署                                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

GitHub Actions #

基本工作流 #

yaml
name: Terraform CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  terraform:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.6.6
      
      - name: Terraform Init
        run: terraform init
      
      - name: Terraform Format
        run: terraform fmt -check
      
      - name: Terraform Validate
        run: terraform validate
      
      - name: Terraform Plan
        run: terraform plan -out=tfplan
      
      - name: Terraform Apply
        if: github.ref == 'refs/heads/main'
        run: terraform apply -auto-approve tfplan

完整工作流 #

yaml
name: Terraform Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  TF_VERSION: 1.6.6
  AWS_REGION: us-east-1

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: ${{ env.TF_VERSION }}
      
      - name: Terraform Init
        run: terraform init -backend=false
      
      - name: Terraform Format
        run: terraform fmt -check -recursive
      
      - name: Terraform Validate
        run: terraform validate
      
      - name: TFLint
        uses: terraform-linters/setup-tflint@v3
        with:
          tflint_version: v0.47.0
      - run: tflint --init
      - run: tflint -f compact

  security:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - uses: actions/checkout@v3
      
      - name: Run tfsec
        uses: aquasecurity/tfsec-action@v1.0.0
        with:
          soft_fail: false
      
      - name: Run Checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: .
          framework: terraform
          soft_fail: false

  plan:
    runs-on: ubuntu-latest
    needs: [validate, security]
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: ${{ env.TF_VERSION }}
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}
      
      - name: Terraform Init
        run: terraform init
      
      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color -out=tfplan
      
      - name: Comment Plan
        uses: actions/github-script@v6
        with:
          script: |
            const plan = `${{ steps.plan.outputs.stdout }}`;
            const output = `## Terraform Plan\n\`\`\`\n${plan}\n\`\`\``;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            });

  apply:
    runs-on: ubuntu-latest
    needs: [validate, security]
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    environment: production
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: ${{ env.TF_VERSION }}
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}
      
      - name: Terraform Init
        run: terraform init
      
      - name: Terraform Plan
        run: terraform plan -out=tfplan
      
      - name: Terraform Apply
        run: terraform apply -auto-approve tfplan

GitLab CI #

基本配置 #

yaml
stages:
  - validate
  - plan
  - apply

variables:
  TF_ROOT: ${CI_PROJECT_DIR}
  TF_IN_AUTOMATION: "true"

.terraform:
  image: hashicorp/terraform:1.6.6
  before_script:
    - cd ${TF_ROOT}
    - terraform init

validate:
  extends: .terraform
  stage: validate
  script:
    - terraform fmt -check -recursive
    - terraform validate

plan:
  extends: .terraform
  stage: plan
  script:
    - terraform plan -out=tfplan
  artifacts:
    paths:
      - ${TF_ROOT}/tfplan
    expire_in: 1 week

apply:
  extends: .terraform
  stage: apply
  script:
    - terraform apply -auto-approve tfplan
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
  environment:
    name: production

完整配置 #

yaml
stages:
  - validate
  - security
  - plan
  - apply
  - destroy

variables:
  TF_ROOT: ${CI_PROJECT_DIR}
  TF_IN_AUTOMATION: "true"
  AWS_REGION: us-east-1

.terraform:
  image: hashicorp/terraform:1.6.6
  before_script:
    - cd ${TF_ROOT}
    - terraform init -backend-config="lockfile=false"

validate:
  extends: .terraform
  stage: validate
  script:
    - terraform fmt -check -recursive
    - terraform validate

tflint:
  stage: security
  image: alpine:latest
  script:
    - apk add --no-cache curl
    - curl -L https://github.com/terraform-linters/tflint/releases/download/v0.47.0/tflint_linux_amd64.zip -o tflint.zip
    - unzip tflint.zip
    - ./tflint --init
    - ./tflint -f compact

tfsec:
  stage: security
  image: aquasec/tfsec:latest
  script:
    - tfsec . --soft-fail=false

plan:
  extends: .terraform
  stage: plan
  script:
    - terraform plan -out=tfplan -input=false
  artifacts:
    paths:
      - ${TF_ROOT}/tfplan
      - ${TF_ROOT}/.terraform
    expire_in: 1 week

apply:
  extends: .terraform
  stage: apply
  script:
    - terraform apply -auto-approve tfplan
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
  environment:
    name: production
    url: https://example.com

destroy:
  extends: .terraform
  stage: destroy
  script:
    - terraform destroy -auto-approve
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
  environment:
    name: production
    action: stop

多环境部署 #

环境矩阵 #

yaml
name: Terraform Multi-Environment

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'dev'
        type: choice
        options:
          - dev
          - staging
          - prod

jobs:
  terraform:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment: [dev, staging, prod]
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
      
      - name: Terraform Init
        run: |
          cd environments/${{ matrix.environment }}
          terraform init
      
      - name: Terraform Plan
        run: |
          cd environments/${{ matrix.environment }}
          terraform plan -out=tfplan
      
      - name: Terraform Apply
        if: github.event.inputs.environment == matrix.environment
        run: |
          cd environments/${{ matrix.environment }}
          terraform apply -auto-approve tfplan

环境保护规则 #

yaml
name: Terraform Production Deploy

on:
  push:
    branches: [main]

jobs:
  apply:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
      
      - name: Terraform Apply
        run: terraform apply -auto-approve

最佳实践 #

1. 使用环境变量 #

yaml
env:
  TF_VAR_environment: ${{ github.ref_name }}
  TF_VAR_project_name: my-project
  TF_IN_AUTOMATION: true

2. 缓存 Provider #

yaml
- name: Setup Terraform
  uses: hashicorp/setup-terraform@v2
  with:
    terraform_version: 1.6.6

- name: Cache Terraform
  uses: actions/cache@v3
  with:
    path: |
      ~/.terraform.d/plugin-cache
      .terraform
    key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }}
    restore-keys: |
      ${{ runner.os }}-terraform-

3. 使用 OIDC 认证 #

yaml
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions
    aws-region: us-east-1

4. 添加审批 #

yaml
jobs:
  plan:
    outputs:
      plan: ${{ steps.plan.outputs.stdout }}
    steps:
      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color

  approve:
    needs: plan
    runs-on: ubuntu-latest
    environment: approval
    steps:
      - name: Approve
        run: echo "Approved"

  apply:
    needs: approve
    runs-on: ubuntu-latest
    steps:
      - name: Terraform Apply
        run: terraform apply -auto-approve

下一步 #

掌握了 CI/CD 集成后,接下来学习 Web 应用部署,通过实战案例巩固所学知识!

最后更新:2026-03-29