JenkinsでTerraformのCI/CDパイプラインを構築する方法

1. 概要

  • JenkinsでTerraformのCI/CDを構築する手順
  • Jenkinsfileでのplan/applyパイプライン
  • AWS認証情報をJenkinsのCredentialsで管理する方法
  • plan結果の承認ゲートの設定

Jenkinsはオンプレミスでの実績があるCI/CDサーバーです。Terraform専用機能はありませんが、Jenkinsfileでplan/applyのパイプラインを自由に構築できます。


2. Terraform設定

terraform {
  required_version = ">= 1.9"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    bucket         = "my-company-tfstate"
    key            = "app/terraform.tfstate"
    region         = "ap-northeast-1"
    encrypt        = true
    dynamodb_table = "terraform-lock"
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

variable "environment" {
  description = "環境名"
  type        = string
  default     = "dev"
}

data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = "t3.micro"

  root_block_device {
    volume_size = 20
    volume_type = "gp3"
    encrypted   = true
  }

  tags = {
    Name        = "${var.environment}-web"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

3. Jenkinsfile(宣言型パイプライン)

// Jenkinsfile
pipeline {
    agent any

    environment {
        AWS_REGION      = 'ap-northeast-1'
        TF_VERSION      = '1.9.0'
        TF_VAR_environment = 'dev'
    }

    tools {
        // Jenkins Tool設定でTerraformを登録している場合
        // terraform 'terraform-1.9'
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Terraform Init') {
            steps {
                withCredentials([
                    string(credentialsId: 'aws-access-key-id',
                           variable: 'AWS_ACCESS_KEY_ID'),
                    string(credentialsId: 'aws-secret-access-key',
                           variable: 'AWS_SECRET_ACCESS_KEY')
                ]) {
                    sh '''
                        terraform init \
                          -backend-config="bucket=my-company-tfstate" \
                          -backend-config="key=app/terraform.tfstate" \
                          -backend-config="region=${AWS_REGION}"
                    '''
                }
            }
        }

        stage('Terraform Validate') {
            steps {
                sh 'terraform validate'
            }
        }

        stage('Terraform Plan') {
            steps {
                withCredentials([
                    string(credentialsId: 'aws-access-key-id',
                           variable: 'AWS_ACCESS_KEY_ID'),
                    string(credentialsId: 'aws-secret-access-key',
                           variable: 'AWS_SECRET_ACCESS_KEY')
                ]) {
                    sh 'terraform plan -no-color -out=tfplan | tee plan_output.txt'
                }
                // plan結果をJenkinsのArtifactとして保存
                archiveArtifacts artifacts: 'plan_output.txt', fingerprint: true
            }
        }

        stage('Approval') {
            // mainブランチのみ承認ゲートを設ける
            when {
                branch 'main'
            }
            steps {
                // 手動承認(デフォルト60分でタイムアウト)
                timeout(time: 60, unit: 'MINUTES') {
                    input message: 'Terraform Applyを実行しますか?',
                          ok: 'Apply',
                          submitter: 'admin,infra-team'
                }
            }
        }

        stage('Terraform Apply') {
            when {
                branch 'main'
            }
            steps {
                withCredentials([
                    string(credentialsId: 'aws-access-key-id',
                           variable: 'AWS_ACCESS_KEY_ID'),
                    string(credentialsId: 'aws-secret-access-key',
                           variable: 'AWS_SECRET_ACCESS_KEY')
                ]) {
                    sh 'terraform apply tfplan'
                }
            }
        }
    }

    post {
        always {
            // tfplanファイルを削除(Stateファイルのリークを防ぐ)
            sh 'rm -f tfplan'
        }
        success {
            echo 'Terraform pipeline succeeded'
        }
        failure {
            echo 'Terraform pipeline failed'
        }
    }
}

4. AWS認証情報の管理

Credentials設定(Jenkins UI):

Jenkins → Manage Jenkins → Credentials → System → Global → Add Credentials

  • Kind: Secret text
  • ID: aws-access-key-id / aws-secret-access-key
  • Secret: IAMアクセスキーID / シークレットキー

IAMロールで認証する場合(JenkinsがEC2上で動いている場合):

// IAMロールをEC2インスタンスプロファイルで設定している場合
// AWS認証情報の記述は不要
stage('Terraform Plan') {
    steps {
        sh 'terraform plan -no-color'
    }
}

5. 関連記事


6. まとめ

  • Jenkinsfileの宣言型パイプラインでinit → validate → plan → 承認 → applyを構築できる
  • AWS認証はJenkins Credentialsに保存するか、EC2インスタンスプロファイル(推奨)を使う
  • inputステップで手動承認ゲートを設けることで意図しないapplyを防止できる
  • Terraform専用機能はないが、Jenkinsの柔軟なパイプラインでカスタムワークフローを実現できる

対象バージョン: Terraform >= 1.9 / Jenkins LTS 公式ドキュメント: https://www.jenkins.io/doc/book/pipeline/