0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

2重ループ的な処理を Terraform で実現する (Azure)

Last updated at Posted at 2022-07-29

やりたいこと

業務で Terraform を使っている中で、2重ループ的な処理を実現する必要があった。
分かってしまえば簡単だが、わりと手こずったので備忘録兼他の方の参考になればと思い書いておく。

どういう処理をしたいか

複数のリソースに対して、複数のユーザ (AzureAD で管理) のアクセス権を割り当てる、というようなケース。
具体的には、5つのストレージアカウント内のコンテナへの Blob Data Contributor ロールを2人のユーザに割り当てる、というケース。

本来は、グループを作成してそのグループに Blob Data Contributor を割り当てる方が良い。
最初に上記の形で割り当てたのでその内容で話を進める。現在はグループに割り当てる形にしている。

通常の Terraform でのループ処理 (単一ループ)

以下のような感じ。for_each を使う。st_names 内の値がストレージアカウント名として使用される。

single_loop_example.tf
local {
  st_names = ["st1", "st2", "st3", "st4", "st5"]
}

# ストレージアカウントを作成
resource "auzrerm_storage_account" "storages" {
  for_each = local.st_names

  name                     = each.value
  resource_group           = "rg-sample"
  location                 = "japaneast"
  account_tier             = "Standard"
  account_replication_type = "GRS"
}

これらのストレージアカウントは Terraform 内では、以下のようなリソースとして管理されている。
それぞれのリソースは、コードを通して一意である必要があり重複は許されない。

auzrerm_storage_account.storages["st1"]
azurerm_storage_account.storages["st2"]
azurerm_storage_account.storages["st3"]
azurerm_storage_account.storages["st4"]
azurerm_storage_account.storages["st5"]

この状態で各ストレージアカウントの Blob Data Contributor をユーザに割り当てようとする以下のような感じ (力技)。

assign_role_example.tf
local {
  st_names = ["st1", "st2", "st3", "st4", "st5"]
  users    = ["user1@example.com", "user2@example.com"]
}

# ストレージアカウントを作成
resource "auzrerm_storage_account" "storages" {
  for_each = local.st_names

  name                     = each.value
  resource_group           = "rg-sample"
  location                 = "japaneast"
  account_tier             = "Standard"
  account_replication_type = "GRS"
}

# AzureAD からユーザ情報を取得
data "azuread_users" "blob_access_users" {
  user_pricipal_names = local.users
}

# st1 の Blob Data Contributor に各ユーザを割り当てる
resource "azurerm_role_assignment" "st1" {
  for_each = data.azuread_users.blob_access_users.object_ids

  scope                = azurerm_storage_account.sotrages["st1"]
  role_definition_name = "Blob Data Contributor"
  principal_id         = each.value
}

# st2 の Blob Data Contributor に各ユーザを割り当てる
resource "azurerm_role_assignment" "st2" {
  for_each = data.azuread_users.blob_access_users.object_ids

  scope                = azurerm_storage_account.sotrages["st2"]
  role_definition_name = "Blob Data Contributor"
  principal_id         = each.value
}

# st3~5 も同様
# st1~5 をループで処理したくなる

数が少ないうちは上記のような書き方でも問題はないかもしれないが、数が多くなってくると大変になる。
その一方、可読性は高いのでそれはそれでありなのかも? :thinking:
個人的には同じようなコードは何度も書きたくないので、ループで処理するようにしたい。その方がかっこ良いし!

2重ループを実現するには

こうする。

multi_loop_example.tf
local {
  st_names = ["st1", "st2", "st3", "st4", "st5"]
  users    = ["user1@example.com", "user2@example.com"]
}

# ストレージアカウントを作成
resource "auzrerm_storage_account" "storages" {
  for_each = local.st_names

  name                     = each.value
  resource_group           = "rg-sample"
  location                 = "japaneast"
  account_tier             = "Standard"
  account_replication_type = "GRS"
}

# AzureAD からユーザ情報を取得
data "azuread_users" "blob_access_users" {
  user_pricipal_names = local.users
}

# st1~5 の Blob Data Contributor に各ユーザを割り当てる
# 2重ループ部分
resource "azurerm_role_assignment" "blob_data_contributor" {
  for_each = {
    for item in setproduct(
      [for st_name in local.st_names : st_name],
      [for user_id in data.azuread_users.object_ids]
    ) : join("-", item) => item
  }

  scope                = each.value[0]           # st_name
  role_definition_name = "Blob Data Contributor"
  principal_id         = each.value[1]            # user_id
  # each.key は st_name と user_id を '-' で join した文字列 (下記参考)
}

上記で使っている setproduct が今回のポイント。詳細は 公式ドキュメント を。要は、引数で与えられたリストの直積を作成する関数。
これによって、以下のような形の map のリストを作成して、for_each に与えている。

[
  "st1-user1@example.com" = {"st1", "user1@example.com"},
  "st1-user2@example.com" = {"st1", "user2@example.com"},
  "st2-user1@example.com" = {"st2", "user1@example.com"},
  "st2-user2@example.com" = {"st2", "user2@example.com"},
  ...
]

こうすることで、auzrerm_role_assignment の各ストレージへのロールアサインのリソースは以下のようになり、重複しなくなるため実行できる。

azurerm_role_assignment.blob_data_contributor["st1-user1@example.com"]
azurerm_role_assignment.blob_data_contributor["st1-user2@example.com"]
azurerm_role_assignment.blob_data_contributor["st2-user1@example.com"]
azurerm_role_assignment.blob_data_contributor["st2-user2@example.com"]
azurerm_role_assignment.blob_data_contributor["st3-user1@example.com"]
...

3重ループとかも実現可能なはずだが、そこまではやっていない。そこまでいくと可読性が下がりそうなので、別の方法でやった方が良いのかも?

これが最良の方法なのかは分からないので、他の方法などあればコメントいただけると嬉しいです!

以上です。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?