Terraform基本構文

# 概要
Terraformの基礎学習

## HCL2
- `HashiCorp Language2`の略
- JSONと似ているが少し違う
- `#`でコメントが記載できる*JSONではコメント記載できない
- `=`で値を記載する
- ヒアドキュメントが利用可能
```
    sample = <<EOF
Hello World
EOF
```
- ブロックで記載(ブロック ラベル{})
### ブロックタイプ

|種類|説明|
|:-:|:--|
|locals|外部から変更できないローカル変数|
|variable|外部から変更可能な変数|
|terraform|Terraformの設定|
|provider|プロバイダ|
|data|Terraform管理していないリソースの取り込み|
|resource|Terraform管理対象となるリソース|
|output|外部から参照できるようにする値|

## 変数
HCL2で利用可能な変数

|種類|説明|
|:-:|:--|
|locals|ローカル変数。プライベートな変数で外部から変更できない|
|variables|外部から変更可能な変数。コマンドライン実行時にオプションやファイル指定で上書きできる|
```
# locals定義
locals {
    project = "test"
    env = "dev"
}
```
```
# locals参照
Name = "${local.project}-${local.env}"
```

```
# variables定義
variable "project" {
    type = string
    default = "test"
}
```
```
# variables参照
Name = "${var.project}"
```

## データ型
### 基本のデータ型
```
variable "message" {
    type = string
    default = "Hello"
}

variable "max_count" {
    type = number
    default = 10
}

variable "is_enable" {
    type = bool
    default = true
}
```

### object
キーバリュー形式で定義されるデータがた
```
# 定義
veariable "obj_sample" {
    type = object({
        name = string
        age = number
    })
    default = {
        name = "Tom"
        age = 28
    }
}
# 参照
username = var.obj_sample.name
```

### tuple
- 配列のn番目にどういった型を使うかが決められたデータ型
- RDBの1レコードのようなもの
- 基本は配列でn番目の型が固定される
```
# 定義
variable "tuple_sample" {
    type = tuple([
        string, number
    ])
    default = ["Tom", 28]
}
# 参照
username = var.tuple_sample[0]
```

### list
すべて同じ型で指定される配列

```
# 定義
variable "list_sample" {
    type = list(string)
    default = ["Tom", "Mike"]
}
# 参照
username = var.list_sample[0]
```

### map
キーが文字列、バリューが指定された型となる配列
```
# 定義
variable "map_sample" {
    type = map(string)
    default = {
        "High" = "m5.2xlarge"
        "Mid" = "m5.large"
        "Low" = "t2.micro"
    }
}
# 参照
instance = var.map_sample.High
```

### set
バリューの重複が排除される配列
```
# 定義
variable "set_sample" {
    type = set(string)
    default = [
        "Tom",
        "Mike",
        "Jon",
        "Tom,
    ]
}
# 参照
[for item in var.set_sample : item]
```


## 外部から変数を与える
外部から変数値を与える方法は3種類

|種類|説明|使いわけ|
|:-:|:--|:--|
|環境変数|環境変数へ予め設定してある値を利用|実行ログに残らない。鍵情報、環境依存情報など運用管理サーバーで作業するような厳格な場合|
|変数ファイル|予め決められた変数ファイル名のファイルに指定|git管理できる。ロジックとデータを切り離すことで変更した際に変更箇所がわかりやすい|
|コマンド引数|コマンド引数で指定された値を利用する|実行ログに残る。テストで部分的に変更したい。デバッグで一部変更したいなどの一時利用|

### 環境変数を使った上書き
ソースコードー
```main.tf
variable "message" {
    type = string
    default = "nothing"
}
```
環境点数定義、ビルド実行
```
export TF_VAR_message="Hello World!"
terraform apply
```
＊「TF_VAR_」は接頭辞で接頭辞に続けて変数名を記載する

### 変数ファイルを使った上書き
ソースコード
```main.tf
variable "message" {
    type = string
    default = "nothing"
}
```
変数ファイル
＊拡張子はtfvars
```terraform.tfvars
message = "Hello World!"
```

ビルド実行
```
terraform apply
```

### コマンドを使った上書き
ソースコード
```main.tf
variable "message" {
    type = string
    default = "nothing"
}
```
変数を指定したコマンド実行
```
terraform apply -var message="Hello World!"
```

## Terraforブロック
Terraform全体に関わる設定
- 「Terraformの設定」と「プロバイダー」を切り分けて設定することができる
- 「Terraformのバージョン」と「プロバイダーのバージョン」がある
- Terraformのバージョンがどんどん上がってしまうのでTerraformのバージョンを固定する必要がある

## プロバイダーブロック
プロバイダーの設定
1. クレデンシャル情報(profile: AWSへアクセスするためのプロファイル)
2. デフォルトリージョン(region: デフォルトリージョン)

## データリソース
Terraformの管理対象外のリソースを取り込める
### Terraformの管理対象外のリソース
- AWSが作成したリソース
- 既存システムのリソース
- 外だしの設定

```
data <DATA_TYPE> <DATA_NAME> {
    ...
}
```

## 出力
outputブロック
```
output "<OUTPUT_NAME>" {
    ...
}
```
作成したリソースを外部参照できるようにする
```
resource "aws_instance" "hello-world" {
    ami = "ami-0ce107ae7af2e92b5"
    instance_type = "t2.micro"
}

output "ec2_instance_id" {
    value = aws_instance.hello-world.id
}
```

## リソース参照
リソース間でデータを参照する、させる
↓サブネットがVPCを参照する場合
```
resource "aws_vpc" "vpc" {
    cidr_block = "192.168.0.0/20"
    enable_dns_support = true
    enable_dns_hostnames = true
}

resource "aws_subnet" "public_subnet_1a" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = "ap-northeast-1a"
    cidr_block = "192.168.1.0/24"
    map_public_ip_on_launch = true
}
```
HCL2でリソースを参照するには「.」で連結して参照する
`<BLOCK_TYPE>.<LABEL_1>.<LABEL_2>`
「ドット」連結で表現されるリソースパスを「アドレス」と呼ぶ
＊resourceブロックの場合、<BLOCK_TYPE>は省略される(<BLOCK_TYPE>を記載しない)

## 組み込み関数
|種類|説明|
|:-:|:--|
|numeric|数値演算|
|string|文字列操作|
|collection|配列操作|
|encoding|エンコード。Base64,CSV,JSON,YAMLなどの変換。|
|filesystem|ファイル操作。ディレクトリ名取得、ファイル読み取り|
|date & time|日付操作。現在時刻取得、データフォーマット指定|
|Hash & Crypt|ハッシュ及び暗号化|
|IP Network|CIDR表記の計算。ホスト名、サブネット名取得|
|Type Conversion|型変換。bool,string,numberなどへの変換|

[組み込み関数のリファレンス](https://developer.hashicorp.com/terraform/language/functions)

### 動作確認
「terraform console」コマンド
```
terraform console 
    [-var <KEY>=<VALUE>]
    [-var-file <VAR_FILE>]
```
引数
    -var <KEY>=<VALUE>: 変数の指定
    -var-file <VAR_FILE>: 変数ファイルの指定
＊変数を利用したい場合、変数定義された.tfファイルがあるディレクトリで実行する必要がある

```bash:terminal
terraform console
> floor(4.9)
4
> substr("hello world", 1, 4)
"ello"
>
```

## ファイル分割
- `terraform apply`を実行するにはカレントディレクトリに「.tf」ファイルが存在することが条件
- `terraform apply`を実行するとカレントディレクトリ内の全ての「.tf」ファイルが読み込まれる
- `terraform apply`を実行する際にファイルを指定しなくて良い
- サブディレクトリの「.tf」ファイルは読み込まれない

root/terraform.tfvars: 変数を定義したファイル
root/main.tf: 各種リソースを定義
＊main.tfの中身が大きくなると分割する
＊「ネットワーク」や「データベース」はmain.tfから分割して作成する

## 公式ドキュメント
重要なドキュメント
|種類|説明|リンク|
|:-:|:--|:--|
|HCL2|HCL2の仕様に関わるドキュメント|https://developer.hashicorp.com/terraform/language|
|CLI|Terraformのコマンドに関わるドキュメント|https://developer.hashicorp.com/terraform/cli|
|provider|providerに関するドキュメント|https://registry.terraform.io/?ajs_aid=a84a5b39-6287-4d6e-8c3b-bc782decc2f8&product_intent=terraform|

[AWSの場合](https://registry.terraform.io/providers/hashicorp/aws/latest)
[EC2インスタンス](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance)

## 雛形作成
### 1. 「main.tf]作成

```hcl:main.tf
# ------------------------------
# Terraform configuration
# ------------------------------
terraform {
    required_version = ">=0.13" # terraformのバージョン指定
    required_providers { # 「=」が必要ない
      aws = {
        source = "hashicorp/aws"
        version = "~>3.0"
      }
    }
}

# ------------------------------
# Provider
# ------------------------------
provider "aws" {
    profile = "terraform"
    region = "ap-northeast-1"  
}

# ------------------------------
# Variables
# ------------------------------
variable "project" {
  type = string
}

variable "environment" {
  type = string
}
```

### 2. 「terraform.tfvars」ファイル作成
```hcl:terraform.tfvars
project = "tastylog"
environment = "dev"
```

### 3. 「.gitignore」ファイル作成
「.gitignore」ファイルを作成してgitignore.ioでterraformの内容を取得して記載する
[gitignore.io](https://www.toptal.com/developers/gitignore)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59015/a99fab31-2937-deb2-c944-ed42471b4a06.png)

### 4. 初期化
ターミナルで以下を実行
`terraform init`

#### ファイルのインデントの整形
ターミナルで以下を実行
`terraform fmt`