続 カッコの付け方

AWSを始めとしたクラウドコンピューティング全般と、唯一神emacsにおける()の付け方についてだらだら書きます

terraform で gcp iam管理に入門してみる

terraform で gcpのIAMを管理してみたときのメモ

f:id:iga-ninja:20210214015531p:plain
terraform with gcp

GCP/AWS 用語対応表

AWSからterraformを触った人が多数派と思うので、用語の意味違いが結構な落とし穴です。なので整理しときます。

AWS GCP memo
User Google アカウント GCPより広い世界
Group Google Group 同上
Role ServiceAccount 厳密には異なるがだいたい
Policy Role つらい、嫌がらせか?
PolicyAttachment IAM_member terraform上
該当なし? Policy terraform上?

まずUserとGroupについて、GCPのIAMは認証機能は持たず、基本的に認可だけやります。認証は、GCPに閉じた世界ではなく、Gooleアカウント/Groupで行っています。Gmailのフリーアカウントや、Google Workspaceのメアドがユーザーに該当します。

次にAWSのRoleに対応するものは、ServiceAccount(SA)というものです。GCPにもRoleがありますが、これはAWSのPolicyに相当します。まあ、英語の意味はあってますが、後発なので敢えて変えたんでしょうね。。AWSのRoleは主に人間以外のサーバやSAMLで使うものですが、GCPで同じことやる場合、人間以外のためにメールアドレス(メールは受信できないが)を払い出します、それがServiceAccountと見ていい。

AWSのPolicy=GCPのRoleは認可内容のセット・塊です。GCSの全権限を持つとか、GCE何でもできる権限とかを指します。AWSのIAMのようにどのサービスの何ができるということは個別で指定できます、が最初のうちは、プリセットのRoleが用意されているのでこれでほぼことが足りるはずです。プリセットのRole=AWSのAWSManagedのPolicyです。GCPのRoleは自作もできます。

注意喚起です、google_project_iam_policy というAWS触った人ならすぐにいじりたくなるものがありますが、基本いじっちゃだめです。本番でやると即死あり。

Terraform x GCP で、IAM権限を全削除してしまった - Qiita

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_policy

最後にPolicyAttachment、これは厳密にはAWSではなく、terraformの話ですが、どのAWS User/RoleがどのPolicyと紐付いているかをつなぐものです。これがGCPでは google_project_iam_member に該当します。

terraformでの認可の振り方

2つやり方があります。google_project_iam_bindinggoogle_project_iam_member です。結論を先に書きますが、AWSのterraform猛者であれば google_project_iam_member 一択です。 google_project_iam_bindingをどうしても使いたい場合であっても、人間ユーザーが使うものだけに適応するのが望ましく、ServiceAccountで使うのは悪手です。その説明を書く。

google_project_iam_binding

Roleを主体にして、それを使う人やServiceAccountを列挙します。だから members = [...] で指定します。AWSでterraformそこそこ昔からやってた人ならすぐにわかると思いますが、これを使ってる=死亡フラグです。

google_project_iam_member

Member=使う人やServiceAccountとRoleを1対1で紐づけます。aws provider (terraform) の IamPolicyAttachmentです。1対1紐づけの重要さを知っている人はこれから先は読まなくていいです。お疲れ様でした。

1対1紐づけでないと何がまずいのか?

terraformの習熟度次第では「そんなの俺には関係ないね」となるかもしれないですが、知ってて損はないと思うのでまあ、読んでください。 私が認識するterraformの良さの一つに、コードで管理する対象(バケツやGCE-VM GCPのリソースのこと)を選べる、を挙げます。例えば同じGCPプロジェクトでも、このチームはVPCだけ、このチームはGCSだけとかを、それぞれ異なるtfstate(実態としてはディレクトリとなる)で管理できる点です。
「インフラなので、1枚岩で1プロジェクト1ディレクトリで全部のリソースを管理しないとダメ」という管理ポリシーももちろん間違えではないですが、ユーザーが選べるという点が優れてる、嫌なら分割しなきゃいい。
で、google_project_iam_bindingを使ってしまうと、この管理対象を実質的に分割することができなくなってしまいます。(できなくはない、が運用が大変)まあ、ディレクトリ分割しなくてもtfファイルはかなり不格好になります。
もう一つは terraform workspaceを使っている場合も(知ってると思うけど)危ないです。回避方法は google_project_iam_member を使えです。

実証1 (神レビュアがいれば避けられるかも?)

四の五の言わずにさくっとtffileを書きます。まずは分割しないけどapply後に延々とdiffが出るパターンです

resource "google_service_account" "sa-one" {
  account_id = "sa-one"
  display_name = "sa-one"
  description = "テスト用1"
}

resource "google_project_iam_binding" "sa-one" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-one.email}"]
}

### 1ヶ月後に誰かがsa2を追加
resource "google_service_account" "sa-two" {
  account_id = "sa-two"
  display_name = "sa-two"
  description = "テスト用2"
}

resource "google_project_iam_binding" "sa-two" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-two.email}"]
}

Roleは間違って流してもいいように、普段使わんであろうやつを指してます。sa-one にこのロールを当ててその後、sa-twoにもそのロールを当てようとしてます。これは plan/applyともに通ります。しかしapply後に planを叩くと差分が出ます。というか何回applyしようが永遠に出ます。なぜか?
google_project_iam_bindingの書き方が間違っています。members = [] で2つのSAを列挙しないとだめです。正しい使い方は、Roleを主体で考えるので、それを使っている人・SAを全部列挙しろということになります。これ、とても恐ろしいことが起こってます、仮に人アカウントのRole割当は一切terraform管轄にしていない場合に、上記のRoleがProject/Editorだったりした場合、Applyしたら人アカウントからEditor権限全部剥奪されます。そのため、例ではどうでもいいRoleを採用してます。

terraformコード管理してます、Reviewを通さないとApplyできないという運用をしてます、sa-oneを参考にしてsa-twoをベースコピペでPR出しました、Planもグリーンです、さあレビューしてください!私は急いでます!、これでレビューでNGだせますか? シンプルに google_project_iam_bindinggrepして引っかかったら「これあかんで」ぐらいの運用しかできないんじゃないかな。

今回は、1ディレクトリに2つのSAを両方書きましたが、ディレクトリ分割し、tfstate分離してたら悲惨です。他のディレクトリのSAで、Roleの参照があるかないかを調べて忖度し続けなければならない。

実証2 (workspaceパターン)

workspaceでも試してみた、こちらはworkspace切り替え後のapplyで差分が出るので気づけるチャンスはある

resource "google_service_account" "sa-one" {
  account_id = "sa-one-${terraform.workspace}"
  display_name = "sa-one"
  description = "テスト用1"
}

resource "google_project_iam_binding" "sa-one" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-one.email}"]
}

defaultでapplyしたあと、別workspace(production)でapplyすると、差分が出ます。

Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # google_project_iam_binding.sa-one will be updated in-place
  ~ resource "google_project_iam_binding" "sa-one" {
        id      = "pj/roles/storage.hmacKeyAdmin"
      ~ members = [
          - "serviceAccount:sa-one-default@pj.iam.gserviceaccount.com",
          + "serviceAccount:sa-one-production@pj.gserviceaccount.com",
        ]
        # (3 unchanged attributes hidden)
    }

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

差分が出る原理は同じですが、ちゃんと想定外のが出ている分だけましか。。

まとめ

  • GCPでも人・SAとRoleの紐づけは1対1 google_project_iam_member を推奨します
  • google_project_iam_binding をどうしても使いたい場合は、(ちょっと苦しいと思うが) SAに割り当てるRoleは全部スタムで、SAにしか割り当てない運用とする

エンジニアなので、コード短く書きたい、DRYに書きたい、変数でループしたい、よくわかります。しかしIAM設定はミスったときのダメージが大きすぎるので、リスクおよびその回避策とを天秤にかけて選択してください。