【Terragrunt】ステージング環境が足りない？人数分用意すればいいじゃない

## 概要
チーム開発を行なっている際、メンバーごとにAWS上で環境を用意したい時ありますよね。
環境なんていくつあっても困りませんからね〜。
![みるく-milk.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759541/f2e44f49-3d8b-2a60-9cb0-d289dab11bdf.gif)

今回！Terragrunt + GitHub Actionsでうまく複数メンバー分環境を立てるためのインフラを作成したので、それを説明しようと思います。

ご意見、アドバイスお待ちしています٩( ᐛ )و

## 環境
```bash
$ terragrunt -v
terragrunt version v0.46.2

$ terraform -v
Terraform v1.3.7
```

## 手法
### 技術選定
- Terraform
- Terragrunt

[Terragrunt](https://terragrunt.gruntwork.io/)はTerraformのラッパーです。
今回、採用した理由としては
1. `tfstate` 間の依存関係定義が容易
1. `backend`ブロック中で環境変数にアクセスができる

の2点となります。

#### `tfstate` 間の依存関係定義が容易
環境を分けるとは言ったものの、完全に独立なリソースを作るわけではありません。
VPCやALB等、共通で参照するものもいくつかあります。
新しい環境はこれらの値(idやARN)に依存するわけですが、Terraformで`tfstate` 間で値を受け渡すには手間がかかります。
具体的には`tfstate1`で`output`した値を`tfstate2`の`tfvars`にハードコード or 実行時に明示的に値を受け渡す必要があります。

一方Terragruntは[dependency block](https://terragrunt.gruntwork.io/docs/reference/config-blocks-and-attributes/#dependency)を使用することで、`tfstate` 間で簡単に値を共有することが可能です。
```hcl
dependency "common" {
  config_path = "../common_resources"
}
```

メンバーの環境の`tfstate`はそれぞれ共通の依存関係があるため、この記述がすごくマッチしました。

#### `backend`ブロック中で環境変数にアクセスができる

GitHub ActionsでのTerragrunt実行を想定しているため、`tfstate`ファイルはlocalに保存しておくわけにはいきません。
また、ユーザー毎にtfstateを管理するキーも分ける必要があります。

Terraformでは、backendブロック内で直接環境変数を参照することができません。代わりに、-backend-configオプションを使用してterraform initの一部として引数を渡す必要があります。これは、各環境での手動の操作が必要となります。

```tf
  backend "s3" {
     bucket = "dummy-terraform"
     key    = "staging/terraform.tfstate"
     region = "ap-northeast-1"
  }
```

```bash
$ terraform init \
 -backend-config="bucket=real-terraform"\
 -backend-config="key=userA/terraform.tfstate"\
 -backend-config="region=us-east-1"
```

一方で、Terragruntでは、backendブロック内で直接環境変数を参照することが可能です。

```hcl
remote_state {
  backend = "s3"
  config = {
    bucket         = "${get_env("STATE_BUCKET", 'hogehoge-terraform')}"
    key            = "${get_env("ENVIRONMENT", 'staging')}/terraform.tfstate"
    region         = "ap-northeast-1"
  }
}
```

```bash
$ export STATE_BUCKET=real-terraform
$ export ENVIRONMENT=userA
$ terragrunt init 
```

GitHub Actionsでシークレットから取得した環境変数として設定すれば、Terragruntがそれを読み取ってプロビジョニングしてくれます。

### アーキテクチャ

とりあえず完成形です！

https://github.com/iisyos/aws_staging_demo

`/common_resources`
VPC, ALB, Route53, ECR等の共通で使用するある種の「箱」のようなリソースを作成します。
![名称未設定ファイル.drawio (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759541/d8bb8771-1e94-375b-53ed-5cacb921c723.png)


`/user_resources`
（localからでも可能ですが、）GitHub Actions上でworkflow_dispatchをトリガーとしこのリソースがプロビジョニングされます。
実行したGitHub Userの名前をキーとして以下のようなリソースを作成します。

![名称未設定ファイル.drawio (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759541/bcb6a9f4-abc6-6c89-acf0-f87cad8b129f.png)
ALBのリスナールールでホストヘッダーを見て、各ユーザー固有のFargateにリクエストを捌くようになっています。

初回デプロイ時は
1. Route53にサブドメインの登録（`<your github username>.<your domain>`）
1. ALBリスナールールを追加
1. ECSクラスタ、サービスの作成

が行われ、2回目以降はGitHubActionsでECRにpushされたImageを元にタスク定義を書き換えサービスを更新します。

## 結論
いかがでしょうか？
ユーザー毎に環境をGitHub Actionsで作成するためのアーキテクチャの一案です。
もっといい方法があるかもしれないですが、今の私ではこれが最適解でした😫
よければ参考にしてみて下さいっ💡


## 今後
すごい細かいですけど、Fargateは稼働時間でも利用料金が発生するため、ことステージング環境は夜間（業務時間外）は停止させたいですよね。
ECS on EC2であればASGで特定の時間コンテナインスタンスの数を0にすれば実現可能ですが、Fargateの場合どうすればいいのでしょうか・・・？

え…？夜間は業務時間外ではないですって？
す、すいませんでした🙇