Terraform EC2構築 × Ansible Nginxインストール

# Terraform構築
## 基本コマンド
```
# aws configure profile作成  profile名：default
$ aws configure --profile default  

# aws configure 確認・編集
$ vim ~/.aws/credentials

# aws configure 削除
$ rm -r ~/.aws

# terraformでモジュール単位でリソース作成
$ terraform apply -target module.VPC

# Ansible Role作成
$ ansible-galaxy init nginx
```


## ディレクトリ構成

```:tree
├── backend.tf
├── main.tf
├── modules
│   ├── alb
│   │   ├── alb.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── ec2
│   │   ├── ec2.tf
│   │   └── variables.tf
│   ├── security_group
│   │   ├── outputs.tf
│   │   ├── security-group.tf
│   │   └── variables.tf
│   └── vpc
│       ├── outputs.tf
│       └── vpc.tf
```

## backend.tf

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

terraform {
  backend "s3" {
    bucket                  = "tfstate-s3bucket"  // 事前にs3バケットを作成する
    key                     = "terraform.tfstate"
    shared_credentials_file = "~/.aws/credentials" 
    profile                 = "default"           // 事前にprofile作成する
    region                  = "ap-northeast-1"
  }
}
```

## main.tf

```main.tf
module vpc {
  source = "./modules/vpc"
}

module security_group {
  source = "./modules/security_group"

  config = {
    // vpc.tfでoutputする
    vpc_id      = module.vpc.vpc_id
  }
}

module ec2 {
  source = "./modules/ec2"

  config = {
    // vpc.tfでoutputする
    subnet-a_id = module.vpc.subnet-a_id
    subnet-c_id = module.vpc.subnet-c_id
    security_group_id = module.security_group.security_group_id
  }
}

module alb {
  source = "./modules/alb"

  config = {
    // vpc.tfでoutputする
    vpc_id      = module.vpc.vpc_id
    subnet-a_id = module.vpc.subnet-a_id
    subnet-c_id = module.vpc.subnet-c_id
    // security_group.tfでoutputする
    security_group_id = module.security_group.security_group_id
  }
}
```

## vpcモジュール

```modules/vpc/vpc.tf
## VPCの設定
resource "aws_vpc" "public-vpc" {
  cidr_block           = "10.0.0.0/16"
  instance_tenancy     = "default"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "public-vpc"
  }
}

##サブネットの作成
resource "aws_subnet" "public-subnet-a" {
  vpc_id            = aws_vpc.public-vpc.id
  cidr_block        = "10.0.16.0/24"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "public-subnet-a"
  }
}

resource "aws_subnet" "public-subnet-c" {
  vpc_id            = aws_vpc.public-vpc.id
  cidr_block        = "10.0.32.0/24"
  availability_zone = "ap-northeast-1c"

  tags = {
    Name = "public-subnet-c"
  }
}


##ルートテーブルの追加(0.0.0.0/0)
resource "aws_route_table" "public-route" {
  vpc_id = aws_vpc.public-vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.example_GW.id
  }
}

##ルートテーブルの追加
resource "aws_route_table_association" "puclic-subnet-a" {
  subnet_id = aws_subnet.public-subnet-a.id
  route_table_id = aws_route_table.public-route.id
}

resource "aws_route_table_association" "puclic-subnet-c" {
  subnet_id = aws_subnet.public-subnet-c.id
  route_table_id = aws_route_table.public-route.id
}

##ゲートウェイの設定
resource "aws_internet_gateway" "example_GW" {
  vpc_id = aws_vpc.public-vpc.id
}
```

```modules/vpc/outputs.tf
output "vpc_id" {
  value = aws_vpc.public-vpc.id
}

output "subnet-a_id" {
  value = aws_subnet.public-subnet-a.id
}

output "subnet-c_id" {
  value = aws_subnet.public-subnet-c.id
}
```

## security_groupモジュール
```modules/security_group/security-group.tf
## security group
resource "aws_security_group" "example_SG" {
  name        = "example_SG"
  description = "example_SG"
  vpc_id      = var.config.vpc_id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "example_SG"
  }
}
```

```modules/security_group/variables.tf
variable "config" {
  type = object({
    vpc_id      = string
 })
}
```

```modules/security_group/outputs.tf
output "security_group_id" {
  value = aws_security_group.example_SG.id
}
```

## EC2モジュール

```modules/ec2/ec2.tf
##EC2(example_EC2)
resource "aws_instance" "example_EC2" {
  ami                     = var.ami
  instance_type           = var.instance_type
  disable_api_termination = false
  key_name                = "EC2-key"
  vpc_security_group_ids  = [var.config.security_group_id]
  subnet_id             = var.config.subnet-a_id

  root_block_device {
    volume_type = "gp2"
    volume_size = var.volume_size
  }

  tags = {
    Name = "example_EC2"
  }
}

##EIP(example_EC2)
resource "aws_eip" "example_EC2" {
  instance = "${aws_instance.example_EC2.id}"
  vpc      = true
}
```

```modules/ec2/variables.tf
variable "config" {
  type = object({
    subnet-a_id = string
    subnet-c_id = string
    security_group_id = string
  })
}

variable "ami" {
  default = "ami-XXXXXXXXXXXX"
}

variable "instance_type" {
  default = "t2.micro"
}

variable "volume_size" {
  default = "8"
}
```

## ALBモジュール

```modules/alb/alb.tf
resource "aws_alb" "alb" {
  name                       = "alb"
  security_groups            = [var.config.security_group_id]
  subnets                    = [var.config.subnet-a_id, var.config.subnet-c_id]
  internal                   = false
  enable_deletion_protection = false
}

resource "aws_alb_target_group" "target-group" {
  name        = "target-group"
  depends_on  = [aws_alb.alb]
  port        = 80
  protocol    = "HTTP"
  vpc_id      = var.config.vpc_id
  target_type = "ip"

  health_check {
    protocol            = "HTTP"
    path                = "/ping"
    port                = 80
    unhealthy_threshold = 5
    timeout             = 5
    interval            = 10
    matcher             = 200
  }
}

resource "aws_alb_listener" "listener" {
  load_balancer_arn = aws_alb.alb.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    target_group_arn = aws_alb_target_group.target-group.arn
    type             = "forward"
  }
}

resource "aws_alb_listener_rule" "rule" {
  depends_on   = [aws_alb_target_group.target-group]
  listener_arn = aws_alb_listener.listener.arn
  priority     = 100

  action {
    type             = "forward"
    target_group_arn = aws_alb_target_group.target-group.arn
  }
  condition {
    path_pattern {
      values = ["*"]
    }
  }
}
```

```modules/alb/variables.tf
variable "config" {
  type = object({
    vpc_id = string
    subnet-a_id = string
    subnet-c_id = string
    security_group_id = string
  })
}
```

```modules/alb/outputs.tf
output "dns_name" {
  value = aws_alb.alb.dns_name
}
```

# ansible構築

## ディレクトリ構成

#### Role作成
```
$ ansible-galaxy init nginx
```

```:tree
├── nginx
│   ├── README.md
│   ├── defaults
│   │   └── main.yml
│   ├── files
│   ├── handlers
│   │   └── main.yml
│   ├── meta
│   │   └── main.yml
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   ├── tests
│   │   ├── inventory
│   │   └── test.yml
│   └── vars
│       └── main.yml
├── playbook.yml
├── aws_ec2.yml
```

## aws_ec2.yml

```aws_ec2.yml
plugin: aws_ec2

aws_access_key_id: XXXXXXXX
aws_secret_access_key: XXXXXXXX
aws_security_token:

regions:
  - ap-northeast-1
hostnames:
  - tag:Name
groups:
  nginx: "'<EC2インスタンス名>' in tags.Name"
compose:
  ansible_host: public_ip_address
```

## playbook.yml

```playbook.yml
- hosts: nginx
  become: true

  vars_files:
    - ./nginx/vars/main.yml
  roles:
    - nginx
```

## Nginx Role

```nginx/tasks/main.yml
- name: Enable amzn2extra-nginx1.12 repository
  shell: amazon-linux-extras enable nginx1.12
  changed_when: false

- name: Install Nginx packages from amazon-linux-extras
  yum:
    name: nginx
    state: present
```

```nginx/vars/main.yml
# vars file for nginx
ansible_ssh_user: ec2-user
ansible_ssh_private_key_file: ~/.ssh/EC2-key.pem
```

#### 実行コマンド

```
$ ansible-playbook -i aws_ec2.yml playbook.yml
```

## 参考文献
 - [AnsibleでEC2のAmazon Linux 2にNginxをインストールする方法の検討（古いJinja2でもOK）](https://qiita.com/3244/items/051a2c44e19ab932dc0f)
 - [AnsibleでAmazonLinux2 + Laravel + mysql5.7 + nginx + SSL環境を構築する。](https://qiita.com/infratoweb/items/433387c45edd4fe696a0)
 - [【AWS・Ansible】 コマンド１つでAmazon EC2インスタンス作成](https://qiita.com/yamazakim/items/078572d3dabb453842ed)
 - [Ansibleでnginx+php構築](https://qiita.com/saisai07/items/2c20e0607bac593e61a5)
 - [TerraformでALBを作成する](https://qiita.com/gogo-muscle/items/81d9f73f16f901d95424)
 - [特定のresourceだけ terraform {plan|apply} したい
](https://qiita.com/tacke/items/5263b59f52c353d41e6d)
 - [Ansibleでできることを中の人が教えます - インストールと実行～EC2へのNginx投入までを学ぼう](https://eh-career.com/engineerhub/entry/2019/04/12/103000)
 - [[Terraform][Backends][v0.9]tfstateファイルの管理方法](https://blog.adachin.me/archives/5884)