❄️

TerraformでSnowflake環境構築スターターキットを作る試み

2021/12/20に公開

はじめに

Snowflake Advent Calendar 2021 11日目の記事になります。はい、11日目です。これは11日目なんです。遅くなって本当にごめんなさい。心の中で焼き土下座しながら投稿しています。

Snowflake環境構築の初期段階で必要なものをササっと作れるスターターキットを、Terraformで作ってみる取り組みのお話です。

背景

私が所属する会社では、Snowflakeを利用した環境構築プロジェクトがいくつか進行しています。それぞれのプロジェクトの情報を共有する中で、初期段階でよく使うお決まりリソース構成がなんとなくわかってきました。
そこで、社内Snowflakeユーザの間で プロジェクト開始時にリソースを速攻で展開できるスターターキットを作ろう! という取り組みを行っています。
本記事では、作成しているスターターキットについて、簡単にご紹介したいと思います。

やったこと

リソースのコード化

まず何よりもやりたいことは、前述のとおり「Snowflake利用プロジェクトで、開始時に速攻でリソースを展開できるようにしたい」です。
TerraformのSnowflakeプロバイダを使ってTerraformingしていきます。よく使うリソース構成をコード化し、ソースをコピペしてパラメータを変更すればOKなサンプルコードセットにまとめておけば、パパっと構築できます。ついでに、構成の使いまわしができて嬉しい感じになるはず。

というわけで、やったことは次になります:

  • よく使うリソース構成をTerraformでコード化
  • コピペしてパラメータを変更すればOKなサンプルコードセットにまとめる

リソースの洗い出し

次に、よく使うリソースにどんなものがあるか、洗い出しを行いました。挙がったものは、

  • リソースモニター
  • ストレージ統合一式(Integrationと外部のストレージ)
  • ユーザー個別環境一式(ユーザー、ウェアハウス、データベース)
    ...などです。これらのサンプルコードセットをスターターキットに詰め込むことにしました。

その他

さらに、 個人的な趣味で作りたかった 社内のSnowflakeユーザーみんなが使うなら、やったほうが良さそうだなーと思った要素を追加したりしました。

  • チームで開発するなら使うであろうバックエンド等の設定
  • 必要なツールが一式揃ったDockerイメージ
  • ついでにCI/CD(Gitlab CI/CD)も組んじゃえ(本記事では省略)

構成

全体図

AWS部分が若干目立つ気がしますが、メインは右側じゃなくて下側にあるSnowflakeブロック群です。ブラウザバックしないで下さいお願いします。
このように、複数のリソース構成(+開発環境のイメージ)を配下に置く形にしています。

Terraformディレクトリ構成

複数のリソース構成と開発環境を配下に置くため、こんな形にしています。

terraform
  ├─environments      # 開発環境
  │  ├─dev            # 作業場所
  │  ├─image          # イメージ。AWS ECRで保存
  │  └─backend        # バックエンド。S3バケットとDynamoDBテーブル(※管理対象外)
  │
  ├─examples          # サンプルソース置き場
  │  ├─ex_1
  │  ├─ex_2
  │  :
  └─module            # モジュール置き場
      ├─snowflake
      └─aws

terraform/environments/devが作業場所です。ここに、使用するproviderやbackend設定と、examples/以下にあるサンプルコードを参照するモジュールを定義します。
また、backendとして使用するS3バケットとDynamoDBテーブルについては、「Terraformが利用するインフラリソースはTerraform管理外に置く」ことが推奨されているため、管理下に置かずソースのみ置いておきます。

https://www.terraform.io/language/settings/backends/s3#multi-account-aws-architecture

Terraform is an administrative tool that manages your infrastructure, and so ideally the infrastructure that is used by Terraform should exist outside of the infrastructure that Terraform manages. This can be achieved by creating a separate administrative AWS account which contains the user accounts used by human operators and any infrastructure and tools used to manage the other accounts.

実際のプロジェクトでは、開発系とは別のリソース管理系(インフラ系など)の配下におくか、これらのみ単発コマンドで作成することを想定しています。

Terraformソース

作業場所

作業場所(terraform/environments/dev)の内容をいくつか挙げてみます。

まず、provider設定はこんな感じです。AWS・Snowflakeの両方のプロバイダを使います。

provider.tf
terraform {
  required_version = ">= 1.0.11"

  required_providers {
    snowflake = {
      source  = "chanzuckerberg/snowflake"
      version = "0.25.29"
    }
    aws = {
      version = "3.69.0"
    }
  }
}

# 
# Snowflake
# 
# ACCOUNTADMINを使うときはこちら
provider "snowflake" {
  alias            = "snowflake_accountadmin"
  account          = var.snowflake_account
  username         = var.snowflake_username
  private_key_path = var.snowflake_private_key_path
  role             = "ACCOUNTADMIN"
}

# Snowflake SYSADMINで十分なときはこちら
provider "snowflake" {
  alias            = "snowflake_sysadmin"
  account          = var.snowflake_account
  username         = var.snowflake_username
  private_key_path = var.snowflake_private_key_path
  role             = "SYSADMIN"
}

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

Terraformが使用するSnowflakeユーザーの権限設定については、こちらの記事を参考に、ACCOUNTADMIN権限の行使は必要な場合だけにとどめられるようにしています。

https://zenn.dev/u2/articles/7796b0e96d0266

次にbackendです。あらかじめ作成してあるS3バケットとDynamoDBテーブルを指定します。

backend.tf
# S3バケット、DynamoDBテーブル定義は terraform/environments/backend/resource.tf にある
terraform {
  backend "s3" {
    bucket         = "snowflake-starterkit"       # 作成済み
    key            = "dev/terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "snowflake-starterkit-lock"  # 作成済み
  }
}

最後に main.tf です。

main.tf
# 
# ここからは各サンプルコードを呼び出すだけ
# パラメータの与え方は examples/ 以下の各サンプルで書く
# 

module "example_storage_integration_s3" {
  source = "../../examples/storage_integration_s3"
  providers = {
    snowflake = snowflake.snowflake_accountadmin
    aws       = aws
  }
  env = "dev"
}

これはストレージ統合を作るサンプルコードを参照しているものです。ストレージ統合を作成するにはACCOUNTADMINが必要なので、snowflake.snowflake_accountadminプロバイダを渡しています。さらに、サンプルコードではS3バケット・IAMロール・IAMポリシーも作成するので、awsプロバイダも渡します。

サンプルコード

terraform/examples/以下に、リソース構成ごとのサンプルコードを置いています。ここでは、さきほど作業環境から参照していた「ストレージ統合」のサンプルコードを紹介します。

作成するリソースの構成は、こんな感じです。

では、ソースを見ていきます。
まず、外部ステージとして使用するバケット名など、いくつかの変数を定義しておきます。

locals.tf
locals {
  snowflake_storage_integration   = "${var.env}-s3_integration" 
  snowflake_external_stage_bucket = "${var.env}-snowflake-external-stage"
  snowflake_external_stage_role   = "${var.env}-snowflake-external-stage-role"
  snowflake_external_stage_policy = "${var.env}-snowflake-external-stage-policy"
}

次に、定義した変数を渡して、S3バケット、IAMロール、IAMポリシーを定義します。一括作成するモジュールをあらかじめ作っておいています(中身は省略します)。

aws_main.tf
# 外部ステージ(S3)
module "snowflake_external_stage" {
  # S3・IAMロール・IAMポリシーを作成するモジュール
  # Snowflake側のExternal IDを設定できるようにしてある
  source                 = "../../modules/aws/s3_snowflake_external_stage"
  providers = {
    aws = aws
  }

  bucket_name            = local.snowflake_external_stage_bucket
  role_name              = local.snowflake_external_stage_role
  policy_name            = local.snowflake_external_stage_policy
  snowflake_external_id  = [snowflake_storage_integration.s3_integration.storage_aws_external_id]
  snowflake_iam_user_arn = [snowflake_storage_integration.s3_integration.storage_aws_iam_user_arn]
  env                    = var.env

  depends_on = [
    # storage integration が先に存在する必要がある
    snowflake_storage_integration.s3_integration
  ]
}

最後に、Snowflakeのストレージ統合を定義します。

snowflake_main.tf
# 外部ステージを参照するためのintegration
resource "snowflake_storage_integration" "s3_integration" {
  provider = snowflake
  name    = local.snowflake_storage_integration
  type    = "EXTERNAL_STAGE"
  enabled = true
  storage_provider = "S3"
  storage_allowed_locations = [
    # 名前がわかっているので変数で与える
    "s3://${local.snowflake_external_stage_bucket}/"
  ]

  # 名前がわかっているので変数で与える
  storage_aws_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.snowflake_external_stage_role}"
}

これで、いつものTerraformコマンドを実行すれば、ストレージ統合をパパっと作成することができます。あとは、locals.tf に定義したs3バケット名などをプロジェクトに合わせて設定したり、プロジェクトごとに必要なもの(例えば、ストレージ統合を使用するタスクなど)だけ作成していけば、環境構築でスタートダッシュを決められる、という感じです。

Dockerイメージ

最後に、スターターキット開発環境のDockerイメージをご紹介します。
Terraform + SnowSQL + AWS CLIという環境になっています。これは、local-execでSnowSQLやAWS CLIのコマンドを使用することがたまーにあるためと、スターターキット開発中にコンテナ内でコマンドを使えると何かと便利だからです。

Dockerfile
################################################################################
# Base Image
################################################################################
FROM ubuntu:20.04

################################################################################
# Environments
################################################################################
# timezone
# "questions will narrow this down by presenting a list of cities, representing the time zones in which they are located." への対策
ENV TZ=Asia/Tokyo 

################################################################################
# Arguments
################################################################################
# terraform version
ARG terraform_version="1.0.11"

# Snowflake version
ARG snowflake_bootstrap_version="1.2"
ARG snowflake_version="1.2.20"

# Snowflake install option
ARG snowsql_dest="/root/.snowsql/${snowflake_version}"
ARG snowsql_login_shell="/root/.bashrc"

################################################################################
# Run commands
################################################################################
# Set timezone
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# Install tools
RUN apt update && \
  apt-get install --yes sudo make curl unzip jq git && \
  # Install awscliv2
  curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
  unzip awscliv2.zip && \
  rm -rf awscliv2.zip && \
  sudo ./aws/install && \
  # Install terraform
  curl -O "https://releases.hashicorp.com/terraform/${terraform_version}/terraform_${terraform_version}_linux_amd64.zip" && \
  unzip ./terraform_${terraform_version}_linux_amd64.zip -d /usr/local/bin/ && \
  rm -rf ./terraform_${terraform_version}_linux_amd64.zip && \
  # Install SnowSQL
  curl -O "https://sfc-repo.snowflakecomputing.com/snowsql/bootstrap/${snowflake_bootstrap_version}/linux_x86_64/snowsql-${snowflake_version}-linux_x86_64.bash" && \
  sudo touch "${snowsql_login_shell}" && \ 
  mkdir -p ${snowsql_dest} && \
  export SNOWSQL_DEST=${snowsql_dest} && export SNOWSQL_LOGIN_SHELL=${snowsql_login_shell} && bash ./snowsql-${snowflake_version}-linux_x86_64.bash

おわりに

スターターキットは、今まさに開発中のものです。キット内容もまだまだ少ないです。
もっと内容を充実させたら、また記事で公開したいと思います。

それと、最後にもう一つ、、、

盛大に遅刻して本当にすみませんでした!!!!!!!!!!111

Discussion