🛕

Terraform・terraformer で Gmail の filter を管理する

2021/05/09に公開

published_at: 2020-09-13

[2021/5/9 変更]

  • Terraform 0.13 以上での terraform init を行う際の調整事項を末尾に追記
    ( terraform-provider-gmailfilter 1.0.1 で確認 )
  • 問い合わせ先を Twitter へのリプライからこの記事への Discussion コメントに変更

すこし前に、 gmailfilters というツールがあるのを知ってはいました。
が、おそろしいことにこれまで設定したフィルタがふっとんで(消えて)しまうことがありえるとわかっていたので、継続採用は見送ってました。 [1]
そんなときに、ふと Gmailのフィルタを管理するためのTerraformプロバイダーを作った - febc技術メモ というブログを発見。Terraform であれば既存のものに影響なく管理できそうと予想して、使ってみました。

Key Takeaways


gmail-filter.png

  • Terraform・terraformer で Gmail の filter は、管理できる
  • そこに至るまでには Google ( GCP + G Suite ) のちょっとした壁があり、作成したサービスアカウントへAPI アクセスの委任を管理画面で行うのが良さそう
  • 今のところ、設定には以下の制約がありそう
    • 一度設定したフィルタを再度設定しようとするとエラーになる(コメントアウトしておく等で回避できる)
    • 1つのフィルタで付けられるラベルは1つずつの模様

今回試した流れの概要

  • 参考情報の参照
  • GCP にサービスアカウントの作成と G Suite 管理画面でAPIアクセス権の調整
  • 既存 label , filter の保存(バックアップ)を terraformer
  • 新しい label , filter の設定を terraform-provider-gmailfilter

参考情報の参照

サンプルコード https://github.com/sogaoh/TerraformPractice/tree/master/Community/GmailFilter の refs にだいたいまとめてはあるのですが、@yamamoto_febc さんのブログを大いに参考にしました。苦戦中に相談もさせてもらって心強かったです。

大いに参考にしたブログ2つ

苦戦中の相談の様子

(Twitter埋め込みではなく画像なのでご注意を。
キャプションをリンクにしていますので適宜ご利用ください)


https://twitter.com/yamamoto_febc/status/1304212390217543682


https://twitter.com/yamamoto_febc/status/1304389099944509440

ここまででの気付きは、2つありました。

  • G Suite への API での操作に先立っての認証方法に Default Application Credentials というのもある
  • この機構を実現するベースになっているのは、Gmail API

Default Application Credentials を諦めてサービスアカウントでの認証を探る

Default Application Credentials にだいぶとらわれてしまっていたのですが、何度かエラーを見ているうちに、自分のこの MacBook Air の環境ではこれは使えないのでは、予想しました。
たしかに、.zshrc で GOOGLE_CREDENTIALSGOOGLE_APPLICATION_CREDENTIALSも仕込んでしまっていて、それが「勝って」しまっていたんだろうな、と。。 [2]

エラーメッセージが一貫して private key should be a PEM or plain PKCS1 or PKCS8; parse error: asn1: syntax error: sequence truncated と言っていたので、たぶん、サービスアカウントでの認証がベターなんだろう、というのは感じてはいたものの、なかなかわからなかったのはどうサービスアカウントの権限を設定するのか。。

失敗を繰り返す terraform apply のエラーメッセージが変化したときに検索してみたところ見つけた CloudGate HelpCenter の 【Q&A】G Suite:ユーザー操作(作成・更新・削除)の際にプロビジョニングエラーが発生する という文書で、「APIスコープ」の設定が必要なことを掴みました。

GCP にサービスアカウントの作成と G Suite 管理画面でAPIアクセス権の調整

APIスコープの設定をして、ついに terraform apply が成功したときの叫びがこちら。

所要時間、実に 9/11 21:11 - 9/12 0:53 の約4時間ほど・・・(間に家事してたけど)

(こちらもTwitter埋め込みではなく画像です。キャプションをリンクにしています)


https://twitter.com/sogaoh/status/1304447900303286274

整理して振り返ると、ポイントは以下になると思います。

1. GCP 管理コンソールで作成したサービスアカウントに、Domain wide deligation(G Suite ドメイン全体の委任を有効に) する -> クライアントID が発生するのでそれを控える

以下 G Suite 管理画面での設定にはAdmin権限が必要なのでシステム管理者との調整が必要なことをご留意ください

2. G Suite 管理画面で Google Cloud Platform サービスへのアクセスコントロールがオンになっているか確認する
  • https://admin.google.com/ -> アプリ -> その他のGoogleサービス (-> Google Cloud Platform のサービスステータス オン を確認)


アプリ


その他のGoogleサービス


Google Cloud Platform のサービスステータス オン 確認

3. G Suite管理画面でアプリのアクセス制御に1.で委任したAPIクライアントに必要なAPIのスコープを設定


セキュリティ


アプリのアクセス制御


ドメイン全体の委任を管理


新しく追加

既存 label , filter の保存(バックアップ)を terraformer

ここまで設定ができていれば、既存 label・filter の保存(バックアップ) はコマンド一発です。

❯ terraformer import gmailfilter -r=filter,label

コマンド実行前に行ったのは以下環境変数の設定のみです。(インストールは サンプルコードの README に記載した事項が必要 )

  • GOOGLE_CREDENTIALS
  • IMPERSONATED_USER_EMAIL

今回の自分のケースでは、以下のような .envrc ファイルを用意して、 direnv で局所的に作業ディレクトリだけの環境変数を設定しました。

.envrc

export GOOGLE_CREDENTIALS=/path/to/domain-wide-deligated_service-account_credentials.json
export IMPERSONATED_USER_EMAIL="xxx@xxx.xxx"

terraformer 実行時の console log (一部)

~/ghq/github.com/sogaoh/TerraformPractice/Community/GmailFilter master*
❯ direnv allow
direnv: loading ~/ghq/github.com/sogaoh/TerraformPractice/Community/GmailFilter/.envrc
direnv: export +IMPERSONATED_USER_EMAIL ~GOOGLE_CREDENTIALS

~/ghq/github.com/sogaoh/TerraformPractice/Community/GmailFilter master*
❯ terraformer import gmailfilter -r=filter,label
2020/09/12 19:59:07 gmailfilter importing... filter
2020/09/12 19:59:09 Refreshing state... gmailfilter_filter.tfer--ANe1BmgPoKSrujLtHzJbg3eOLZCso7JO1UzAVALXRR6n6NY4qI2o0bN5LUnHKsMtaoHBjbXJsA
・・・
2020/09/12 19:59:10 Refreshing state... gmailfilter_filter.tfer--ANe1BmiNYMH21_Y4OxnjFxCJEwOZkR7Dwd9kT58bmnPJITNFajsEPi8FcMtVWiQSOuPMOG6cbQ
・・・
2020/09/12 19:59:10 gmailfilter importing... label
・・・
2020/09/12 19:59:17 Refreshing state... gmailfilter_label.tfer--_Work
・・・
2020/09/12 19:59:17 gmailfilter Connecting....
2020/09/12 19:59:17 gmailfilter save filter
2020/09/12 19:59:17 gmailfilter save tfstate for filter
2020/09/12 19:59:17 gmailfilter save label
2020/09/12 19:59:17 gmailfilter save tfstate for label

~/ghq/github.com/sogaoh/TerraformPractice/Community/GmailFilter master* 11s

作業ディレクトリの generated ディレクトリに設定が保存されていればOKです。
万が一、設定がふっとんでしまっても、これらを元手に terraform apply すれば label, filter がすっかり元に戻るはずです。

新しい label , filter の設定を terraform-provider-gmailfilter

約4時間の格闘の末に確立できた .tf ファイルは以下のような4つです [3]

variables.tf

variable "credentials" {
  type = string
  default = ""
}

variable "user_email" {}
provider.tf

provider gmailfilter {
  credentials             = var.credentials
  impersonated_user_email = var.user_email
}
label.tf

resource "gmailfilter_label" "label_techbookfest9" {
  background_color        = "#c2c2c2"
  label_list_visibility   = "labelShow"
  message_list_visibility = "show"
  name                    = "2020/09/techbookfest9"
  text_color              = "#ffffff"
}
filter.tf

resource gmailfilter_filter "bought_techbookfest9" {
  criteria {
    from            = "info@techbookfest.org"
    query           = "[技術書典] 書籍のご購入ありがとうございます"
  }
  action {
    add_label_ids    = [
      gmailfilter_label.label_techbookfest9.id,
    ]
    remove_label_ids = [
    ]
  }
}

これを terraform init -> terraform plan -> terraform apply と実行していくと、
Gmail API に抵触する点がなければ、指定した label, filter が作られます。

terraform apply 時の console log (少し加工してあります)

~/ghq/github.com/sogaoh/TerraformPractice/Community/GmailFilter master*
❯ terraform apply
gmailfilter_label.label_techbookfest9: Refreshing state... [id=Label_1]
gmailfilter_filter.bought_techbookfest9: Refreshing state... [id=ANe1BmjSrnJdB29oqZz6c70GonyV86RhG2DnYw]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # gmailfilter_filter.bought_techbookfest9 will be created
  + resource "gmailfilter_filter" "bought_techbookfest9" {
      + id = (known after apply)

      + action {
          + add_label_ids    = (known after apply)
          + remove_label_ids = []
        }

      + criteria {
          + from  = "info@techbookfest.org"
          + query = "[技術書典] 書籍のご購入ありがとうございます"
        }
    }

  # gmailfilter_label.label_techbookfest9 will be created
  + resource "gmailfilter_label" "label_techbookfest9" {
      + background_color        = "#c2c2c2"
      + id                      = (known after apply)
      + label_list_visibility   = "labelShow"
      + message_list_visibility = "show"
      + messages_total          = (known after apply)
      + messages_unread         = (known after apply)
      + name                    = "2020/09/techbookfest9"
      + text_color              = "#ffffff"
      + threads_total           = (known after apply)
      + threads_unread          = (known after apply)
      + type                    = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

gmailfilter_label.label_techbookfest9: Creating...
gmailfilter_label.label_techbookfest9: Creation complete after 2s [id=Label_2]
gmailfilter_filter.bought_techbookfest9: Creating...
gmailfilter_filter.bought_techbookfest9: Creation complete after 2s [id=ANe1BmisWaXJ2Aapql6GU1eDOEaOmFHMuT0bHA]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

~/ghq/github.com/sogaoh/TerraformPractice/Community/GmailFilter master*

Gmail の設定画面を一度リロードして、ラベルとフィルタが意図通り作成されていればOKです。
そして、期待通りにフィルタが作用することを可能であれば試すと良いでしょう。

それでは、素敵な Gmail filter 管理を。

質問等は Twitter で↓へのリプライ この記事へ Discussion コメントでいただければ、と思っています。
https://twitter.com/sogaoh/status/1305074553593327617


Terraform 0.13 以上で利用する場合の調整事項

(2021/5/9追記)

https://github.com/sogaoh/TerraformPractice/tree/master/Community/GmailFilter#setup-terraform-provider-gmailfilter の README に加筆したのですが、以下2点の調整が必要でした。[4]

  • バイナリの置き場所を仕様に従った状態にする
  • terraform init 時に -plugin-dir を指定する

Footnotes

脚注
  1. 最高すぎる!Gmail のフィルタ設定をデプロイできる CLI「gmailfilters」 - kakakakakku blog で、ちゃんとバックアップとっておいてください、と言及されています。 ↩︎

  2. 苦戦中の相談の後に terraformer で採取に成功したのは、direnv での GOOGLE_APPLICATION_CREDENTIALS 環境変数の上書きが局所的に効いた、からであろうと考えています。 ↩︎

  3. https://github.com/sogaoh/TerraformPractice/tree/master/Community/GmailFilter ↩︎

  4. https://www.terraform.io/docs/cli/config/config-file.html#provider-installation https://www.terraform.io/upgrade-guides/0-13.html#in-house-providers https://www.terraform.io/upgrade-guides/0-14.html#in-house-providers-and-internal-mirrors ↩︎

Discussion