🌊

サービスアカウント の account_id に UUID を指定したい

2024/05/22に公開1

方針

諸事情でGCPのサービスアカウントのaccount_idにUUIDを指定したかったんですが、意外と苦労したので備忘録的に残します。

どうもサービスアカウントのaccount_idの命名規則は次のようになっているようです。

  • 小文字の英数字とダッシュ(-)で構成される
  • 先頭は英字のみ
  • 6文字以上30文字以内

UUIDは36桁で - を除いても32文字あり、どう頑張ってもそのままでは使用できませんでした。仕方がないので、どうにか上記制約を満たす中での可逆圧縮を考えました。

よくあるのはbase64エンコードかと思いますが、小文字しか使えない上に、記号も - しか使用できないので厳しいなと(ひょっとしたらいい感じに符号化することでいけるのかも?)

そこで今回はUUIDを32進数表記することで対応しました。UUIDは16進数32桁=128bitで構成されているので、これを32進数表記することで26文字まで圧縮することができます。また、32進数であれば 0-9a-v までの英数字で表現できるので、冒頭の制約下で表現できます。これでUUIDを情報を失うことなく26文字まで圧縮することができるようになりました。最後に先頭文字は英字限定という条件を満たすために適当なprefixをつければ完成です。

Terraformによる実装

Terraformのmoduleで表現すると次のようになります。

# main.tf
locals {
  # UUIDをバイナリに変換
  bin_uuid = join("", [for c in split("", replace(var.uuid, "-", "")) : format("%04b", parseint(c, 16))])

  # 先頭を0でパディングして、長さを130ビットに調整
  padded_bin_uuid = format("%02s%s", "0", local.bin_uuid)

  # 32進数に変換
  base32_chars    = "0123456789abcdefghijklmnopqrstuv"
  compressed_uuid = join("", [for bin in regexall(".{5}", local.padded_bin_uuid) : substr(local.base32_chars, parseint(bin, 2), 1)])
}

# variables.tf
variable "uuid" {
  type        = string
  description = "UUID"
}

# outputs.tf
output "result" {
  value = local.compressed_uuid
}

知らなかったんですが、Terraformもテスト書けたんですね。

variables {
  uuid = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
}

run "compress_uuid" {
  command = plan

  assert {
    condition     = local.compressed_uuid == "7o3l7qsvfc278aep80k34hsqvm"
    error_message = "invalid compressed_uuid"
  }
}

Discussion

かせがおかせがお

先頭を0パディングじゃなくて1パディングにすれば、32進数に変換したときに確定で先頭が英字になるので、変なprefixを付ける必要もないですね