[Terraform+Docker(+NewRelic)]環境変数のおすすめ設定方法

# この記事を３行で

- ①Gitに載せられないセンシティブな環境変数(API KEYとか)はローカルで設定しコンテナに渡す設定にする
- ②Gitに載せられるような環境変数は `*.tfvars` ファイルに記載
- ③各環境ごとのディレクトリを作り、そのディレクトリでTerraformコマンドを叩くことで同一ソースを複数環境でデプロイしよう

# 前提

- 次の過去記事で構築した環境を前提としています

[Terraform+Docker 環境構築 - Qiita](https://qiita.com/sawa-akabee/items/ab9f9ff226a24beaa1bd)

- NewRelicをTerraformでIaC(Infrastructure as Code)化するプロジェクトを想定しています

## 環境

- macOS Big Sur v11.6.1(Intel)
- Docker for Mac v4.3.1
    - Docker v20.10.11
    - docker-compose v1.29.2

# 本題

## ディレクトリ構成

```
---- terraform
   │   └ src
   │   │   └ environment
   │   │   │   └ prod
   │   │   │   │   └ prod.tf
   │   │   │   └ dev
   │   │   │       └ dev.tf
   │   │   └ main
   │   │   │   └ main.tf
   │   │   │   └ その他もろもろ // Terraform関連ソース(docker-composeでマウントする)
   │   └ home
   │   │   └ entrypoint.sh // TerraformコンテナのENTRYPOINTに指定するシェルコマンド
   │   └ root
   │   │   └ .bashrc // bashのエイリアスなどをここに記載
   │   └ Dockerfile
   └ docker-compose.yaml
```

## ①Gitに載せられないセンシティブな環境変数(API KEYとか)はローカルで設定しコンテナに渡す設定にする

- ここでの環境変数は環境間で変更する必要はない想定で話を進めます
    - 変更の必要がある場合、この章に記載されているやり方でローカルの環境変数を変更した上で各環境のコンテナを立ち上げる必要があるかと思います
- 上でも挙げた、下記の過去記事に記載がありますが、改めて要点を記載しておきます

[Terraform+Docker 環境構築 - Qiita](https://qiita.com/sawa-akabee/items/ab9f9ff226a24beaa1bd)

### docker-compose.tml

- `environment`でローカルで設定された環境変数をコンテナ環境内に反映させる

```yaml:docker-compose.tml
version: "3.8"

services:
  terraform:
    container_name: tf
    build: ./terraform
    environment: // コンテナ内の環境変数を指定する => 導入するプロバイダーや求める環境次第
      - NEW_RELIC_API_KEY=${NEW_RELIC_API_KEY}
      - NEW_RELIC_ACCOUNT_ID=${NEW_RELIC_ACCOUNT_ID}
      - NEW_RELIC_REGION=US
    volumes:
      - ./terraform/src:/terraform/src
```

### ローカルでの環境変数設定

- CLIで設定する場合

```
$ NEW_RELIC_API_KEY=xxxx-xxxxxxxxxxxxxxxxxxxxxxx \
   && echo ${NEW_RELIC_API_KEY}

xxxx-xxxxxxxxxxxxxxxxxxxxxxx
```

- .zshenvで設定する場合



```~/.zshenv
export NEW_RELIC_API_KEY=xxxx-xxxxxxxxxxxxxxxxxxxxxxx
```

※ターミナルを開き直し、正しく環境変数が設定されていることを確認すること

```
echo ${NEW_RELIC_API_KEY}

xxxx-xxxxxxxxxxxxxxxxxxxxxxx
```

## ②Gitに載せられるような環境変数は *.tfvars ファイルに記載

- ここについては、実際のTerraform開発時の流れにのって紹介していきます

### ****terraform/home/entrypoint.sh****

(いきなり蛇足ですが、ここでterraform initしておくといい感じなので載せておきます)

```bash:entrypoint.sh
#!/bin/bash
echo -e '\n\n//===========================================';
echo -e '// START_[$ terraform init(prod)]';
echo -e '//===========================================';

cd terraform/src/environment/prod;
terraform init;
cd ../../../..

echo -e '\n\n//===========================================';
echo -e '// START_[$ terraform init(stg2)]';
echo -e '//===========================================';

cd terraform/src/environment/stg2;
terraform init;
cd ../../../..

echo -e '\n\n//===========================================';
echo -e '// SHOW_environment';
echo -e '//===========================================';

echo '$NEW_RELIC_API_KEY=' $NEW_RELIC_API_KEY;
echo '$NEW_RELIC_REGION=' $NEW_RELIC_REGION;
echo '$NEW_RELIC_ACCOUNT_ID=' $NEW_RELIC_ACCOUNT_ID;

tail -f /dev/null
```

- DockerfileのENTRYPOINTに指定しているファイルです
    - (今回の構成に合わせて書き換えてますが、仕組みなどについてはこれも過去記事を参照してください)
- やっていること
    - コンテナ起動時、各環境でterraform initコマンドを叩いている
    - ローカルで定義した環境変数をコンテナに反映している(docker-compose.ymlに記載)ので、正常に反映されているかチェックしている

### ****terraform/root/.bashrc****

```.bashrc
alias ll='ls -la --color' # これは便利なのでおまけ
alias tf='terraform' # これは便利なのでおまけ

# plan
alias plan:dev="cd /terraform/src/environment/dev && terraform plan -var-file ./dev.tfvars && cd ../../../.."
alias plan:prod="cd /terraform/src/environment/prod && terraform plan -var-file ./prod.tfvars && cd ../../../.."

# apply
alias apply:dev="cd /terraform/src/environment/dev && terraform apply -var-file ./dev.tfvars && cd ../../../.."
alias apply:prod="cd /terraform/src/environment/prod && terraform apply -var-file ./prod.tfvars && cd ../../../.."

# tmt
alias fmt="terraform fmt -recursive" # これは便利なのでおまけ
```

- やっていること
    1. 各環境のディレクトリに移動
    2. Terraformコマンド起動
        - -var-fileオプションで各環境ごとの環境変数ファイルを定義している
    3. コンテナのルートディレクトリに移動
- 各環境のディレクトリへの移動は絶対パスで指定しているのでコンテナのどこにいてもコマンドは叩ける
- 「terraform initが必要」という旨のエラーが現れた場合
    - 各環境のディレクトリに移動した状態でコマンドが中断されているはずなので、そこでinitをすればOK

### terraform/src/environment/prod & dev/*.tfvars

ex. prod

```*.tfvars
env              = "prod"
domain           = "www.example_prod.com"
```

- 上記****.****bashrc内エイリアスの-var-fileオプションにて指定されている環境変数の定義ファイル
    - これでplan, applyしたディレクトリ内の.tfファイル内でvariableとして取得し、使用することができる

### terraform/src/environment/prod & dev/*.tf

```*.tf
//-------------------------------------
// variable
//-------------------------------------
variable "env" { type = string }
variable "domain" { type = string }

//-------------------------------------
// modules
//-------------------------------------
module "main" {
  source           = "../../main"
  env              = var.env
  domain           = var.domain
}

//-------------------------------------
// terraform
//-------------------------------------
terraform {
  required_version = "~> 1.0"
  required_providers {
    newrelic = {
      source = "newrelic/newrelic"
    }
  }
}
```

- variableブロックで環境変数を取得している
- modulesブロックで環境変数を”main”としているmoduleに渡している
    - あとは”main”のmoduleから使いたいところまで環境変数をパスしていく

### (参考)以降の環境変数のリレーの仕方

terraform/src/main/modules.tf

```modules.tf
		module "urls" {
      source = "./modules/output/urls"
++    domain = var.domain
    }
```

↓

terraform/src/modules/output/urls/urls-table.tf

```urls-table.tf
      //-------------------------------------
      // variable
      //-------------------------------------
++    variable "domain" {
++      type = string
++    }
      
      //-------------------------------------
      // output
      //-------------------------------------
      output "urls" {
        value = {
--        "https://example.aaaa.com/path/to/hoge" : {
++        "https://${var.domain}:443//path/to/hoge" : {
            url_name = "ほげ"
          }
          ︙
        }
      }
      
      //-------------------------------------
      // terraform
      //-------------------------------------
      terraform {
        required_version = "~> 1.0"
        required_providers {
          newrelic = {
            source = "newrelic/newrelic"
          }
        }
      }
```

## ③各環境ごとのディレクトリを作り、そのディレクトリでTerraformコマンドを叩くことで同一ソースを複数環境でデプロイ

ここまでで

- 各環境ごとのディレクトリ構成ができており
- それぞれの環境で環境変数を定義できていて
(****terraform/src/environment/prod &**** dev****/*.tfvars)****
- それぞれの環境でTerraformコマンドを叩く準備
(****terraform/root/.bashrc)****

ができています

```
---- terraform
   │   └ src
   │   │   └ environment
   │   │   │   └ prod
   │   │   │   │   └ prod.tf
   │   │   │   └ dev
   │   │   │       └ dev.tf
︙
```

あとはコンテナ内でコマンドをたたけばOKです

```
$ plan:dev
```

```
$ apply:prod
```

# 終わりに

無事、各環境ごとでリソースをapplyすることができましたでしょうか？

記事にするためなるべく無駄を省いて記載してますので、もしかしたら必要な部分まで省いてしまっているかもしれませんし、自分の中で当たり前なつもりで書いてない事項があるかもしれません。

何か問題があったり、どこかで詰まったなどありましたらコメントなどぜひよろしくお願いします！