始めての Terraform on AWS

いつも、CloudFormation で構築しているが、今回は、学習のために、Terraform で構築してみた。

## Terraform の導入

以下、Terraform のサイトから、各OSにあったものをインストールする。

https://www.terraform.io/downloads

今回は、Cloud9 (Amazon Linux2) で実行しています。

```
$ sudo yum install -y yum-utils
$ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
$ sudo yum -y install terraform
$ terraform version
Terraform v1.3.1
on linux_amd64
```

## 作業ディレクトリの作成

```
$ mkdir Teraform
$ cd Teraform/ 
```

## provider の指定

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

## 保存先の指定

```tf:terraform.tf
terraform {
   backend "s3" {
     bucket = "<S3バケット名>"
     key    = "path/terraform.tfstate"
     region = "ap-northeast-1"
   }
}
```

## Teraform への権限

- IAMユーザーの認証情報
- IAMロール

## resource構文

ドキュメントを参考に、デプロイするリソースを記述します。

環境変数（var）になっている部分の説明については後述する、**Variable構文** をご確認ください。

関連するリソースについては、`vpc_id     = aws_vpc.vpc.id` のように記述します。

```terraform:vpc.tf
resource "aws_vpc" "vpc" {
  cidr_block = var.vpc_cidr[var.env]
  enable_dns_hostnames = true
  tags = {
    Name = "&(var.env)-$(var.project)-vpc"
  }  
}

resource "aws_subnet" "pubric_a" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = var.subnet_cidr[var.env]
  availability_zone = var.az["az_a"]
  map_public_ip_on_launch = true
  tags = {
    Name = "&(var.env)-$(var.project)-pubric-a-subnet"
  }
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "&(var.env)-$(var.project)-igw"
  }
}

resource "aws_route_table" "pubric" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
  tags = {
    Name = "&(var.env)-$(var.project)-pubric-rtb"
  }
}

resource "aws_route_table_association" "pubric_a" {
  subnet_id      = aws_subnet.pubric_a.id
  route_table_id = aws_route_table.pubric.id
}
```


```ec2.tf
resource "aws_instance" "ec2" {
  ami           = "ami-078296f82eb463377"
  instance_type = "t2.micro"
  subnet_id   = aws_subnet.pubric_a.id
}

output "ec2_public_ip" {
  value = aws_instance.ec2.public_ip
}
```


## Variable構文

### string

値を単一文字列として使う。

```terraform
variable "変数名" {
    type = string
    default = "xxx"
{
```

### list

値を配列として使う。

```terraform
variable "変数名" {
    type = list
    default = ["xxx", "yyy"]
{
```

### map

値と文字列に紐づけて使う。

```terraform
variable "変数名" {
    type = map
    default{
        aaa = "xxx"
        bbb = "yyy"
    }
{
```

次にような記述ができると思います。

```terraform:variable.tf
variable "vpc_cidr" {
    type = map(string)
    default = {
        prd = "10.1.0.0/16"
        dev = "10.2.0.0/16"
    }
}

variable "subnet_cidr" {
    type = map(string)
    default = {
        prd = "10.1.1.0/24"
        dev = "10.2.2.0/24"
    }
}

variable "az" {
    type = map(string)
    default = {
        az_a = "ap-northeast-1a"
        az_c = "ap-northeast-1c"
    }
}

variable "project" {
    type = string
    default = "test"
}

variable "env" {
    type = string
    default = "dev"
}
```

## init

ここまでの必要なファイルを纏めるとつぎのような構造になると思います。

```
├── terraform
│   ├── develop
│   │   ├── terraform.tf
│   │   └── variable.tf
│   │   └── vpc.tf
│   │   └── ec2.tf
```

作業ディレクトリ(develop)で、init コマンドを実行し、初期化する。

```
$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v4.33.0...
- Installed hashicorp/aws v4.33.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```

## plan

作成されるリソースが表示されます。※.tfファイルの構文やリソースの設定に問題がある場合はErrorとなります。

```
$ terraform plan
```

## apply

`terraform apply`コマンドを実行し、`yes`を入力することでデプロイされていきます。

```
$ terraform apply

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

Outputs:

ec2_public_ip = "X.X.X.X"
```

## 環境複製方法

次のように、production ディレクトリを作成し、variable.tf ファイルで、`env` を `prod` にし、後は、develop と同様に、init、plan、apply することでデプロイされます。

```
├── terraform
│   ├── develop
│   │   ├── terraform.tf
│   │   └── variable.tf
│   │   └── vpc.tf
│   │   └── ec2.tf
│   ├── production
│   │   ├── terraform.tf
│   │   └── variable.tf
│   │   └── vpc.tf
│   │   └── ec2.tf
```

```terraform:variable.tf
variable "env" {
    type = string
    default = "prod"
}
```

## 削除

`terraform destroy`コマンドを実行し、`yes`とすることで削除が完了します。

```
$ terraform destroy
Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes
```