TerraformでTROCCOのチームもいい感じに管理してみる

## はじめに
前回、TROCCOのユーザーをTerraformでいい感じに管理できるようにしました

https://qiita.com/khpkymgc/private/011880d16186129560b7

TROCCOでは**チーム機能**も提供しているため、こちらも合わせてTerraformで管理できるように考えてみました。

## チーム機能とは？
TROCCOのチーム機能は、チームとリソースグループという2つの概念によって、TROCCO上に作成された各種設定に対する操作権限を管理します。

https://documents.trocco.io/docs/about-team

## やりたいこと
- [var.departments](https://qiita.com/khpkymgc/private/011880d16186129560b7#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E3%81%A4%E3%81%8F%E3%82%8B)で指定した部署を利用してチームに所属するユーザーを決定する
- TROCCO上にユーザーが追加された際はチームにも自動で追加される
- チームの管理者、メンバーといった権限も自動で設定される

## やりかたを考える
Terraform Provider for TROCCO のサンプルを見てみます。
ユーザーはメールアドレスではなく、user_idで指定する必要があるようです。

https://registry.terraform.io/providers/trocco-io/trocco/latest/docs/resources/team

```
resource "trocco_team" "example" {
  name        = "team name"
  description = "description"
  members = [
    {
      user_id = 1
      role    = "team_admin"
    },
    {
      user_id = 2
      role    = "team_member"
    }
  ]
}
```



弊社Dさんの公開しているサンプルリポジトリも参考にしていきます。

https://github.com/SoySoySoyB/terraform-trocco-sample

下記で指定されているようですが…？？？？
Terraform初心者なのでなぜこの設定で動くのかわかりませんでした…

```_trocco.tf
locals { ～～～～～
user_email_ids = { for email, user in trocco_user.users : email => user.id }
}
```
```_trocco.tf
resource "trocco_team" "developers" {
  name        = "TROCCO開発者チーム"
  description = "TROCCO開発者のチーム"
  members = concat([
    for email in local.admins : {
      user_id = local.user_email_ids[email]
      role    = "team_admin"
    }
    ], [
    for email in local.developers : {
      user_id = local.user_email_ids[email]
      role    = "team_member"
    }
  ])
}
```

他に簡単そうなやり方でできないか考えてみます。
[ユーザー管理](https://qiita.com/khpkymgc/private/011880d16186129560b7#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)と同じように、**どこかに書かれている情報を利用できたら簡単じゃないかな？？** と思いました。

terraform.tfstate を覗くと、idが記述されていました！この情報を取得して利用できないでしょうか。
```.tfstate
        {
          "index_key": "0",
          "schema_version": 0,
          "attributes": {
            "can_use_audit_log": true,
            "email": "user01@example.com",
            "id": 1, # ここの値を取得したい
            "is_restricted_connection_modify": false,
            "password": "*********",
            "role": "admin"
          },
```

## .tfstateファイルからidを取得したい
data句を使うと、他ファイルから値を取得できるようです。下記のように設定しました。

::: note warn
この設定は失敗したのでざっくり概要だけ記載します！
:::

```
data "terraform_remote_state" "existing" {
  backend = "local" # 必要に応じて変更
  config = {
    path = "./terraform.tfstate" # `terraform.state`のパス
  }
}
```

リソース **"trocco_team"** も合わせて設定し、実行してみます。
結果はエラーでした…。

```
Error: Failed to read state file
```

内容を読むと、ファイルがロックされていることが原因のようです。
ローカル環境での実行が原因の場合があるようです。
今回はクラウド環境にバックエンドを移せないので、他のやり方を模索していきます。

## 他のやり方を考える
サンプルリポジトリの下記部分で何をしているのか解明するのが早そうです。
Dさんにきいたところ、ユーザー作成のリソース（**trocco_user.users**）の実行結果から値を取ってきて **email** と **id** を紐づけているということでした。
```
user_email_ids = { for email, user in trocco_user.users : email => user.id }
```

また、**[email]** にフルアドレスを挿入すると紐づいている **id** が取得できるようです。
```
user_id = local.user_email_ids[email]
```

## 自環境に合わせて設定する
自環境では、下記の設定にしているため前述のコードそのままでは設定が足りず、対応方法が変わりました。

**設定状況**
[部署とユーザーを設定しているファイル (var.departments)](https://qiita.com/khpkymgc/private/011880d16186129560b7#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E3%81%A4%E3%81%8F%E3%82%8B)でユーザーを **メールアドレスのローカルパートのみ** で指定している。そのため、フルアドレスとidの紐づけだけでは部署が特定できない。


**対応方法**
- メールアドレス（フルアドレス）とidを紐づける
- メールアドレス（フルアドレス）とメールアドレスのローカルパートを紐づける

この設定により、var.departmentsに設定している **メールアドレスのローカルパート** と **id** も間接的に紐づけられ **id** から **部署** が特定できるようになります。

下記をlocalsとして記述します。
```.tf
locals{
# フルアドレスとidの紐づけ
user_email_ids = { for user in trocco_user.users : user.email => user.id }
# メールのローカルパートとフルアドレスの紐づけ
local_part_to_email = { for user in trocco_user.users : split("@", user.email)[0] => user.email }
}
```
前準備は終わったので、リソース **"trocco_team"** を実行する準備に入っていきます！

## "trocco_team" を実行する準備をする
例としてチーム **営業部** を作成していきます。
今回もChatGPTと話し合いをして、下記のようになりました。

```.tf
resource "trocco_team" "sales_teams" {
  name        = "営業部"
  description = "営業部メンバーのチーム"

  members = flatten([
    for department, members in var.departments : [
      for member in members : {
        user_id = local.user_email_ids[local.local_part_to_email[member]],
        role    = lower(department) == "sales_admin" ? "team_admin" : "team_member"
      }
      if lower(department) == "sales" || lower(department) == "sales_admin"
    ]
  ])
}
```

forループの部分は前回の使い回しにしました！

https://qiita.com/khpkymgc/private/011880d16186129560b7#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B

その他の部分については下記の処理になっています。

```
user_id = local.user_email_ids[local.local_part_to_email[member]],
```
**user_id** を指定します。下記のような処理が行われます。
① var.departments に記載されている **member** を挿入する
② locals の設定で **member** に紐づけられている **フルアドレス** を呼び出す
② locals の設定で **フルアドレス** に紐づけられている **id** を呼び出す
<br>

```
role    = lower(department) == "sales_admin" ? "team_admin" : "team_member"
```
ロールの振り分けを定義しています。
var.departments で **sales_admin** に定義しているユーザは **team_admin** にしてくれます。
それ以外のユーザは **team_member** になります。

:::note warn
前回の記事内では var.departments に sales_admin は定義していなかったため追加で設定しています
:::

```
if lower(department) == "sales" || lower(department) == "sales_admin"
```
チームに所属させる対象ユーザーを絞り込んでいます。
[var.departments](https://qiita.com/khpkymgc/private/011880d16186129560b7#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E3%81%A4%E3%81%8F%E3%82%8B) で **sales** もしくは **sales_admin** に定義しているユーザのみを対象に実施するようにしています。正規表現でも対応できるのですが、部署数が増えた場合は負荷がかかってしまうようなので今回は手書きのOR条件を使いました。

## 実行する
まずは、```terraform plan```で反映内容を確認します
var.departmentsでは、salesに2人、sales_adminに1人設定しているので想定した通りに動作していそうです。

```
>terraform plan 

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # trocco_team.sales_teams will be created
  + resource "trocco_team" "sales_teams" {
      + description = "営業部メンバーのチーム"
      + id          = (known after apply)
      + members     = [
          + {
              + role    = "team_member"
              + user_id = 10406
            },
          + {
              + role    = "team_member"
              + user_id = 10410
            },
          + {
              + role    = "team_admin"
              + user_id = 10405
            },
        ]
      + name        = "営業部"
    }

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

つづいて、```terraform apply```を実行します。正常に完了したようです！

```
>terraform apply    

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # trocco_team.sales_teams will be created
  + resource "trocco_team" "sales_teams" {
      + description = "営業部メンバーのチーム"
      + id          = (known after apply)
      + members     = [
          + {
              + role    = "team_member"
              + user_id = 10406
            },
          + {
              + role    = "team_member"
              + user_id = 10410
            },
          + {
              + role    = "team_admin"
              + user_id = 10405
            },
        ]
      + name        = "営業部"
    }

Plan: 1 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

trocco_team.sales_teams: Creating...
trocco_team.sales_teams: Creation complete after 0s [name=営業部]

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

webUIからも確認しておきます。問題なさそうです！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929084/7db552aa-3e08-a98e-af26-310e30bb17c6.png)

他のチームを作成したいときは、下記を適宜変更すれば良いので簡単にできそうです

例
```
  name        = "技術部"
  description = "技術部メンバーのチーム"

～～～

        role    = lower(department) == "tech_admin" ? "team_admin" : "team_member"
      }
      if lower(department) == "tech" || lower(department) == "tech_admin"
```

お疲れ様でした！

## おわりに
Terraform初心者なので、サンプルリポジトリとChatGPTを参考に自分なりにこうしたら便利かな～と想像して行き当たりばったりで検証・勉強しています。
前回、「ドメイン書くのめんどくさいから省略できるようにした！」とウキウキだったんですが、先人とは違う設定をしたことで今回の設定でうまくいかないところも増えました。(user_idの指定がコピペで対応できなくなりました。)関連する機能がある場合は、他機能についても対応できるのか考えてから始めないとだめだな、と反省しました。。

また、今回チーム営業部を作成しましたが使用した環境にもすでに営業部があったためダブりが発生しました。うまいことチーム名で照合して既存の営業部にメンバーを追加するような動作にならないかな、と期待していたのですがそこまでいい感じにはしてくれませんでした。

恐らく、最初に環境情報をTerraformにインポートしてから管理を始めのが正解ですね…。
今更ですが、次は環境情報のインポートも試してみたいと思います。