かゆいところに手が届く、Terraformの書き方 (configuration_aliasesの使い方)

こんにちは。TIGの伊藤太斉です。
この記事は[フューチャー Advent Calendar 2023](https://qiita.com/advent-calendar/2023/future)の3日目の記事です。
さて、今年の私自身のIaC事情を振り返ってみると、だいたい半年くらいはAnsibleのみを主として読み解いたり、書いたりしていたので、Terraformerとして何かする、ということが結構抜け落ちていた1年でした。来年もTerraformやっていきたい所存です。
そんなブランクがある中でも4Qからはまた触り始めており、今は現在のプロジェクトの将来を見据えたTerraformのリファクタリングも自分の1つのバリューとしているわけですが、今回はそんななかで出てきたネタです。

## はじめに結論
`configuration_aliases`を使うことでモジュールを利用しているときも柔軟にプロバイダーを利用できる。

# 本題

今回はAWSを例として書いていきます。

## マルチプロバイダーな使い方

Terraformでは、例えばAWSのプロバイダーを利用する時、最低限であれば以下のような書き方で済みます。

```tf
provider "aws" {
  region = "ap-northeast-1"
}
```

上記のように書くことでTerraformでどのクラウドサービスに対して、どのリージョンに対してリクエストを投げるか仕分けています。ここでリージョンを書かなくても、各リソースで定義することはできますが、毎回書くのは冗長かつ、ヒューマンエラーを招くことも考えると、あまり現実的ではないでしょう。
次に、投げるリージョンが2つ以上になる時は、上記のブロックを2つ以上設定し、２つ目以降には`alias`を指定して、呼び出すときにはここで指定した値を利用します。東京リージョン(ap-northeast-1)とバージニアリージョン(us-east-1)を利用する時の例で言うと以下の形です。

```tf
provider "aws" {
  region = "ap-northeast-1"
}

provider "aws" {
  alias  = "ue1"
  region = "us-east-1"
}

# 実際にリソースに定義する場合
resource "aws_hogehoge" "sample_resource" {
  provider = aws.ue1
  ...
}
```

さらに、複数の環境を管理している時は、環境ごとシークレット分けるケースもあります。シークレットは`aws configure`コマンドで設定し、このときに`--profile`の引数にプロファイル名を設定します。設定したシークレットをTerraformで利用する場合、以下のように`profile`に指定して書くことができます。

```tf
provider "aws" {
  profile = "profile-name"
  alias   = "ue1"
  region  = "us-east-1"
}
```

さて、この3つがプロバイダーを利用するときによく使うパラメータになりますが。モジュールで利用するときに、困ったことが私にありました。うまくプロファイル(権限)が使えない、と。
実際にどう埋めようかとしていた話ですが、モジュール定義側にて`provider`に渡すパラメータをvariableにて変数化して、プロファイル名を渡す形式を取っていました。

```tf
# モジュール定義側
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  profile = var.profile
  alias   = "ue1"
  region  = "us-east-1"
}

variable "profile" {
  type = string
}

# モジュール呼び出し側
module "test" {
  source  = "some_dir"
  profile = "profile-name"
}
```

上記の書き方だと、権限が当たっていないエラーにかかりました。実際に試してはいないですが、モジュール定義側にプロファイル名をハードコードしたらできたかもしれません。ただ、モジュールはTerraformの再利用性を高めるために使われる重要な機能でもあり、プロファイルは環境ごと用意する前提があったので、別の方法を考えていました。
ひとまず、これまでの変数定義をまとめると、以下のようになります。

|  | モジュール定義側 | 呼び出し側 |
|--|--------------|----------|
| region | ◯ | ◯ |
| alias | ◯ | ◯ |
| prifile | 変数では定義できない | ◯ |

### そこで、`configuration_aliases`

プロバイダー周りを中心に調べているときに[`configuration_aliases`](https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations)に出会いました。公式の文を引用します。

> To declare a configuration alias within a module in order to receive an alternate provider configuration from the parent module, add the `configuration_aliases` argument to that provider's `required_providers` entry. The following example declares both the mycloud and `mycloud.alternate` provider configuration names within the containing module:

モジュール内部で`configuration_aliases`にエイリアスを定義しておくことで、代替する（エイリアスに載せる）ものを入れ込めるパラメータだそうです。
実際に先ほど書いたソースを直してみることにします。

```tf
# モジュール定義側
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
      configuration_aliases = [ aws.ue1 ]
    }
  }
}

# モジュール呼び出し側
module "test" {
  source = "some_dir"

  providers = {
    aws.ue1 = aws.ue1
  }
}

provider "aws" {
  profile = "profile-name"
  alias   = "ue1"
  region  = "us-east-1"
}
```

これで、プロファイルとリージョンを合わせて設定することができるようになりました。そのため、モジュール定義側で

- `provider`を設定していないリソース→ap-northeast-1
- `provider`を設定しているリソース→us-east-1

と使い分けができるようになりました。

### 余談) `providers`ブロックを用いた別の使い方
上記では1つのモジュールに対して複数のリージョンを含めたいケースで利用しましたが、`aws.ue1`に対して利用するプロバイダーの情報を渡すのではなく、`aws`自体(デフォルトで使うプロバイダー)に設定を入れることができます。
何が良いかというと、モジュールのポータビリティがさらに向上するという点です。例えば以下のような書き方ができます。([参考](https://developer.hashicorp.com/terraform/language/meta-arguments/module-providers))

```tf
# モジュール呼び出し側
## エイリアスをはっていないプロバイダーを利用する
module "test_ane1" {
  source = "some_dir"
}

## エイリアスをはったプロバイダーを利用する
module "test_ue1" {
  source = "some_dir"

  providers = {
    aws = aws.ue1
  }
}

provider "aws" {
  region  = "ap-northeast-1"
}

provider "aws" {
  alias   = "ue1"
  region  = "us-east-1"
}
```

上記で、呼び出すモジュールは同一ながらも、プロバイダーの情報を切り替えることで、別リージョンへ同一のモジュールを当てることができます。モジュールの中身にはある程度の考慮(命名のユニークさなど)はあるにせよ、TerraformもといIaCのメリットを享受できる記法だと思いました。

## どんなときに使うか
使い方を知ったうえで、どんなところに使い所があるか考えましょう。
1つのモジュールで複数リージョン利用したい時はどんな時でしょうか？いくつか例を考えていきます。

### バックアップを他リージョンで展開する場合

クラウド基盤とは言えど、実態として動いているのは物理サーバです。ここも含めてリージョン障害が起きたとき、サービス断になることは好ましくありません。そこで、クラウドでは地理的に明確に離れたリージョンへの展開を容易にすることで、DRを考えやすくしてくれます。
AWSではAWS Backupがマネージドで各種バックアップを取ってくれるサービスですが、これをマルチリージョンで展開するときにモジュール1つで起き上がってくれたらすごく便利です。

### フロントの構成
フロントの構成では、CloudFrontを最前面に配置した上でバックエンドに各種LBを下げるパターンがよく使う方法でしょう。

## まとめ
今回は自分自身すごく学びになった`configuration_aliases`だけで記事を書いてみました。
無事痒いところに手が届いたのでぐっすり寝れる日々で、また良い年末を迎えられそうです。
Terraformはまだまだ便利になる機能がたくさん出てくるので、楽しみですね！

4日目は澁川さんの記事です。お楽しみにしてください！