GitHub Actions × Terraform で実現する Google Cloud インフラの自動化入門


## はじめに

皆さん、CI/CDってやったことありますか？
私は遅ればせながら、初めて試してみたところ、自動化楽しいー！とハマってしまいました。（と同時にDevOps普段からやっている人羨ましいと叫びました）

普段はGCP触っていますが、DevOpsやっていないので新鮮でした。

というわけで、この記事では、**GitHub Actions**を使って**Terraform**のコードをプッシュするだけで、Google Cloudのインフラを自動で構築・更新できる環境を作っていこうと思います💪

👇の悩みがある人は特にオススメです！
「Terraformは導入したけど、結局手動で適用している...」
「プルリクエストのレビューはするけど、実際の適用は誰かに任せっきり...」
「複数環境の整合性が取れなくて困っている...」

## この記事のゴール
- GitHub ActionsでTerraformのCI/CDパイプラインを構築できる
- インフラの変更をプルリクエストベースで管理できる
- コードをプッシュするだけでインフラが自動で更新される

## 想定する読者
👇あまり分かってなくても手順通りにやればできるとは思います。
- Terraformの基本的な使い方を理解している
- Google Cloudを使ったことがある
- GitHubでのプルリクエストの流れを理解している

## 環境構成
- GitHub: コード管理・CI/CD実行基盤
- GitHub Actions: CI/CDパイプライン
- Terraform: インフラのコード化
- Google Cloud
  - Cloud Storage: tfstateファイルの保管
  - Compute Engine: サンプルリソースとして作成
  
[![](https://mermaid.ink/img/pako:eNqNVEtvm0AQ_itoc0kk3EjYURNURcLYpZdGyKaXQg9rmAUUYNGy28SK8987LBgwlaJwGr7HMI9l30jMEyA2SQWtMyNYR5WBT6MOHeDl8oc6hBHpgoj86QTt4-3CHdS8ySUXxynuhJ3acGKZ86rpOaiSqJrlD0AIyrgo8RNDfPGVwA0HwnCx2CnnTzi_oNWUcyacU9fF8YMyPNdve-Q8LcBwC66SixoG3Q4arkQMDaqH-EKqB-BuQ5eXtZJgbKs0r2AmeNoG4RPIFy6eJ4wubMyxD3Uhxh7HS1P4dhC3j5I1kkr4v5MN_IWC1yCur4fw5sZYLB5PvmoyPbgTbqxTezvNBCJPUxCIOz3uaHz7CjHWfsLZd3jgatyDCgTVhN8T_szQJwq6RK4AlN_-qhPtGic2iL6gqm0QsE2twb7PPcUFbZoNMCM5d2SwvCjsK_bAzEYK_gz21XK57OPFS57IzLbq15k9zWWGh7HzWivrwYLBzhgzY15wocOZUQ7np_N-Xa-stfs5bxrXvWtl3d99X33OJc4T6r3LlXN_t_zQO8kwnoJxZFPa25m44W4cUzxwzcA3cR1Dwxcu1zdxK21HU3hYJpJbE0_0WDwxSQmipHmCF8tba4qIzKCEiNgYJsCoKmREouodpVRJvj9WMbGlUGASwVWaEZvRosE3pc_OJqf4A5YDWtPqN-fjOyTtJfSzu8r0jfb-DwdRjdA?type=png)](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqNVEtvm0AQ_itoc0kk3EjYURNURcLYpZdGyKaXQg9rmAUUYNGy28SK8987LBgwlaJwGr7HMI9l30jMEyA2SQWtMyNYR5WBT6MOHeDl8oc6hBHpgoj86QTt4-3CHdS8ySUXxynuhJ3acGKZ86rpOaiSqJrlD0AIyrgo8RNDfPGVwA0HwnCx2CnnTzi_oNWUcyacU9fF8YMyPNdve-Q8LcBwC66SixoG3Q4arkQMDaqH-EKqB-BuQ5eXtZJgbKs0r2AmeNoG4RPIFy6eJ4wubMyxD3Uhxh7HS1P4dhC3j5I1kkr4v5MN_IWC1yCur4fw5sZYLB5PvmoyPbgTbqxTezvNBCJPUxCIOz3uaHz7CjHWfsLZd3jgatyDCgTVhN8T_szQJwq6RK4AlN_-qhPtGic2iL6gqm0QsE2twb7PPcUFbZoNMCM5d2SwvCjsK_bAzEYK_gz21XK57OPFS57IzLbq15k9zWWGh7HzWivrwYLBzhgzY15wocOZUQ7np_N-Xa-stfs5bxrXvWtl3d99X33OJc4T6r3LlXN_t_zQO8kwnoJxZFPa25m44W4cUzxwzcA3cR1Dwxcu1zdxK21HU3hYJpJbE0_0WDwxSQmipHmCF8tba4qIzKCEiNgYJsCoKmREouodpVRJvj9WMbGlUGASwVWaEZvRosE3pc_OJqf4A5YDWtPqN-fjOyTtJfSzu8r0jfb-DwdRjdA)


## 事前準備

### 1. Google Cloudプロジェクトの設定

まずはGoogle Cloudのプロジェクトを作成し、必要なAPIを有効にします。

```bash
# プロジェクトIDを設定
PROJECT_ID="your-gcp-project-id"

# Compute Engine APIを有効化
gcloud services enable compute.googleapis.com --project=$PROJECT_ID
```

### 2. tfstateファイル用のバケット作成

Terraformの状態管理用のバケットを作成します。チーム開発では、このバケットを共有することで状態を一元管理できます。

```bash
# バケット名を設定（グローバルでユニークな名前に変更してください）
BUCKET_NAME="your-terraform-state-bucket" 

# リージョンを設定
BUCKET_LOCATION="asia-northeast1"

# バケットの作成
gsutil mb -l $BUCKET_LOCATION -p $PROJECT_ID gs://$BUCKET_NAME
```

### 3. GitHub Actions用サービスアカウントの設定

GitHub ActionsがGoogle Cloudを操作するために必要なサービスアカウントを作成します。

```bash
# サービスアカウント名を設定
SA_DEPLOY_NAME="github-actions-terraform"

# サービスアカウントを作成
gcloud iam service-accounts create $SA_DEPLOY_NAME \
    --project="$PROJECT_ID"

# サービスアカウントのメールアドレスを取得
SA_DEPLOY_EMAIL=$(gcloud iam service-accounts list \
    --project="$PROJECT_ID" \
    --filter="name:$SA_DEPLOY_NAME" \
    --format="value(email)")

# 必要な権限を付与（本番環境ではより制限的な権限設定を推奨）
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
    --member="serviceAccount:$SA_DEPLOY_EMAIL" \
    --role="roles/owner"

# キーファイルの作成
gcloud iam service-accounts keys create terraform-deploy.json \
    --iam-account="$SA_DEPLOY_EMAIL" \
    --project="$PROJECT_ID"
```

### 4. GitHubリポジトリの設定

1. GitHubにログインします。
2. 「New」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/3b36e6ba-7609-4876-95ba-5e44b8733d86.png)

3. Repository nameを入力し、「Create Repository」をクリックして新しいリポジトリを作成します。
![image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/d9659837-826d-490f-b706-1620d48a6a98.png)

4. リポジトリのSettings(歯車アイコン)をクリックします。
![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/65753a81-4c56-4296-90c6-a2aadf7e6905.png)

5. Secrets and variables ＞ Actionsで新しいシークレットを追加します。
- Name: `GOOGLE_CREDENTIALS`
- Value: 上記で作成したサービスアカウントキー（terraform-deploy.json）の内容をコピーして貼り付ける
![image (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/1996e1bb-ad23-48ac-8836-0d035a6aaa10.png)
![image (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/a6ac8bb0-a16b-49c0-ac75-30f85dae26c6.png)
![image (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/3502465a-8104-4113-98a2-0842bb4d4502.png)


## CI/CDパイプラインの構築

### 1. GitHub Actionsワークフローの作成

1. GitHubで「Create new file」をクリックします。
![image (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/cabbba75-383c-4d80-8688-7cf37dae3f53.png)

`.github/workflows/terraform_apply.yml`を作成し、以下の内容を追加します：
![image (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/8ef37ecb-27dd-40dc-9083-a42f57ed6bde.png)

```yaml
name: terraform apply

on:
  push:
    branches:
    - main
  pull_request:

defaults:
  run:
    working-directory: ./

jobs:
  terraform-workflow:
    name: terraform workflow
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
    env:
      GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}

    steps:
    - name: checkout
      uses: actions/checkout@v4

    - name: setup terraform
      uses: hashicorp/setup-terraform@v3

    - name: terraform init
      run: terraform init

    - name: terraform format
      run: terraform fmt

    - name: terraform plan
      run: terraform plan

    - name: terraform apply
      run: terraform apply -auto-approve
```

### 2. GItHubとCloudShellの連携
1. CloudShellでエディタを開き、以下を実行してクローンします。

```
git clone リポジトリURL(https://github.com/XXX/XXX.git)
```

2. 以下のポップアップで「Allow」をクリックします。
![image (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/d155fe9f-530f-4ab9-b92f-21333fd10e5f.png)

3. 以下のポップアップで「Copy＆Continue to GitHub」をクリックします。
![image (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/570d2e6a-5767-441c-a2b3-8c18ad2e6b7c.png)

4. 「open」をクリックします。
![image (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/a22f6001-0c96-479c-a143-adf53f1a4999.png)


5. ユーザーネーム、パスワードを入力して「Sign in」をクリックします。
![image (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/e9e9d056-6213-49b8-87e2-89f6b594381b.png)

6. 「Continue」をクリックします。
7. 3. でコピーした番号を入力します。
8. 「Authorize Visual-Studio-Code」をクリックします。
9. 以下のような表示が出れば完了です。
![image (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/a5176f36-cc97-470d-b671-776d310101c3.png)

### 3. Terraformコードの作成
`main.tf`を作成し、まずはメインブランチにプッシュします。

1. クローンしたフォルダ配下に移動し、`main.tf`を作成します。
```bash
cd "クローンしたフォルダ"
sudo vi main.tf
```

ここでは、Compute Engineインスタンス1台を作っていきます。
ネットワークはGCPのデフォルトのものを使います。
```hcl
terraform {
  required_version = "~> 1.10.5"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.40.0"
    }
  }
  backend "gcs" {
    bucket = "your-terraform-state-bucket"  # 作成したバケット名に変更
  }
}

provider "google" {
  project = "your-gcp-project-id"  # プロジェクトIDに変更
}

resource "google_compute_instance" "example" {
  name         = "terraform-managed-instance" # 作成するCompute Engineの名前
  machine_type = "n1-standard-1"
  zone         = "asia-northeast1-a"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-11"
    }
  }

  network_interface {
    network    = "default"
    subnetwork = "default"
  }
}
```

2 . 以下を実行してメインリポジトリにプッシュします。 
```bash
git config --local user.email "メールアドレス" # GitHubのメールアドレス
git config --local user.name "ユーザー名" # 任意のユーザー名
git add .
git commit -m "First Commit."
git log
git push -u origin main
```

### 4. プルリクエストする
開発用の新しいブランチを作成し、メインブランチにプルリクエストします。
新しいブランチを作成し、メインブランチにプルリクエストするという流れは、安全かつ効率的に開発を進めるためのベストプラクティスです。

1. 新しいブランチを作成してインスタンス名を変更します。
```bash
git branch "new_branch_name" # 新しいブランチの名前
git checkout "new_branch_name" # 新しいブランチの名前
git branch # ブランチを確認
```

2 . `main.tf`を編集し、インスタンス名を以下のように変更します。
```hcl
resource "google_compute_instance" "example" {
  name         = "instance-test"  # 名前を変更
  ...
}
```

3 . 変更をコミットしてプッシュします。
```bash
git add .
git commit -m "First Commit"
git push origin "new_branch_name"
```

4 . GitHubでプルリクエストを作成します。
「Compare & pull request」をクリックします。
![image (13).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/db12fd78-d312-4d32-8f78-122e303dd0da.png)

5 . タイトルとメッセージを入力し、「Create pull request」
をクリックします。
![image (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/fb071e71-c09d-49c5-ac4d-d7b29e379094.png)

6 . GitHub Actionsの実行結果を確認します。
コミットメッセージに✅が付いてればプルリクエストが
メインブランチにマージされています。
![image (15).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/1c27d694-21d9-41e2-8493-95e557f8163c.png)


6 . Google Cloud Consoleでリソースが作成されていることを確認します。
![image (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/ab6c5d4d-cc5f-402b-97fd-fb5e740a0e25.png)

7 . GCSにtfstateファイルが作成されていることを確認します。
![image (19).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/5c4096d5-4610-42d5-93cd-1727fad98cd2.png)

### 5. GitHub Actionsで自動実行
それではいよいよCI/CDの実践です。
Compute Engineのインスタンス名を変更が自動更新されるようにしていきたいと思います！

1. `main.tf`内のGCEインスタンス名を変更します。

```hcl
   resource "google_compute_instance" "compute_instance" {
      name         = "instance-test2" # 名前を変更
      machine_type = "n1-standard-1"
      zone         = "asia-northeast1-a"
```

2 . 以下を実行してリポジトリにプッシュします。

```bash
git add .
git commit -m "Second Commit."
git log
git push
```

3 . GitHubにアクセスし、自動でGCEインスタンス名が変更されていることを確認します。
![image (20).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636793/d4d1e40c-9f5d-4793-87b7-e9e0a829b141.png)

Compute Engineのインスタンス名を変更するだけの簡単な変更でしたが、これでCI/CDパイプラインの完成です！！
これで、次回の変更時もプッシュするだけでメインブランチにマージされ、リソースを更新することができます🏋️ 

## クリーンアップ
以下は削除手順になります。

`.github/workflows/terraform_destroy.yml`を作成し、リソースを削除します。
※ディレクトリ名が異なると実行されないので注意。

```bash
sudo vi .github/workflows/terraform_destroy.yml
```
```yaml
name: terraform destroy

on:
  workflow_dispatch:

jobs:
  terraform-workflow:
    name: terraform workflow
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
    env:
      GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}

    steps:
    - name: checkout
      uses: actions/checkout@v4

    - name: setup terraform
      uses: hashicorp/setup-terraform@v3

    - name: terraform init
      run: terraform init

    - name: terraform destroy
      run: terraform destroy -auto-approve
```

## まとめ
CI/CD構成により、以下のメリットが得られます。

- コードレビューとインフラ変更の一元管理
- 手動オペレーションによるミスの防止
- 変更履歴の可視化
- チーム開発での整合性確保

この記事を参考にして皆さんもCI/CDを実践してもらえたら嬉しいです🤖