❄️

Snowflake Terraform Providerの0.95でfully_qualified_nameが導入されて快適に!

2024/09/08に公開

最近Snowflake Terraform Providerと戯れているのですが、皆さん新しいバージョンに追従できていますでしょうか?

新しいバージョンに上げるたびに破壊的変更に悩まされて上げるのがおっくうになると思いますが、最近出た0.95へ上げるのは結構モチベーション上がるであろう機能改善が来ておりました!

それは fully_qualified_name というものです↓
https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers

何ができるようになったのか

これまではTerraformと戯れているときに特に一番登場するであろうsnowflake_grant_privileges_to_account_role (もしくはdatabase_role版)でリソース名を完全修飾名で入れないといけなくて(マジでエスケープめんどくさい)、依存関係を解決させようと頑張ってリソース参照させたときに1行めっちゃ長くなって、抽象化させてfor_eachで回した日には可読性とはどこへやら...という気持ちになって、メンテナンス性を上げているつもりが、下がってやしないか...?とちょっとした矛盾を抱える日々でした。

今回のアップデートで基本的に必要なリソースにはほぼすべてfully_qualified_nameという完全修飾名が保存される属性が追加されているので、それを参照することができるようになり開発者体験が爆上がりすること間違いなしです(神アプデ!)

サンプルコード

効果が分かりやすいように変数を使いながら抽象化してfor_eachを利用しながらリソースを定義しているようなケースをサンプルコードにしています。
スキーマを用意するついでに権限をセットで用意するようなユースケースです。(READ系の権限が付いたdatabase roleをセットで追加するようなコードです)

terraform {
  required_providers {
    snowflake = {
      source  = "Snowflake-Labs/snowflake"
      version = "0.95.0"
    }
  }
}

provider "snowflake" {
  # settings ...
}

# Database
resource "snowflake_database" "zenn" {
  name = "ZENN_DB"
}

# Schema and Database role
## サンプルとしてスキーマとそのスキーマへのREAD権限を持つDatabase Roleをセットで作るケースを想定(細かいオプションは割愛)
locals {
  schemas = {
    zenn = {
      schema_name = "ZENN"
      database    = snowflake_database.zenn.name
    }
    # add another schema
  }
}

# Schemas
resource "snowflake_schema" "schemas" {
  for_each = local.schemas

  name     = each.value.schema_name
  database = each.value.database
}

# Database roles (READ priviledges on each schema)
resource "snowflake_database_role" "read_on_schema" {
  for_each = local.schemas

  name     = "${each.value.schema_name}__READ"
  database = each.value.database
}

# grant USAGE on schema to READ role
resource "snowflake_grant_privileges_to_database_role" "usage_on_schema_to_read_role" {
  for_each = local.schemas

  database_role_name = snowflake_database_role.read_on_schema["${each.key}"].fully_qualified_name
  # 古い書き方だとこうなる
  # database_role_name = "\"${snowflake_database_role.read_on_schema["${each.key}"].database}\".\"${snowflake_database_role.read_on_schema["${each.key}"].name}\""
  privileges = ["USAGE"]
  on_schema {
    schema_name = snowflake_schema.schemas["${each.key}"].fully_qualified_name
    # 古い書き方だとこうなる
    # schema_name = "\"${snowflake_schema.schemas["${each.key}"].database}\".\"${snowflake_schema.schemas["${each.key}"].name}\""
  }
}

# grant SELECT on future TABLES in schema to READ role
resource "snowflake_grant_privileges_to_database_role" "select_on_future_tables_in_schema_to_read_role" {
  for_each = local.schemas

  database_role_name = snowflake_database_role.read_on_schema["${each.key}"].fully_qualified_name
  # 古い書き方だとこうなる
  # database_role_name = "\"${snowflake_database_role.read_on_schema["${each.key}"].database}\".\"${snowflake_database_role.read_on_schema["${each.key}"].name}\""
  privileges = ["SELECT"]
  on_schema_object {
    future {
      object_type_plural = "TABLES"
      in_schema          = snowflake_schema.schemas["${each.key}"].fully_qualified_name
      # 古い書き方だとこうなる
      # in_schema = "\"${snowflake_schema.schemas["${each.key}"].database}\".\"${snowflake_schema.schemas["${each.key}"].name}\""
    }
  }
}

# grant SELECT on future VIEWS in schema to READ role
resource "snowflake_grant_privileges_to_database_role" "select_on_future_views_in_schema_to_read_role" {
  for_each = local.schemas

  database_role_name = snowflake_database_role.read_on_schema["${each.key}"].fully_qualified_name
  # 古い書き方だとこうなる
  # database_role_name = "\"${snowflake_database_role.read_on_schema["${each.key}"].database}\".\"${snowflake_database_role.read_on_schema["${each.key}"].name}\""
  privileges = ["SELECT"]
  on_schema_object {
    future {
      object_type_plural = "VIEWS"
      in_schema          = snowflake_schema.schemas["${each.key}"].fully_qualified_name
      # 古い書き方だとこうなる
      # in_schema = "\"${snowflake_schema.schemas["${each.key}"].database}\".\"${snowflake_schema.schemas["${each.key}"].name}\""
    }
  }
}

0.94.1まで

こんな具合に長いです。
(VSCodeが間違っているところをハイライトしてくれるからいいものの、サンプルコード書きながら何回修正したことか...一発で誤字脱字もなくこれを書ける人はすごいと思います。マジで\"がいる意味が分からない)

database_role_name = "\"${snowflake_database_role.read_on_schema["${each.key}"].database}\".\"${snowflake_database_role.read_on_schema["${each.key}"].name}\""

0.95.0だとこう

こうあるべきだよなと思ういい感じの書き方ができるようになりました

database_role_name = snowflake_database_role.read_on_schema["${each.key}"].fully_qualified_name

おまけ

ステートを見るとこんな感じで入っています(特別にdiffでハイライトさせています)

# snowflake_database_role.read_on_schema["zenn"]:
resource "snowflake_database_role" "read_on_schema" {
    comment              = null
    database             = "ZENN_DB"
+   fully_qualified_name = "\"ZENN_DB\".\"ZENN__READ\""
    id                   = "\"ZENN_DB\".\"ZENN__READ\""
    name                 = "ZENN__READ"
    show_output          = [
        {
            comment                   = null
            created_on                = "2024-09-07T20:04:37.592-07:00"
            database_name             = "ZENN_DB"
            granted_database_roles    = 0
            granted_to_database_roles = 0
            granted_to_roles          = 0
            is_current                = false
            is_default                = false
            is_inherited              = false
            name                      = "ZENN__READ"
            owner                     = "ACCOUNTADMIN"
            owner_role_type           = "ROLE"
        },
    ]
}

最後に

なんとなくTerraformを始めるとリソース名を直書きする形で始めてしまい、依存関係うまく解決できなくてリソース追加が一発で成功しないなんてことが皆さんの周りにもあるかと思いますが(特にGrant系は起こりがち)、今後はきっちり依存関係をしっかり解決できるようにdepends_onに頼らない形でコーディングをしやすくなるかなと思います。

神アプデが来て、どんどん使いやすく安定化してきているのでTerraform化していくモチベーションも上がっていきますね。

Discussion