Snowflake × Terraform のマルチアカウント環境をGitHub ActionsでCI/CD化してみた

Snowflake × Terraform のマルチアカウント環境をGitHub ActionsでCI/CD化してみた

2025.10.11

はじめに

データ事業本部のkasamaです。
今回は、開発環境と本番環境でアカウントが分かれているSnowflake × Terraform環境を、GitHub ActionsでCI/CD化してみました。

前提

前提として、オープンソース版のTerraformの話であり、HCP Terraform(クラウド版)は考慮していません。
以下ブログの構成を参考にし、自分なりにカスタマイズして使用します。OIDC設定や一連の流れは大体同じですが、マルチアカウント構成箇所が少し異なるため、その辺りを中心に記載します。
https://dev.classmethod.jp/articles/snowflake-terraform-design-with-functional-and-access-role/
https://dev.classmethod.jp/articles/snowflake-how-to-terraform-with-github-actions/

以下の構成を元に検討していきたいと思います。moduleをaccess role + リソース単位で作成し、dev/prdごとのmain.tfで呼び出す実装です。terraformのinitやplan,applyコマンドの実行はGitHub Actions経由で行います。

			
			snowflake-terraform-sample % tree
.
├── .github/
│   └── workflows/
│       ├── dev-snowflake-terraform-cicd.yml
│       └── prd-snowflake-terraform-cicd.yml
├── cfn
│   └── create_state_gha_resources.yml
├── environments
│   ├── dev
│   │   ├── backend.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   └── prd
│       ├── backend.tf
│       ├── main.tf
│       ├── outputs.tf
│       ├── variables.tf
│       └── versions.tf
├── modules
│   ├── access_role_and_database
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   ├── access_role_and_file_format
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   ├── access_role_and_schema
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   ├── access_role_and_stage
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   ├── access_role_and_storage_integration
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   ├── access_role_and_warehouse
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   ├── aws_storage_integration
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   ├── functional_role
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── versions.tf
└── README.md

		

GitHub Actions 処理フロー

今回のプロジェクト構成ではGit Flow戦略を採用しています。Git Flowは、mainブランチ(本番環境)とdevelopブランチ(開発環境)を中心とした、環境ごとにブランチを分離する戦略です。featureブランチで開発を行い、developブランチで統合・検証した後、mainブランチへマージして本番リリースを行います。各ブランチが特定の環境に対応し、ブランチへのマージをトリガーに自動デプロイが実行されます。

https://qiita.com/homhom44/items/9f13c646fa2619ae63d0

  • 開発環境 (feature → develop): featureブランチからdevelopブランチへのPRマージ時に開発環境へ自動デプロイ
  • 本番環境 (develop → main): 開発環境での検証完了後、developブランチからmainブランチへのPRマージ時に本番環境へ自動デプロイ

各環境で、PR作成時にterraform planでプレビュー、PRマージ時にterraform applyで実際にデプロイが実行されます。

開発環境デプロイフロー (feature → develop)

  1. PR作成時(terraform plan)

    • 開発者がfeatureブランチからdevelopブランチへPRを作成
    • GitHub Actionsワークフローが自動トリガー
    • AWSにOIDC認証し、S3からstateを取得してterraform init
    • コード品質チェック(fmt/validate/TFLint/Trivy)を実行
    • Snowflake環境にKey-pair認証でterraform planを実行
    • Plan結果をPRにコメント投稿
  2. PRマージ時(terraform apply)

    • PRが承認されdevelopブランチへマージ
    • pushイベントでワークフローが再トリガー
    • AWSにOIDC認証し、terraform initを実行
    • コード品質チェックを再実行
    • terraform apply -auto-approveで開発環境Snowflakeへ自動デプロイ

本番環境デプロイフロー (develop → main)

  1. PR作成時(terraform plan)

    • 開発環境での検証完了後、developブランチからmainブランチへPRを作成
    • GitHub Actionsワークフローが自動トリガー
    • AWSにOIDC認証し、S3からstateを取得してterraform init
    • コード品質チェック(fmt/validate/TFLint/Trivy)を実行
    • 本番Snowflake環境にKey-pair認証でterraform planを実行
    • Plan結果をPRにコメント投稿
  2. PRマージ時(terraform apply)

    • レビュー・承認を経てmainブランチへマージ
    • pushイベントでワークフローが再トリガー
    • AWSにOIDC認証し、terraform initを実行
    • コード品質チェックを再実行
    • terraform apply -auto-approveで本番環境Snowflakeへ自動デプロイ

本番環境の処理フローは開発環境と同様ですが、ブランチ保護の設定で違いを持たせることができます。
一般的には、本番環境へのデプロイとなるmainブランチへのPRマージには、最低1人以上の承認を必須とします。一方、開発環境となるdevelopブランチへのマージは、プロジェクトの方針により柔軟に設定します。例えば、TerraformのState Lockを考慮してローカルからのデプロイを禁止している場合、developブランチへのマージ承認を任意にすることで、CI/CD経由での検証サイクルを高速化できます。または、コード品質を重視してdevelopブランチは承認者1人、mainブランチは管理者と承認者の2人必須、といった段階的な承認体制も選択できます。

GitHubのRulesetsでマージ承認などのbranch保護設定が可能です。以下の記事が参考になりました。
https://zenn.dev/kuritify/articles/github-rulesets

試してみましたが、作成はできるもののFree PlanのPrivateリポジトリへ適用できないようでした。
Screenshot 2025-10-11 at 10.20.58

実装

.github/workflows/dev-snowflake-terraform-cicd.yml
			
			name: "Snowflake Terraform CI/CD - DEV"

on:
  push:
    branches:
      - develop
    paths:
      - "environments/dev/**"
      - "modules/**"
      - ".github/workflows/dev-snowflake-terraform-cicd.yml"
  pull_request:
    branches:
      - develop
    paths:
      - "environments/dev/**"
      - "modules/**"
      - ".github/workflows/dev-snowflake-terraform-cicd.yml"

jobs:
  plan-dev:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    environment: dev

    env:
      # Terraform
      TF_VERSION: "1.11.0"
      # Snowflake - TF_VAR_プレフィックスでTerraform変数にマッピング
      TF_VAR_snowflake_organization_name: ${{ secrets.SNOWFLAKE_ORG_NAME }}
      TF_VAR_snowflake_account_name: ${{ secrets.SNOWFLAKE_ACCOUNT_NAME }}
      TF_VAR_snowflake_private_key: ${{ secrets.SNOWFLAKE_PRIVATE_KEY }}

    permissions:
      id-token: write # OIDCを利用する際に必須
      contents: read # actions/checkout のために必要
      pull-requests: write # PRコメント投稿用

    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

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

      - name: Set up AWS credentials
        uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 # v5.0.0
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
          audience: sts.amazonaws.com

      - name: Terraform format
        run: terraform fmt -check -recursive
        working-directory: environments/dev

      - name: Terraform Init
        run: terraform init -upgrade -no-color
        working-directory: environments/dev

      - name: Terraform validate
        run: terraform validate -no-color
        working-directory: environments/dev

      - name: Setup TFLint
        uses: terraform-linters/setup-tflint@ae78205cfffec9e8d93fd2b3115c7e9d3166d4b6 # v5.0.0
        with:
          tflint_version: v0.58.0

      - name: Run TFLint
        run: tflint --init && tflint -f compact --minimum-failure-severity=error
        working-directory: environments/dev

      - name: Run Trivy Security Scan
        uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
        with:
          scan-type: "config"
          scan-ref: "environments/dev"
          format: "table"
          exit-code: 1
          severity: CRITICAL,HIGH
          ignore-unfixed: true

      - name: Terraform Plan
        id: plan
        if: github.event_name == 'pull_request'
        run: |
          terraform plan -no-color -out=tfplan.binary
          terraform show -no-color tfplan.binary > tfplan.txt
        working-directory: environments/dev

      - name: Post Plan to PR
        if: github.event_name == 'pull_request' && steps.plan.outcome == 'success'
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd  # v8.0.0
        with:
          script: |
            const fs = require('fs');
            const plan = fs.readFileSync('environments/dev/tfplan.txt', 'utf8');

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `### Terraform Plan - Dev

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

            \`\`\`terraform
            ${plan.substring(0, 65000)}
            \`\`\`

            </details>`
            });

      - name: Terraform Apply
        if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
        run: terraform apply -auto-approve
        working-directory: environments/dev

		

開発環境用のGitHub Actionsワークフロー設定です。

  • on: トリガーをdevelopブランチに限定し、pathsフィルタで関連ファイル変更時のみ実行します
  • env: 事前に設定したGitHub Secretsの値をTF_VAR_プレフィックスで参照し、Terraform変数へ自動マッピングします
  • permissions: 最小権限の原則に基づき、id-token: write(OIDC認証)、contents: read(チェックアウト)、pull-requests: write(PRコメント)のみ指定します
  • サードパーティAction: actions/checkout@08c6903...のようにコミットSHAで固定し、タグ改ざん攻撃を防止します
  • AWS認証: OIDCで一時的な認証情報を取得し、長期アクセスキーの漏洩リスクを削減します
  • コード品質チェック: terraform fmt/validate、TFLint、Trivyを順次実行し、問題があればワークフローを停止します
  • terraform plan: github.event_name == 'pull_request'の条件でPR作成時のみ実行します
  • Plan結果のPR投稿: steps.plan.outcome == 'success'の条件でplanが成功した場合のみ、actions/github-scriptでPRにコメントを投稿します
  • terraform apply: github.ref == 'refs/heads/develop' && github.event_name == 'push'の条件でdevelopブランチへのマージ時のみ実行します

セキュリティ面で気をつけているポイントがあります。permissionsでは、デフォルトのwrite権限を付与せず、id-token: writecontents: readpull-requests: writeのみを明示的に指定することで、必要最小限の権限のみ付与し、攻撃対象領域を最小化しています。サードパーティActionは、バージョンタグ(v5.0.0)ではなく、コミットSHA(actions/checkout@08c6..)で固定しています。タグは後から書き換え可能なため、悪意のあるコードに置き換えられるリスクがありますが、コミットSHAは不変なのでこのリスクを防止できます。秘密情報の管理では、Snowflakeの秘密鍵などの機密情報をGitHub Secretsで管理し、ワークフローログに出力されないようにしています。GitHub Actionsは自動的にSecretsの値をマスキングするため、ログに秘密情報が露出するリスクを低減できます。また、今回は設定していないですが、サードパーティActionのバージョン更新を自動化するため、Dependabotの導入をしてもい良いと思います。

これらの内容は以下の記事を参考にしています。
https://docs.github.com/en/actions/reference/security/secure-use
https://zenn.dev/azu/articles/ad168118524135
https://zenn.dev/farstep/books/learn-github-actions/viewer/security-and-operations
https://zenn.dev/cybozu_ept/articles/573c706ec08b48

.github/workflows/prd-snowflake-terraform-cicd.yml
			
			name: "Snowflake Terraform CI/CD - PRD"

on:
  push:
    branches:
      - main
    paths:
      - "environments/prd/**"
      - "modules/**"
      - ".github/workflows/prd-snowflake-terraform-cicd.yml"
  pull_request:
    branches:
      - main
    paths:
      - "environments/prd/**"
      - "modules/**"
      - ".github/workflows/prd-snowflake-terraform-cicd.yml"

jobs:
  plan-prd:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    environment: prd

    env:
      # Terraform
      TF_VERSION: "1.11.0"
      # Snowflake - TF_VAR_プレフィックスでTerraform変数にマッピング
      TF_VAR_snowflake_organization_name: ${{ secrets.SNOWFLAKE_ORG_NAME }}
      TF_VAR_snowflake_account_name: ${{ secrets.SNOWFLAKE_ACCOUNT_NAME }}
      TF_VAR_snowflake_private_key: ${{ secrets.SNOWFLAKE_PRIVATE_KEY }}

    permissions:
      id-token: write # OIDCを利用する際に必須
      contents: read # actions/checkout のために必要
      pull-requests: write # PRコメント投稿用

    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

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

      - name: Set up AWS credentials
        uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 # v5.0.0
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
          audience: sts.amazonaws.com

      - name: Terraform format
        run: terraform fmt -check -recursive
        working-directory: environments/prd

      - name: Terraform Init
        run: terraform init -upgrade -no-color
        working-directory: environments/prd

      - name: Terraform validate
        run: terraform validate -no-color
        working-directory: environments/prd

      - name: Setup TFLint
        uses: terraform-linters/setup-tflint@ae78205cfffec9e8d93fd2b3115c7e9d3166d4b6 # v5.0.0
        with:
          tflint_version: v0.58.0

      - name: Run TFLint
        run: tflint --init && tflint -f compact --minimum-failure-severity=error
        working-directory: environments/prd

      - name: Run Trivy Security Scan
        uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
        with:
          scan-type: "config"
          scan-ref: "environments/prd"
          format: "table"
          exit-code: 1
          severity: CRITICAL,HIGH
          ignore-unfixed: true

      - name: Terraform Plan
        id: plan
        if: github.event_name == 'pull_request'
        run: |
          terraform plan -no-color -out=tfplan.binary
          terraform show -no-color tfplan.binary > tfplan.txt
        working-directory: environments/prd

      - name: Post Plan to PR
        if: github.event_name == 'pull_request' && steps.plan.outcome == 'success'
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd  # v8.0.0
        with:
          script: |
            const fs = require('fs');
            const plan = fs.readFileSync('environments/prd/tfplan.txt', 'utf8');

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `### Terraform Plan - Production

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

            \`\`\`terraform
            ${plan.substring(0, 65000)}
            \`\`\`

            </details>`
            });

      - name: Terraform Apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve
        working-directory: environments/prd

		

本番環境用のGitHub Actionsワークフロー設定です。
開発環境と同様の構成ですが、トリガーブランチがmain、作業ディレクトリがenvironments/prd、Apply条件がgithub.ref == 'refs/heads/main'となります。

事前準備

Snowflakeデプロイ用ユーザー作成

2025年10月現在、Snowflake CLIはOIDCに対応していますが、Snowflake Terraformは未対応ですので、Snowflakeデプロイ用ユーザーでのキーペア認証で接続します。
https://qiita.com/bgcanary/items/432f12744478e4165395

ローカルもしくはAWS CloudShell上などで以下のコマンドを実行しkey-pairを作成します。GitHub Secretsは後から値の参照ができないため、AWS SSM Parameterに値を格納しています。

			
			# RSA鍵ペア生成
openssl genrsa -out snowflake_private_key.pem 2048
openssl rsa -in snowflake_private_key.pem -pubout -out snowflake_public_key.pem

# 公開鍵を整形(改行を削除して1行にする)
PUBLIC_KEY=$(cat snowflake_public_key.pem | grep -v "BEGIN PUBLIC KEY" | grep -v "END PUBLIC KEY" | tr -d '\n')
echo $PUBLIC_KEY

# 秘密鍵をAWS SSM Parameter Storeに保存
aws ssm put-parameter \
    --name "/<your-unique-name>/dev/snowflake/terraform-user/private-key" \
    --value "$(cat snowflake_private_key.pem)" \
    --type "SecureString" \
    --overwrite

# 公開鍵(整形済み)を保存
aws ssm put-parameter \
    --name "/<your-unique-name>/dev/snowflake/terraform-user/public-key" \
    --value "$PUBLIC_KEY" \
    --type "String" \
    --overwrite

		

Snowflakeアカウントで以下のSQLを実行し、デプロイ用ユーザー等を作成します。

			
			USE ROLE SECURITYADMIN;

CREATE USER TERRAFORM_BLOG_USER
    TYPE = SERVICE
    RSA_PUBLIC_KEY='<YOUR_RSA_PUBLIC_KEY>'  -- 上記で整形した公開鍵を貼り付け
    DEFAULT_ROLE=PUBLIC;

CREATE ROLE TERRAFORM;

GRANT ROLE TERRAFORM TO USER TERRAFORM_BLOG_USER;
GRANT ROLE SECURITYADMIN TO ROLE TERRAFORM;
GRANT ROLE SYSADMIN TO ROLE TERRAFORM;
GRANT ROLE TERRAFORM TO ROLE ACCOUNTADMIN;

-- INTEGRATION作成権限の付与(ACCOUNTADMINで実行)
USE ROLE ACCOUNTADMIN;
GRANT CREATE INTEGRATION ON ACCOUNT TO ROLE TERRAFORM;

USE ROLE SYSADMIN;

CREATE OR REPLACE WAREHOUSE TERRAFORM_BLOG_WH 
    WAREHOUSE_SIZE=XSMALL
    AUTO_RESUME=TRUE
    AUTO_SUSPEND=60
    INITIALLY_SUSPENDED=TRUE
    STATEMENT_TIMEOUT_IN_SECONDS=300  -- 5min
    COMMENT='For terraform.';

-- SECURITYADMINにもウェアハウスの操作権限を付与(Terraform実行に必要)
GRANT USAGE ON WAREHOUSE TERRAFORM_BLOG_WH TO ROLE SECURITYADMIN;

-- MANAGE GRANTS権限をSYSADMINに付与(future grant実行に必要)
USE ROLE SECURITYADMIN;
GRANT MANAGE GRANTS ON ACCOUNT TO ROLE SYSADMIN;

		

AWS GitHub Actions用リソースデプロイ

以下は、Terraformデプロイに使用するIAM RoleやS3の一例です。リソース名等はプロジェクトによって変更いただければと思います。こちらをCloudFormationでデプロイしました。

cfn/create_state_gha_resources.yml
			
			AWSTemplateFormatVersion: 2010-09-09
Description: |-
  Snowflake-Terraform S3 & GitHub Actions Setup - Creates S3 bucket for Terraform state
  and IAM role for GitHub Actions with comprehensive configuration

Mappings:
  # AWS AccountIdによって値を切り替える
  EnvMapping:
    "123456789012":
      EnvName: dev

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "GitHub Configuration"
        Parameters:
          - GitHubAccountName
          - GitHubRemoteRepoName
          - GitHubOIDCProviderArn

Parameters:
  GitHubAccountName:
    Type: String
    Default: your-github-account
    Description: GitHub account name of the repository
  GitHubRemoteRepoName:
    Type: String
    Default: "terraform-snowflake-sample"
  GitHubOIDCProviderArn:
    Type: String
    Description: ARN of the existing GitHub OIDC Provider

Resources:
  # S3 Bucket for Terraform state
  TerraformStateBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub
        - "your-project-${EnvName}-s3-snf-state"
        - EnvName: !FindInMap
            - EnvMapping
            - !Ref "AWS::AccountId"
            - EnvName
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      Tags:
        - Key: service-id
          Value: your-project
        - Key: module
          Value: terraform-state
        - Key: project
          Value: integrated-analysis-env

  # IAM Role for GitHub Actions
  GitHubActionsRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - "your-project-${EnvName}-iamrole-snf-gha"
        - EnvName: !FindInMap
            - EnvMapping
            - !Ref "AWS::AccountId"
            - EnvName
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: TrustGitHubActionsOIDCProvider
            Effect: Allow
            Principal:
              Federated: !Ref GitHubOIDCProviderArn
            Action: sts:AssumeRoleWithWebIdentity
            Condition:
              StringLike:
                token.actions.githubusercontent.com:sub:
                  - !Sub "repo:${GitHubAccountName}/${GitHubRemoteRepoName}:*"
      Policies:
        - PolicyName: !Sub
            - "your-project-${EnvName}-iampolicy-snf-gha"
            - EnvName: !FindInMap
                - EnvMapping
                - !Ref "AWS::AccountId"
                - EnvName
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              # Terraform state S3 bucket permissions
              - Sid: TerraformS3StateBucketPolicy
                Effect: Allow
                Action:
                  - s3:ListBucket
                  - s3:GetBucketLocation
                  - s3:ListBucketMultipartUploads
                  - s3:ListBucketVersions
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:PutObject
                  - s3:DeleteObject
                Resource:
                  - !Sub "arn:aws:s3:::${TerraformStateBucket}"
                  - !Sub "arn:aws:s3:::${TerraformStateBucket}/*"
              # Permissions to create and configure S3 buckets used by Terraform modules
              - Sid: ModuleS3BucketManagement
                Effect: Allow
                Action:
                  - s3:CreateBucket
                  - s3:DeleteBucket
                  - s3:PutBucketVersioning
                  - s3:GetBucketTagging
                  - s3:PutEncryptionConfiguration
                  - s3:GetEncryptionConfiguration
                  - s3:PutBucketTagging
                  - s3:PutBucketPolicy
                  - s3:GetBucketPolicy
                  - s3:DeleteBucketPolicy
                  - s3:GetBucketLocation
                  - s3:GetBucketAcl
                  - s3:GetBucketCORS
                  - s3:ListBucket
                  - s3:GetBucketWebsite
                  - s3:GetBucketVersioning
                  - s3:GetAccelerateConfiguration
                  - s3:GetBucketRequestPayment
                  - s3:GetBucketLogging
                  - s3:GetLifecycleConfiguration
                  - s3:GetReplicationConfiguration
                  - s3:GetBucketObjectLockConfiguration
                Resource:
                  - !Sub
                    - "arn:aws:s3:::your-project-${EnvName}-s3-*"
                    - EnvName: !FindInMap
                        - EnvMapping
                        - !Ref "AWS::AccountId"
                        - EnvName
                  - "arn:aws:s3:::your-project-snowflake-*"
              # IAM permissions to create roles and attach inline policies for modules
              - Sid: ModuleIamRoleManagement
                Effect: Allow
                Action:
                  - iam:CreateRole
                  - iam:DeleteRole
                  - iam:GetRole
                  - iam:TagRole
                  - iam:PutRolePolicy
                  - iam:GetRolePolicy
                  - iam:DeleteRolePolicy
                  - iam:ListRolePolicies
                  - iam:ListAttachedRolePolicies
                  - iam:ListInstanceProfilesForRole
                  - iam:UpdateAssumeRolePolicy
                  - iam:UntagRole
                Resource:
                  - !Sub
                    - "arn:aws:iam::${AWS::AccountId}:role/your-project-${EnvName}-iamrole-*"
                    - EnvName: !FindInMap
                        - EnvMapping
                        - !Ref "AWS::AccountId"
                        - EnvName
                  - !Sub "arn:aws:iam::${AWS::AccountId}:role/your-project-snowflake-*"
              # SSM Parameter Store permissions
              - Sid: SSMParameterStorePolicy
                Effect: Allow
                Action:
                  - ssm:GetParameter
                  - ssm:GetParameters
                Resource:
                  - !Sub "arn:aws:ssm:ap-northeast-1:${AWS::AccountId}:parameter/your-project/snowflake/*"
Outputs:
  TerraformStateBucketName:
    Description: Terraform state bucket name
    Value: !Ref TerraformStateBucket
  GitHubActionsRoleArn:
    Description: IAM Role ARN for GitHub Actions
    Value: !GetAtt GitHubActionsRole.Arn

		

GitHub Secrets設定

GitHubリポジトリのSecretsを手動で設定します。
GitHubリポジトリのSettings > Environmentsを開き、New environmentからdev,prdを作成します。作成したEnvironment内でPRIVATE_KEYなどのSecretsを定義することでGitHub Actions内で参照します。

Screenshot 2025-10-11 at 15.31.49
Screenshot 2025-10-11 at 15.32.41

デプロイ

それでは実際にSnowflake Terraformをデプロイしてみます。Snowflake Terraformの実際の実装については、以下のブログの内容と同様のため、省略します。main.tfで設定値を定義してmoduleを呼び出す構成です。
https://dev.classmethod.jp/articles/snowflake-terraform-design-with-functional-and-access-role/

Screenshot 2025-10-11 at 16.20.22

実装した内容をローカルのfeatureブランチからpushします。

			
			@ snowflake-terraform-sample % git add .
@ snowflake-terraform-sample % git commit -m "feat(dev): Enable all Snowflake infrastructure modules"
@ snowflake-terraform-sample % git push origin

		

GitHub上でfeatureブランチ → developブランチに対するPull Requestを作成します。
Screenshot 2025-10-11 at 16.26.50

developブランチへのPull Requestが作成されるとGitHub Actionsが起動します。
Actionsログを確認すると正常に終了していることが確認できました。terraform applyは想定通りskipされています。
Screenshot 2025-10-11 at 16.28.29

Pull Requestに再度戻るとbotからterraform planコマンドの結果が出力されていることが確認できます。ClaudeのAIレビューを導入しているプロジェクトの場合は、terraform planの結果を要約させてみても良いですね。
Screenshot 2025-10-11 at 16.29.58
Screenshot 2025-10-11 at 16.30.14

問題ないと判断しmergeしました。GitHub Actionsが動き、今度はterraform planコマンドをskipし、terraform applyコマンドを実行しています。
Screenshot 2025-10-11 at 16.35.24

処理は正常終了し、Snowflakeアカウント上でリソースが存在することを確認できました。
Screenshot 2025-10-11 at 16.38.41

最後に

GitHub Actionsの設定は色々と考慮すべき内容が多いですので一つの参考になれば幸いです。

この記事をシェアする

FacebookHatena blogX

関連記事

Snowflake × Terraform のマルチアカウント環境をGitHub ActionsでCI/CD化してみた | DevelopersIO