5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actions × Terraform でさくらのクラウドをCI/CD管理する

Last updated at Posted at 2025-12-03

さくらインターネットでエンジニアをしている 芦野と申します。( https://x.com/tar_xzvff )
アカウント名の通り、自宅サーバに始まり、かれこれずっとサーバに関わる仕事をしています。

普段はインフラエンジニア/バックエンドエンジニアとして勤務しており、
VMM やコンテナ技術周辺を主な関心領域としています。

社外での活動では、ITカンファレンスのイベントネットワーク(NOC)に携わることがあります。
ルータ、L2スイッチ、無線LANアクセスポイントなどの物理機材を用いた構築が主ですが、最近では監視やログサーバなどをクラウド上に事前構築しておき、現地から VPN でクラウドに接続することで迅速にサービスを展開する、といったハイブリッド構成を取ることが増えてきました。

そのような場面で、チームメンバーに対して Terraform + GitHub Actions を使った IaC / CI/CD をレクチャーする機会がありました。
本記事では、その際に実践した「GitHub Actions で Terraform を使ってさくらのクラウドを管理する方法」を紹介します。

本記事は、Terraform や GitHub Actions をある程度触ったことがあり、さくらのクラウドを GitOps で管理してみたい方を対象としています。

前提

  • GitHubにサインアップ済み
  • さくらのクラウドにサインアップ済み、プロジェクト作成済み

1. Terraform Backendとして利用するストレージを作成

Terraform の Backend として、さくらのクラウドのオブジェクトストレージ(S3互換)を利用します。

tfstate の保存先となるバケットを作成します。作成方法は以下をご参考ください。
https://manual.sakura.ad.jp/cloud/objectstorage/basic.html

今回は以下の内容で作成しました。任意の名前で作成してください。

バケット名: sacloud-terraform

バケット作成後パーミッションを作成します。作成方法は以下をご参考ください。
バケット sacloud-terraform に対して、 READ/WRITE の設定を行ってください。
https://manual.sakura.ad.jp/cloud/objectstorage/basic.html#objectstrage-about-permission

今回は以下の内容で作成しました。

パーミッション名: gha

アクセスキーIDシークレットアクセスキーが発行されるので手元にコピーしてください、後ほど利用します。

バケットに対してパーミッションからREAD/WRITEの権限が付与されていることを確認してください。

permission.png

2. さくらのクラウド APIキーの作成

Terraform からさくらのクラウドにアクセスするために、API キーを作成します。
APIキーの作成方法は以下をご参考ください。
https://manual.sakura.ad.jp/cloud/api/apikey.html

アクセスレベル: 作成・削除 で作成してください。

アクセストークンアクセストークンシークレットが発行されるので手元にコピーしてください、後ほど利用します。

3. GitHubリポジトリの作成

TerraformでCI/CDするために管理用のリポジトリを作成します。
mainブランチが存在しているところまで進めてください。
https://github.com/new

4. Terraform Provider設定

mkdir terraform

bucket = "sacloud-terraform" の部分は作成したバケット名に修正してください。

./terraform/provider.tf
terraform {
  backend "s3" {
    bucket = "sacloud-terraform"
    key    = "terraform.tfstate"
    region = "jp-north-1"
    endpoints = {
      s3 = "https://s3.isk01.sakurastorage.jp"
    }
    skip_credentials_validation = true
    skip_requesting_account_id  = true
    skip_region_validation      = true
    skip_metadata_api_check     = true
    skip_s3_checksum            = true
  }
  required_providers {
    sakuracloud = {
      source  = "sacloud/sakuracloud"
      version = "2.31.2"
    }
  }
}

provider "sakuracloud" {}

5. GitHub Actionsの設定

mkdir -p .github/workflows
./.github/workflows/ci.yml
name: CI

on:
  pull_request:
    branches:
      - main
    paths:
      - "terraform/**"
  workflow_dispatch:

permissions:
  contents: write
  pull-requests: write

defaults:
  run:
    working-directory: ./terraform

env:
  TF_VERSION: "1.13.0"
  TFCMT_VERSION: "v4.14.12"
  SAKURACLOUD_ACCESS_TOKEN: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN }}
  SAKURACLOUD_ACCESS_TOKEN_SECRET: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN_SECRET }}
  SAKURACLOUD_ZONE: ${{ vars.SAKURACLOUD_ZONE }}
  AWS_ACCESS_KEY_ID: ${{ secrets.SAKURACLOUD_STORAGE_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.SAKURACLOUD_STORAGE_SECRET_ACCESS_KEY }}
  AWS_REQUEST_CHECKSUM_CALCULATION: WHEN_REQUIRED
  AWS_RESPONSE_CHECKSUM_VALIDATION: WHEN_REQUIRED

jobs:
  fmt:
    name: terraform fmt
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: terraform fmt
        run: terraform fmt -check

  validate:
    name: terraform validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - run: terraform init
      - run: terraform validate -no-color

  plan:
    name: terraform plan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: setup tfcmt
        run: |
          wget "https://github.com/suzuki-shunsuke/tfcmt/releases/download/${TFCMT_VERSION}/tfcmt_linux_amd64.tar.gz" -O /tmp/tfcmt.tar.gz
          tar xzf /tmp/tfcmt.tar.gz -C /tmp
          mv /tmp/tfcmt /usr/local/bin
          tfcmt --version

      - run: terraform init

      - name: terraform plan
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: tfcmt plan --patch -- terraform plan -no-color -input=false
./.github/workflows/cd.yml
name: CD

on:
  push:
    branches:
      - main
    paths:
      - "terraform/**"

concurrency:
  group: terraform-deploy
  cancel-in-progress: false

permissions:
  contents: write
  pull-requests: write

defaults:
  run:
    working-directory: ./terraform

env:
  TF_VERSION: "1.13.0"
  TFCMT_VERSION: "v4.14.12"
  SAKURACLOUD_ACCESS_TOKEN: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN }}
  SAKURACLOUD_ACCESS_TOKEN_SECRET: ${{ secrets.SAKURACLOUD_ACCESS_TOKEN_SECRET }}
  SAKURACLOUD_ZONE: ${{ vars.SAKURACLOUD_ZONE }}
  AWS_ACCESS_KEY_ID: ${{ secrets.SAKURACLOUD_STORAGE_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.SAKURACLOUD_STORAGE_SECRET_ACCESS_KEY }}
  AWS_REQUEST_CHECKSUM_CALCULATION: WHEN_REQUIRED
  AWS_RESPONSE_CHECKSUM_VALIDATION: WHEN_REQUIRED

jobs:
  apply:
    name: terraform apply
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: terraform fmt
        run: terraform fmt -check

      - name: setup tfcmt
        run: |
          wget "https://github.com/suzuki-shunsuke/tfcmt/releases/download/${TFCMT_VERSION}/tfcmt_linux_amd64.tar.gz" -O /tmp/tfcmt.tar.gz
          tar xzf /tmp/tfcmt.tar.gz -C /tmp
          mv /tmp/tfcmt /usr/local/bin
          tfcmt --version

      - run: terraform init
      - run: terraform validate -no-color

      - name: terraform apply
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: tfcmt apply -- terraform apply -auto-approve

6. シークレット・変数の設定

Terraformの実行に必要な環境変数を設定します。
作成した GitHub リポジトリの Settings → Secrets and variables → Actions から、
Repository Secrets & Variables にシークレットと変数を設定します。

https://docs.github.com/ja/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets
https://docs.github.com/ja/actions/how-tos/write-workflows/choose-what-workflows-do/use-variables

先ほど作成した、オブジェクトストレージとさくらのクラウド APIキーの情報をそれぞれ設定してください。

Secrets

環境変数
SAKURACLOUD_STORAGE_ACCESS_KEY_ID オブジェクトストレージのアクセスキーID
SAKURACLOUD_STORAGE_SECRET_ACCESS_KEY オブジェクトストレージのシークレットアクセスキー
SAKURACLOUD_ACCESS_TOKEN さくらのクラウド APIキーのアクセストークン
SAKURACLOUD_ACCESS_TOKEN_SECRET さくらのクラウド APIキーのアクセストークンシークレット

Variables

環境変数 説明
SAKURACLOUD_ZONE is1b リソースの作成先ゾーン

7. terraform plan の動作確認

手元で作成したファイルを Push し、Pull Request を作成します。

git checkout -b init
git add .github/
git add terraform/provider.tf
git commit -m "init"
git push origin init

Pull Request作成すると、 terraform fmt / plan / validateのワークフローが実行されます。
スクリーンショット 2025-12-03 19.10.41.png

スクリーンショット 2025-12-03 19.15.04.png

terraform fmt / plan / validateが全て通ることを確認します。
スクリーンショット 2025-12-03 19.10.58.png

全て通ったことを確認したら、マージします。

8. terraform apply の動作確認

Actionsタブを開き、terraform applyのワークフローが実行されたことを確認します。

スクリーンショット 2025-12-03 19.44.46.png

9. [実践編] サーバを作成する

これまでの手順で準備が整いました、実際にさくらのクラウド上にリソースを作成してみましょう。
今回はGitHubに登録している公開鍵でログインできるサーバを1台作成します。

git checkout main
git pull origin main
git checkout -b feat/add-server

2つのファイルを作成します。

ご自身のGitHubユーザ名などに置き換えてください。

terraform/terraform.tfvars
github_username = "tar-xzvff"
terraform/server.tf
variable "github_username" {
  type        = string
  description = "GitHub username for fetching public SSH keys"
}

data "sakuracloud_archive" "ubuntu" {
  os_type = "ubuntu2404"
}

data "http" "key" {
  url = format("https://github.com/%s.keys", var.github_username)
}

resource "sakuracloud_disk" "server" {
  name              = "server"
  source_archive_id = data.sakuracloud_archive.ubuntu.id
}

resource "sakuracloud_server" "server" {
  name        = "server"
  disks       = [sakuracloud_disk.server.id]
  core        = 1
  memory      = 1
  description = "terraformから作成"

  network_interface {
    upstream = "shared"
  }

  disk_edit_parameter {
    hostname        = "server"
    disable_pw_auth = true
    ssh_keys        = split("\n", trimspace(data.http.key.response_body))
  }
}

手元で作成したファイルをPushしPull Request作成します。

git add terraform/
git commit -m "Add new server"
git push origin feat/add-server

terraform plan の結果がPull Requestのコメントとして投稿されます。
マージ前に確認をしましょう。
スクリーンショット 2025-12-03 23.24.55.png

CIも全て通ったことが確認できたらマージしましょう。

スクリーンショット 2025-12-03 23.25.06.png

マージ後 terraform applyのワークフローが実行されます。

スクリーンショット 2025-12-03 23.25.22.png

今回は約2分半ほどで完了しました。

スクリーンショット 2025-12-03 23.28.30.png

Pull Requestのコメントを確認するとterraform applyの結果が投稿が確認できます。

スクリーンショット 2025-12-03 23.42.33.png

さくらのクラウドのコントロールパネルから作成されたサーバを確認します。

server-list.png

これでGitHub ActionsによるTerraformでのさくらのクラウド上へのリソース作成が完了となります。

ポイント

Terraform Backendの設定

さくらのクラウド オブジェクトストレージを利用する場合は、環境変数の設定が必要となります。
以下の 2 つの値に WHEN_REQUIRED をセットします。

  • AWS_REQUEST_CHECKSUM_CALCULATION
  • AWS_RESPONSE_CHECKSUM_VALIDATION

今回はこれらの値を workflow の env で指定しています。詳細は以下をご覧ください。
https://cloud.sakura.ad.jp/news/2025/02/04/objectstorage_defectversion/

terraform apply ワークフローでの排他制御

万が一、同時に terraform apply が実行された場合、エラーや tfstate の破損などにつながる恐れがあるため、concurrency を設定し terraform apply が複数同時に実行されないようにしています。

また、cancel-in-progress: false を指定することで、すでに実行中の apply を途中で強制停止せず、後から来た実行はキューにたまるようになっています。
Terraform の実行を中断すると状態の不整合につながりやすいため、この設定は特に重要です。

感想

今回は最低限の構成でGitHub Actionsによるさくらのクラウドの管理の仕組みをご紹介しました。
実際の運用では Branch ruleset などを活用することで、より安全にインフラを管理する仕組みを実現できます。
また、このような GitOps を行うことで Source of Truth を実現できます。
情報の不整合性の排除、権限と操作の集中管理、履歴管理による再現性の確保などさまざまな恩恵を享受することができます。

この記事をきっかけに、さくらのクラウドを CI/CD で管理することにトライしてみようと思っていただけたら幸いです。

今回の記事で設定したファイル一式を含んだ、GitHubリポジトリテンプレートも用意しました。
このテンプレートからリポジトリを作成することで、より簡単に本記事と同じ環境を構築できます。

ぜひご活用ください
https://github.com/tar-xzvff/sacloud-gitops-template/

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?