【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

GitHub でソースコードを管理しているとして、複数人で Terraform を利用する場合、それぞれが terraform plan、terraform apply コマンドを実行することになります。

 

今回は GitHubActions を利用して、Terraform でデプロイをする CI/CD パイプライン環境を構築したので解説します。

 

 

GitHub Actions を選んだ理由

  • GitHub でソースコードを管理している。
  • terraform plan、terraform apply コマンドを実行するだけのシンプルなフロー。
  • 月2,000分までは無料で使える。
  • AWS CodeCommit、AWS CodeBuild、AWS Pipeline で構成すると複雑で属人化しそう。

 

 

ざっくりとした流れ

ざっくりとした流れは以下のようになるかと思います。

それぞれがローカルでブランチを切ってコードを作成していきます。

そしてリモートブランチにコミット・プッシュします。

コミットしたコードをmainブランチにプルリクを出すところから CI/CD が稼働します。

一度レビュアーのチェックを入れて承認をさせたいので、プルリクの段階で terraform plan コマンドを実行してコメントとして残します。

(terraform plan コマンドの結果を特にコメントとして残さなくてもいいですが、レビュアーがわざわざコードを見たり terraform plan の結果をいろんなページを廻って閲覧するのは面倒かなと思ってコメントにまとめた方がいいかと思いました。)

レビュアーがレビューして問題なければmainブランチにマージをします。マージをすると自動的に terraform apply が実行されます。

ただ terraform の場合、terraform plan が OK でも terraform apply でエラーになることがあるので、そこをどうするか。

 

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

フロー図

構成図としては以下のようになると思います。

各ブランチにプッシュし、レビュアーがレビューして問題なければ main ブランチにマージする。マージすると自動的に terraform apply コマンドを実行され、AWS 上にリソースがデプロイされるという構成です。

 

GitHubの公式サイトには以下のフロー図があります。

https://learn.hashicorp.com/tutorials/terraform/github-actions

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

GitHub Actionsの料金

GitHub Actions はプライベートリポジトリの場合は 1か月 2,000分までは無料で使えます。

1か月を20営業日と計算すると、1日あたり 100分(1時間40分)使える計算になります。

1デプロイ1分と計算すると、1日100デプロイが無料でできる計算となります。

そこそこの企業で利用してもなんとか無料枠で行ける範囲となりますでしょうか。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

まずはシンプルなサンプルワークフローを試す

まずは GitHub Actions のシンプルなサンプルワークフローを試してみます。

 

新規の GitHub リポジトリを作成する

GitHub にログインしてリポジトリの画面に移動し「New」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

 

「Create new repository」画面で下図のように設定します。

最初に「Add .gitignore」で .gitignore ファイルを追加しておくことをお勧めします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

設定に問題がなければ下にスクロールして「Create repository」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

リポジトリが作成されることを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

ローカルにリポジトリをクローンする

次にローカル(私の場合は Cloud9)にリポジトリをクローンします。

 

■ローカルにリポジトリをクローンする

$ git clone git@github.com:xxxxxxx/test_demo.git
Cloning into ‘test_demo’…
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

 

 

 

■リポジトリの確認

$ ls -l | grep test_demo
drwxrwxr-x 3 ec2-user ec2-user 36 Sep 11 15:02 test_demo

 

 

 

 

■リポジトリの確認

$ cd test_demo/
$ ls -la
total 4
drwxrwxr-x 3 ec2-user ec2-user 36 Sep 11 15:02 .
drwxrwxr-x 6 ec2-user ec2-user 69 Sep 11 15:02 ..
drwxrwxr-x 8 ec2-user ec2-user 163 Sep 11 15:02 .git
-rw-rw-r– 1 ec2-user ec2-user 716 Sep 11 15:02 .gitignore

 

 

 

シンプルな GitHub Actions を作成する

ここまで来たらデモ用のシンプルな GitHub Actions のワークフローを作って動かしてみます。

 

「Actions」をクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

「set up a workflow yourself」をクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

ファイル名(/.github/workflows/test_demo.yml)を入力し「Start commit」ボタンをクリックします。

ソースコードはデフォルトのままです。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

以下のように .github/workflows ディレクトリに「test_demo.yml」ファイルが作成されることを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

ちなみに GitHub Actions のサンプルソースコード「test_demo.yml」ファイルの中身は以下のようになっています。

 

 

■デモ用のソースコード(デフォルトのまま)

# This is a basic workflow to help you get started with Actions 
 
name: CI 
 
# Controls when the workflow will run 
on: 
  # Triggers the workflow on push or pull request events but only for the “main” branch 
  push: 
    branches: [ “main” ] 
  pull_request: 
    branches: [ “main” ] 
 
  # Allows you to run this workflow manually from the Actions tab 
  workflow_dispatch: 
 
# A workflow run is made up of one or more jobs that can run sequentially or in parallel 
jobs: 
  # This workflow contains a single job called “build” 
  build: 
    # The type of runner that the job will run on 
    runs-on: ubuntu-latest 
 
    # Steps represent a sequence of tasks that will be executed as part of the job 
    steps: 
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 
      – uses: actions/checkout@v3 
 
      # Runs a single command using the runners shell 
      – name: Run a one-line script 
        run: echo Hello, world! 
 
      # Runs a set of commands using the runners shell 
      – name: Run a multi-line script 
        run: | 
          echo Add other actions to build, 
          echo test, and deploy your project. 

 

コードの内容は main ブランチに push か pull request を実行すると GitHub Actions のワークフローが自動的に実行されます。

実行されると echo コマンドで「Hello word!」が出力されます

 

 

 

ブランチを切ってコードを作成し git commit git push をする

次にブランチを切ってテスト用コードを作成し git commit、git push をします。

 

まずは最初に git pull を実行します。

 

■git pull を実施

git pull 
remote: Enumerating objects: 6, done. 
remote: Counting objects: 100% (6/6), done. 
remote: Compressing objects: 100% (3/3), done. 
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 
Unpacking objects: 100% (5/5), 1.28 KiB | 657.00 KiB/s, done. 
From github.com:xxxxxxxx/test_demo 
   fdb7857..402c5b3  main       -> origin/main 
There is no tracking information for the current branch. 
Please specify which branch you want to merge with. 
See git-pull(1) for details. 
 
    git pull   
 
If you wish to set tracking information for this branch you can do so with: 
 
    git branch –set-upstream-to=origin/ demo_20220911 
 

 

 

 

 

■ブランチを切ってコードを作成しリモートリポジトリに push まで実行する

(main) $ git branch demo_20220911 
(main) $ git checkout demo_20220911  
Switched to branch ‘demo_20220911’ 
(demo_20220911) $ touch test.txt 
(demo_20220911) $ echo “test” > test.txt  
(demo_20220911) $ cat test.txt  
test 

(demo_20220911) $ ls 
test.txt 
sasagawam@gmail.com:~/environment/test/test_demo (demo_20220911) $ git status 
On branch demo_20220911 
Untracked files: 
  (use “git add …” to include in what will be committed) 
        test.txt 
 
nothing added to commit but untracked files present (use “git add” to track) 
(demo_20220911) $ git add . 
(demo_20220911) $ git status 
On branch demo_20220911 
Changes to be committed: 
  (use “git restore –staged …” to unstage) 
        new file:   test.txt 
 


(demo_20220911) $ git commit -m “demo_01” 
[demo_20220911 1603f9a] demo_01 
 Committer: EC2 Default User <ec2-user@ip-172-31-44-157.ap-northeast-1.compute.internal> 
Your name and email address were configured automatically based 
on your username and hostname. Please check that they are accurate. 
You can suppress this message by setting them explicitly: 
 
    git config –global user.name “Your Name” 
    git config –global user.email you@example.com 
 
After doing this, you may fix the identity used for this commit with: 
 
    git commit –amend –reset-author 
 
 1 file changed, 1 insertion(+) 
 create mode 100644 test.txt 


(demo_20220911) $ git push origin demo_20220911  
Enumerating objects: 4, done. 
Counting objects: 100% (4/4), done. 
Compressing objects: 100% (2/2), done. 
Writing objects: 100% (3/3), 314 bytes | 314.00 KiB/s, done. 
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 
To github.com:xxxxxxxxx/test_demo.git 
   fdb7857..1603f9a  demo_20220911 -> demo_20220911 

 

この段階ではまだブランチ(demo_20220911)上に push しているだけなので GitHub Actions は反応しません。

 

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

main ブランチにマージして GitHub Actions のワークフローを実行する

ここまで環境が出来上がったら次に main ブランチにマージして GitHub Actions のワークフローを実行してみます。

 

 

「Pull request」をクリックして「New pull request」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

ブランチを選択して「Create pull request」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

必要ならコメントを記載し「Create pull request」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

GitHub Actions のワークフローが実行されたことを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

 

Hello Worldが表示されていることを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

以上が、GitHub Actions の一番最初のサンプルコード実行でした。

 

 

OIDC を作成する

次に Terraform の CI/CD パイプラインを構築する前に事前準備をします。

今回は AWS 環境でリソースをデプロイします。

セキュリティを考慮して AWS のアクセスキーとシークレットアクセスキーを使わずに GitHub Actions から AWS 認証をして AWS API へセキュアにリクエストをする為に OIDC を作成します。

 

OIDC は Terraform で作成します。

 

■OIDC 作成のコード

$ vi iam.tf  
#============================================================================ 
# Github Actions 用ロール 
#============================================================================ 
# —————————————————————————– 
# GitHub Actions プロバイダー設定 
# —————————————————————————– 
resource “aws_iam_openid_connect_provider” “terra_cicd_demo” { 
  url             = “https://token.actions.githubusercontent.com” 
  client_id_list  = [“sts.amazonaws.com”] 
  # このコードは固定値 
  thumbprint_list = [“6938fd4d98bab03faadb97b34396831e3780aea1”] 

 
# —————————————————————————– 
# GitHub Actions 用ロール作成 
# —————————————————————————– 
resource “aws_iam_role” “terra_cicd_demo_oidc_role” { 
  name = “TerraCICDDemoOIDCRole” 
  path = “/” 
  assume_role_policy = jsonencode({ 
    Version = “2012-10-17” 
    Statement = [{ 
      Effect = “Allow” 
      Action = “sts:AssumeRoleWithWebIdentity” 
      Principal = { 
        Federated = aws_iam_openid_connect_provider.terra_cicd_demo.arn 
      } 
      Condition = { 
        StringLike = { 
          “token.actions.githubusercontent.com:sub” = [ 
            # リポジトリ制限 

            # リポジトリに複数のブランチを作成している場合は特定のブランチを絞ることができる。リスト化もできる。 
            “repo:xxxxx/test_demo:ref:refs/heads/demo_20220911”,
          ] 
        } 
      } 
    }] 
  }) 

 
# —————————————————————————– 
# ポリシーのアタッチ(AdministratorAccess_attachment) 
# —————————————————————————– 
resource “aws_iam_role_policy_attachment” “AdministratorAccess_attachment” { 
  role       = aws_iam_role.terra_cicd_demo_oidc_role.name 
  policy_arn = “arn:aws:iam::aws:policy/AdministratorAccess” 
}   

 

 

ロール名を「TerraCICDDemoOIDCRole」にしてポリシー「AdministratorAccess」を割り当てます。

今回は IAM ロールを作成するので「AdministratorAccess」を割り当てましたが、適宜必要最低限のポリシーを割り当てます。

 

 

6938fd4d98bab03faadb97b34396831e3780aea1 はどこから来た数値なのか?

6938fd4d98bab03faadb97b34396831e3780aea1 の数値は何でしょうか?

調べてみると OIDC ID プロバイダーのサムプリントのようです。

 

GitHub Actions – Update on OIDC based deployments to AWS

https://github.blog/changelog/2022-01-13-github-actions-update-on-oidc-based-deployments-to-aws/

 

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

OpenID Connect ID プロバイダーのサムプリントの取得

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html

 

 

 

初期段階で作成した terraform の各 tf ファイル

以下のように各 tf ファイルを作成します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

■main.tf ファイル

provider “aws” { 
  region = “ap-northeast-1” 
}

 

 

■terraform.tf ファイル

terraform { 
  backend “s3” { 
    bucket = “terraform-tfstate-file-demo” 
    key    = “terraform.tfstate” 
    region = “ap-northeast-1” 
  } 
}

 

 

■iam.tf ファイル

#============================================================================ 
# Github Actions 用ロール 
#============================================================================ 
# —————————————————————————– 
# GitHub Actions プロバイダー設定 
# —————————————————————————– 
resource “aws_iam_openid_connect_provider” “terra_cicd_demo” { 
  url             = “https://token.actions.githubusercontent.com” 
  client_id_list  = [“sts.amazonaws.com”] 
  # このコードは固定値 
  thumbprint_list = [“6938fd4d98bab03faadb97b34396831e3780aea1”] 

 
# —————————————————————————– 
# GitHub Actions 用ロール作成 
# —————————————————————————– 
resource “aws_iam_role” “terra_cicd_demo_oidc_role” { 
  name = “TerraCICDDemoOIDCRole” 
  path = “/” 
  assume_role_policy = jsonencode({ 
    Version = “2012-10-17” 
    Statement = [{ 
      Effect = “Allow” 
      Action = “sts:AssumeRoleWithWebIdentity” 
      Principal = { 
        Federated = aws_iam_openid_connect_provider.terra_cicd_demo.arn 
      } 
      Condition = { 
        StringLike = { 
          “token.actions.githubusercontent.com:sub” = [ 
            # リポジトリ制限 

            # リポジトリに複数のブランチを作成している場合は特定のブランチを絞ることができる。リスト化もできる。 
            “repo:xxxxx/test_demo:ref:refs/heads/demo_20220911”,
          ] 
        } 
      } 
    }] 
  }) 

 
# —————————————————————————– 
# ポリシーのアタッチ(AdministratorAccess_attachment) 
# —————————————————————————– 
resource “aws_iam_role_policy_attachment” “AdministratorAccess_attachment” { 
  role       = aws_iam_role.terra_cicd_demo_oidc_role.name 
  policy_arn = “arn:aws:iam::aws:policy/AdministratorAccess” 
}   

 

 

 

terraform init コマンドを実行する

各 tf ファイルが準備出来たところで terraform init コマンドを実行してみます。

 

■terraform init コマンド実行

$ terraform init 
 
Initializing the backend… 
 
Successfully configured the backend “s3”! Terraform will automatically 
use this backend unless the backend configuration changes. 
 
Initializing provider plugins… 
– Finding latest version of hashicorp/aws… 
– Installing hashicorp/aws v4.30.0… 
– Installed hashicorp/aws v4.30.0 (signed by HashiCorp) 
 
Terraform has created a lock file .terraform.lock.hcl to record the provider 
selections it made above. Include this file in your version control repository 
so that Terraform can guarantee to make the same selections by default when 
you run “terraform init” in the future. 
 
Terraform has been successfully initialized! 
 
You may now begin working with Terraform. Try running “terraform plan” to see 
any changes that are required for your infrastructure. All Terraform commands 
should now work. 
 
If you ever set or change modules or backend configuration for Terraform, 
rerun this command to reinitialize your working directory. If you forget, other 
commands will detect it and remind you to do so if necessary.

 

 

 

terraform plan コマンドを実行する

terraform plan コマンドを実行してみます。

 

■terraform plan コマンド実行

terraform plan 
 
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following 
symbols: 
  + create 
 
Terraform will perform the following actions: 
 
  # aws_iam_openid_connect_provider.terra_cicd_demo will be created 
  + resource “aws_iam_openid_connect_provider” “terra_cicd_demo” { 
      + arn             = (known after apply) 
      + client_id_list  = [ 
          + “sts.amazonaws.com”, 
        ] 
      + id              = (known after apply) 
      + tags_all        = (known after apply) 
      + thumbprint_list = [ 
          + “6938fd4d98bab03faadb97b34396831e3780aea1”, 
        ] 
      + url             = “https://token.actions.githubusercontent.com” 
    } 
 
  # aws_iam_role.terra_cicd_demo_oidc_role will be created 
  + resource “aws_iam_role” “terra_cicd_demo_oidc_role” { 
      + arn                   = (known after apply) 
      + assume_role_policy    = jsonencode( 
            { 
              + Statement = [ 
                  + { 
                      + Action    = “sts:AssumeRoleWithWebIdentity” 
                      + Condition = { 
                          + StringLike = { 
                              + “token.actions.githubusercontent.com:sub” = [ 
                                  + “repo:xxxxx/test_demo:ref:refs/heads/demo_20220911”, 
                                ] 
                            } 
                        } 
                      + Effect    = “Allow” 
                      + Principal = { 
                          + Federated = “arn:aws:iam::xxxxxxxx:oidc-provider/token.actions.githubusercontent.com” 
                        } 
                    }, 
                ] 
              + Version   = “2012-10-17” 
            } 
        ) 
      + create_date           = (known after apply) 
      + force_detach_policies = false 
      + id                    = (known after apply) 
      + managed_policy_arns   = (known after apply) 
      + max_session_duration  = 3600 
      + name                  = “TerraCICDDemoOIDCRole” 
      + name_prefix           = (known after apply) 
      + path                  = “/” 
      + tags_all              = (known after apply) 
      + unique_id             = (known after apply) 
 
      + inline_policy { 
          + name   = (known after apply) 
          + policy = (known after apply) 
        } 
    } 
 
  # aws_iam_role_policy_attachment.AdministratorAccess_attachment will be created 
  + resource “aws_iam_role_policy_attachment” “AdministratorAccess_attachment” { 
      + id         = (known after apply) 
      + policy_arn = “arn:aws:iam::aws:policy/AdministratorAccess” 
      + role       = “TerraCICDDemoOIDCRole” 
    } 
 
Plan: 3 to add, 0 to change, 0 to destroy. 
 
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
 
Note: You didn’t use the -out option to save this plan, so Terraform can’t guarantee to take exactly these actions if you run 
“terraform apply” now.

 

 

 

 

 

terraform apply コマンドを実行する

terraform plan で問題がなければ terraform apply コマンドを実行します。

 

■terraform apply コマンド実行

$ terraform apply

 

 

エラーがなければロールが作成されていることを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

すでにOIDCプロバイダが存在していてエラーになった場合

すでに OIDC プロバイダが他のタイミングで作成されていて存在している場合は以下のようなエラーになります。

 

■エラー

╷ 
│ Error: error creating IAM OIDC Provider: EntityAlreadyExists: Provider with url https://token.actions.githubusercontent.com already exists. 
│       status code: 409, request id: 4b363ff7-928c-41b2-80b5-xxxxxxx 
│  
│   with aws_iam_openid_connect_provider.terra_cicd_demo, 
│   on iam.tf line 7, in resource “aws_iam_openid_connect_provider” “terra_cicd_demo”: 
│    7: resource “aws_iam_openid_connect_provider” “terra_cicd_demo” { 
│  
╵ 

 

 

 

その場合は既存のOIDCプロバイダを直接記載して再度実行します。

 

IAMダッシュボードより「ID プロバイダ」を選択します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

対象のIDプロバイダを選択します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

下図の OIDC プロバイダの ARN をコピーします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

更に以下のように OIDC プロバイダの部分をコメントアウトします。

 

■修正後のコード

#============================================================================ 
# Github Actions 用ロール 
#============================================================================ 
# —————————————————————————– 
# GitHub Actions プロバイダー設定 
# —————————————————————————– 
/* 
resource “aws_iam_openid_connect_provider” “terra_cicd_demo” { 
  url             = “https://token.actions.githubusercontent.com” 
  client_id_list  = [“sts.amazonaws.com”] 
  # 固定値 
  thumbprint_list = [“6938fd4d98bab03faadb97b34396831e3780aea1”] 

*/ 
 
# —————————————————————————– 
# GitHub Actions 用ロール作成 
# —————————————————————————– 
resource “aws_iam_role” “terra_cicd_demo_oidc_role” { 
  name = “TerraCICDDemoOIDCRole” 
  path = “/” 
  assume_role_policy = jsonencode({ 
    Version = “2012-10-17” 
    Statement = [{ 
      Effect = “Allow” 
      Action = “sts:AssumeRoleWithWebIdentity” 
      Principal = { 
        #Federated = aws_iam_openid_connect_provider.terra_cicd_demo.arn 
        Federated = “arn:aws:iam::xxxxxxxx:oidc-provider/token.actions.githubusercontent.com” 
      } 
      Condition = { 
        StringLike = { 
          “token.actions.githubusercontent.com:sub” = [ 
            # リポジトリ制限 
            “repo:xxxxxx/test_demo:*”, 
          ] 
        } 
      } 
    }] 
  }) 

 
# —————————————————————————– 
# ポリシーのアタッチ(AdministratorAccess_attachment) 
# —————————————————————————– 
resource “aws_iam_role_policy_attachment” “AdministratorAccess_attachment” { 
  role       = aws_iam_role.terra_cicd_demo_oidc_role.name 
  policy_arn = “arn:aws:iam::aws:policy/AdministratorAccess” 

 

 

 

 

 

再度 terraform apply を実行してエラーにならないことを確認します。

 

■再度 terraform apply コマンドを実行する

$ terraform apply

 

 

 

以上で、GitHub Actions から AWS に対して構築・設定作業が実行できる権限を持ったロールが作成されました。

 

 

 

 

GitHub Actions より Terraform コマンドを実行する環境を構築する

次にいよいよ GitHub Action より Terraform のコマンド(init, plan, apply)を実行する環境を構築します。

 

 

リポジトリを作成する

GitHub Actions から terraform コマンドを実行するためのリポジトリを作成します。

今回はすでに作成してある test_demo リポジトリを流用します。

 

 

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

新しいブランチを作成する

terraform コマンドを実行する環境を構築するために新しいブランチを切ります。

 

■新しいブランチを作成

 (main) $ git pull 
remote: Enumerating objects: 4, done. 
remote: Counting objects: 100% (4/4), done. 
remote: Compressing objects: 100% (2/2), done. 
remote: Total 2 (delta 1), reused 0 (delta 0), pack-reused 0 
Unpacking objects: 100% (2/2), 699 bytes | 699.00 KiB/s, done. 
From github.com:xxxx/test_demo 
   402c5b3..9d78560  main       -> origin/main 
Updating fdb7857..9d78560 
Fast-forward 
 .github/workflows/test_demo.yml | 36 ++++++++++++++++++++++++++++++++++++ 
 .terraform.lock.hcl             | 21 +++++++++++++++++++++ 
 iam.tf                          | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 
 main.tf                         |  3 +++ 
 terraform.tf                    |  7 +++++++ 
 5 files changed, 116 insertions(+) 
 create mode 100644 .github/workflows/test_demo.yml 
 create mode 100644 .terraform.lock.hcl 
 create mode 100644 iam.tf 
 create mode 100644 main.tf 
 create mode 100644 terraform.tf 
 (main) $ git branch demo_test_20220918 
 (main) $ git checkout demo_test_20220918  
Switched to branch ‘demo_test_20220918’ 
 (demo_test_20220918) $ 

 

 

 

 

デモ用の tf ファイルを作成する

デモ用の tf ファイルを作成します。

EC2 インスタンス構築の tf ファイルを作成します。

 

■デモ用の EC2 インスタンス作成用 tf ファイル

vi demo-ec2.tf 
resource “aws_instance” “demo-ec2” { 
    ami = “ami-0f36dcfcc94112ea1” 
    instance_type = “t2.micro” 
    subnet_id = “subnet-01890002cc54da77b” 
 
    tags = { 
        Name = “demo-ec2-01” 
    } 
 
}

 

 

 

 

terraform plan 用のファイルを作成する

terraform plan コマンド用のファイルを作成します。

 

■terraform_plan.ymlの内容

$ vi terraform_plan.yml  
name: TerraformPlan 
 
on: 
  pull_request: 
    branches: 
      – main 
 
env: 
  TF_VERSION: 1.1.2 
  AWS_DEFAULT_REGION: ap-northeast-1 
  AWS_ROLE_ARN: arn:aws:iam::xxxxxxxx:role/TerraCICDDemoOIDCRole 
 
permissions: 
  id-token: write 
  contents: read 
  actions: read 
  pull-requests: write 
   
jobs: 
  Terraform_plan_and_Comment: 
    runs-on: ubuntu-latest 
 
    defaults: 
      run: 
        shell: bash 
 
    steps: 
      # リポジトリのチェックアウトをする。     
      – name: Check out repository code 
        uses: actions/checkout@v3 
 
      # OICDでAssumeRoleをする。 
      – name: Configure AWS credentials 
        uses: aws-actions/configure-aws-credentials@v1 
        with: 
          aws-region: ${{ env.AWS_DEFAULT_REGION }} 
          role-to-assume: ${{ env.AWS_ROLE_ARN }} 
 
      – name: Setup Terraform 
        # バージョン2を使用する。 
        uses: hashicorp/setup-terraform@v2 
        with: 
          terraform_version: ${{ env.TF_VERSION }} 
       
      – name: Exec Terraform fmt check 
        id: fmt 

        run: terraform fmt -check -recursive
        # exit code 3でエラーになり終了してしまうため
        # continue-on-error: true で後続の処理も続ける。
        continue-on-error: true

      – name: Exec Terraform init 
        id: init 
        run: terraform init 
 
      – name: Exec Terraform plan 
        id: plan 
        run: terraform plan -no-color 
           
      # terraform plan の結果をコメント欄に出力する。 
      – name : comment 
        uses: actions/github-script@v4 
        env: 
          # ここのstdoutでterraform planの結果をPLANに保存している。 
          PLAN: “terraform\n${{ steps.plan.outputs.stdout }}” 
           
        with: 
          script: | 
 
            const output = `### terraform cicd demo 
            #### Terraform Format and Style \`${{ steps.fmt.outcome }}\` 
            #### Terraform Initialization \`${{ steps.init.outcome }}\` 
            #### Terraform Plan \`${{ steps.plan.outcome }}\` 
            #### Terraform Validation \`${{ steps.validate.outcome }}\` 
 
            

            <details><summary>Show Plan</summary>

            \`\`\`terraform\n 
            ${process.env.PLAN} 
            \`\`\` 
            </details>
          

            *Action: \`${{ github.event_name }}\`*`; 
 
            github.issues.createComment({ 
              issue_number: context.issue.number, 
              owner: context.repo.owner, 
              repo: context.repo.repo, 
              body: output 
            })

 

 

 

 

git add、git commit、git push を実行する

ここまで準備が出来たら実際に git add、git commit、git push コマンドを実行してみます。

 

■git add、git commit、git push コマンドを実行

 (demo_test_20220918) $ git status 
On branch demo_test_20220918 
Changes not staged for commit: 
  (use “git add/rm …” to update what will be committed) 
  (use “git restore …” to discard changes in working directory) 
        deleted:    .github/workflows/test_demo.yml 
 
Untracked files: 
  (use “git add …” to include in what will be committed) 
        .github/workflows/terraform_plan.yml 
        demo-ec2.tf 
 
no changes added to commit (use “git add” and/or “git commit -a”) 
 (demo_test_20220918) $ git add . 
 (demo_test_20220918) $ git status 
On branch demo_test_20220918 
Changes to be committed: 
  (use “git restore –staged …” to unstage) 
        new file:   .github/workflows/terraform_plan.yml 
        deleted:    .github/workflows/test_demo.yml 
        new file:   demo-ec2.tf 
 
 (demo_test_20220918) $ git commit -m “demo_test_20220918”  
[demo_test_20220918 384e1d1] demo_test_20220918 
 Committer: EC2 Default User <ec2-user@ip-172-31-44-157.ap-northeast-1.compute.internal> 
Your name and email address were configured automatically based 
on your username and hostname. Please check that they are accurate. 
You can suppress this message by setting them explicitly: 
 
    git config –global user.name “Your Name” 
    git config –global user.email you@example.com 
 
After doing this, you may fix the identity used for this commit with: 
 
    git commit –amend –reset-author 
 
 3 files changed, 98 insertions(+), 36 deletions(-) 
 create mode 100644 .github/workflows/terraform_plan.yml 
 delete mode 100644 .github/workflows/test_demo.yml 
 create mode 100644 demo-ec2.tf 
 (demo_test_20220918) $ git push origin demo_test_20220918

Enumerating objects: 9, done. 
Counting objects: 100% (9/9), done. 
Compressing objects: 100% (4/4), done. 
Writing objects: 100% (6/6), 1.58 KiB | 811.00 KiB/s, done. 
Total 6 (delta 1), reused 0 (delta 0), pack-reused 0 
remote: Resolving deltas: 100% (1/1), completed with 1 local object. 
remote:  
remote: Create a pull request for ‘demo_test_20220918’ on GitHub by visiting: 
remote:      https://github.com/xxx/test_demo/pull/new/demo_test_20220918 
remote:  
To github.com:xxxxxxxx/test_demo.git 
 * [new branch]      demo_test_20220918 -> demo_test_20220918 
 (demo_test_20220918) $ 

 

 

 

 

 

GitHub 上でプルリクエストを投げる

ブランチに push したら GitHub 上でプルリクエストを投げます。

 

必要事項を入力して「Create pull request」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

エラーなく terraform plan のパイプラインが完了したことを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

 

GitHub Actions での terraform plan コマンドの実行結果をコメントに出力しているのでコメントを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

コメントの「Show Plan」をクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

以下のように terraform plan の実行結果を確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

terraform fmt -check -recursive コマンドでエラーになる場合

パイプラインのコマンド実行に「terraform fmt -check -recursive」を入れると下図のようにエラーになる場合があります。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

展開すると以下のようなエラーが確認できます。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

■エラー内容

Error: Terraform exited with code 3.
Error: Process completed with exit code 1.

 

 

原因は terraform fmt -check -recursive コマンドが exit code 3で終了しているからです。

正常コマンドが終了した場合は、通常、exit code 0 で終了します。

しかしなぜか terraform fmt -check -recursive を実行すると exit code 3 で終了してしまいます。

 

 

■Linux 上で terraform fmt -check -recursive コマンドを実行

$ terraform fmt -check -recursive
demo-ec2.tf
$ echo $?
3

 

 

しかし terraform fmt コマンドなら exit code は 0 で終了します。

 

 

■terraform fmt コマンドは exit code 0 になる

$ terraform fmt
demo-ec2.tf
$ echo $?
0

 

GitHub Actions の場合、各コマンドの exit code が「0」の場合、次に進みます。

しかし exit code が「0」以外だとそこでパイプラインが終了してしまいます。

そのため、exit code 3 になっても後続の処理を続けるために「continue-on-error: true」の設定を入れます。

 

 

■continue-on-error: true の設定を入れる

      – name: Exec Terraform fmt check 
        id: fmt 
        run: terraform fmt -check -recursive 
        # exit code 3でエラーになり終了してしまうため 
        # continue-on-error: true で後続の処理も続ける。 
        continue-on-error: true

 

 

 

 

GitHub Actions で terraform apply コマンドを実行する環境を構築する

先ほどは terraform plan コマンドを実行する環境を構築しましたが、次は terraform apply コマンドを実行する環境を構築します。

 

terraform apply コマンドはプルリクエストが承認され、マージされたタイミングで実行されます。

 

terraform_apply.yml ファイルを作成する

先ほどの「terraform_plan.yml」ファイルと同様に「terraform_apply.yml」ファイルを作成します。

 

■terraform_apply.yml ファイル

name: TerraformApply 
 
on: 
 pull_request: 
    branches: 
      – main 
    types: [closed] 
     
env: 
  TF_VERSION: 1.1.2 
  AWS_DEFAULT_REGION: ap-northeast-1 
  AWS_ROLE_ARN: arn:aws:iam::xxxxxxxxx:role/TerraCICDDemoOIDCRole
 
permissions: 
  id-token: write 
  contents: read 
  actions: read 
  pull-requests: write 
   
jobs: 
  Terraform_Apply: 
    runs-on: ubuntu-latest 
 
    defaults: 
      run: 
        shell: bash 
 
    steps: 
      # リポジトリのチェックアウトをする。     
      – name: Check out repository code 
        uses: actions/checkout@v3 
 
      # OICDでAssumeRoleをする。 
      – name: Configure AWS credentials 
        uses: aws-actions/configure-aws-credentials@v1 
        with: 
          aws-region: ${{ env.AWS_DEFAULT_REGION }} 
          role-to-assume: ${{ env.AWS_ROLE_ARN }} 
           
      – name: Setup Terraform 
        # バージョン2を使用する 
        uses: hashicorp/setup-terraform@v2 
        with: 
          terraform_version: ${{ env.TF_VERSION }} 
       
      – name: Exec Terraform fmt check 
        id: fmt 
        working-directory: ‘${{ env.WORK_DIR }}’ 
        run: terraform fmt -recursive -check 

        # exit code 3でエラーになり終了してしまうため
        # continue-on-error: true で後続の処理も続ける。
        continue-on-error: true
 
      – name: Exec Terraform init 
        id: init 
        working-directory: ‘${{ env.WORK_DIR }}’ 
        run: terraform init 
 
      – name: terraform apply 
        id: apply 
        working-directory: ‘${{ env.WORK_DIR }}’ 
        run: terraform apply -auto-approve

 

 

 

git add、git commit、git push を実行する

ここまで準備が出来たら実際に git add、git commit、git push コマンドを実行してみます。

 

■git add、git commit、git push コマンドを実行

(demo_test_20220918) $ git add . 
(demo_test_20220918) $ git commit -m “demo_test_20220918” 
[demo_test_20220918 c31e9d1] demo_test_20220918 
 Committer: EC2 Default User <ec2-user@ip-172-31-44-157.ap-northeast-1.compute.internal> 
Your name and email address were configured automatically based 
on your username and hostname. Please check that they are accurate. 
You can suppress this message by setting them explicitly: 
 
    git config –global user.name “Your Name” 
    git config –global user.email you@example.com 
 
After doing this, you may fix the identity used for this commit with: 
 
    git commit –amend –reset-author 
 
 2 files changed, 64 insertions(+), 1 deletion(-) 
 create mode 100644 .github/workflows/terraform_apply.yml 
(demo_test_20220918) $ git push origin demo_test_20220918 
Enumerating objects: 10, done. 
Counting objects: 100% (10/10), done. 
Compressing objects: 100% (5/5), done. 
Writing objects: 100% (6/6), 1.32 KiB | 675.00 KiB/s, done. 
Total 6 (delta 2), reused 0 (delta 0), pack-reused 0 
remote: Resolving deltas: 100% (2/2), completed with 2 local objects. 
To github.com:xxxxxxxxx/test_demo.git 
   a6e05c3..c31e9d1  demo_test_20220918 -> demo_test_20220918 

 

 

 

 

 

プルリクエスト作成しマージする

上記のようにプルリクエストを作成後、マージをします。

 

 

GitHub上のプルリクエストをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

「Merge pull request」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

「Confirm merge」ボタンをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

「Actions」タブをクリックします。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

パイプラインが問題なく終了したことを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

 

 

 

 

動作確認をする

最後に動作確認をします。

 

 

AWS 管理コンソールより EC2 のダッシュボードに移動し EC2 インスタンスを確認します。

【GitHub Actions】TerraformでデプロイするCI/CDパイプラインの構築手順

 

 

 

今後やりたいこと

  • 複数人で運用する場合、デプロイでバッティングしないようにしたい。
  • 現状、1つのリポジトリで1パイプラインになっている。1つのリポジトリで複数のパイプラインを運用したい。
  • terraform apply の結果を Slack に通知したい。

 

 

 

参考サイト

https://learn.hashicorp.com/tutorials/terraform/github-actions

 

 

 

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

AlphaOmega Captcha Medica  –  What Do You See?
     
 

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください