TerraformでELBとEC2とRDSのよくありそうなWebパターンを作ってみる。

よくありそうなこういうアーキテクチャをTerraformで練習がてら作ってみました。
変数もモジュールも考慮せず書いています。
（ALBにDNS名でアクセスするとラウンドロビンアクセスされること、
EC2からRDSに接続できることは確認していますが、セキュリティ上穴があるかもしれません。）

![プレゼンテーション1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257241/cbdcf571-ef8f-e454-bf23-0d3cef9f6953.jpeg)

#ディレクトリ構造
```
work/
├── alb.tf
├── ec2.tf
├── rds.tf
└── vpc.tf
```
Terraformの実行環境は以下の通り

```
$ terraform --version
Terraform v0.12.16
+ provider.aws v2.47.0
```

#vpc.tf
```tf
##########################################################
///VPCの定義
##########################################################
resource "aws_vpc" "example" {
    cidr_block = "10.0.0.0/16"
    instance_tenancy = "default" #ハードウェア占有インスタンスを立てるかどうか
    enable_dns_hostnames = true
    enable_dns_support   = true
}
##########################################################
///パブリックサブネットの定義(マルチAZ),ALBを配置する
##########################################################
resource "aws_subnet" "public_a" {
    vpc_id                  = aws_vpc.example.id
    cidr_block              = "10.0.1.0/24"
    map_public_ip_on_launch = true   #サブネットで起動したインスタンスにパブリックIPを許可する
    availability_zone       = "ap-northeast-1a"
}

resource "aws_subnet" "public_c" {
    vpc_id                  = aws_vpc.example.id
    cidr_block              = "10.0.2.0/24"
    map_public_ip_on_launch = true
    availability_zone       = "ap-northeast-1c"
}

##########################################################
///パブリックサブネットの定義(マルチAZ),EC2(webサーバを配置する)
##########################################################
resource "aws_subnet" "public_a_web" {
    vpc_id                  = aws_vpc.example.id
    cidr_block              = "10.0.3.0/24"
    map_public_ip_on_launch = true
    availability_zone       = "ap-northeast-1a"
}

resource "aws_subnet" "public_c_web" {
    vpc_id                  = aws_vpc.example.id
    cidr_block              = "10.0.4.0/24"
    map_public_ip_on_launch = true
    availability_zone       = "ap-northeast-1c"
}
##########################################################
///インターネットゲートウェイの定義
##########################################################
resource "aws_internet_gateway" "example" {
    vpc_id                  = aws_vpc.example.id
}
##########################################################
///パブリックルートテーブルの定義
##########################################################
resource "aws_route_table" "public" {
    vpc_id = aws_vpc.example.id
}
##########################################################
///パブリックルートの定義（IGWに接続する）
##########################################################
resource "aws_route" "public" {
    route_table_id = aws_route_table.public.id
    gateway_id     = aws_internet_gateway.example.id
    destination_cidr_block = "0.0.0.0/0" 
}
##########################################################
///パブリックルートテーブルと関連付け
##########################################################
resource "aws_route_table_association" "public_a" {
    subnet_id      = aws_subnet.public_a.id
    route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "public_c" {
    subnet_id      = aws_subnet.public_c.id
    route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "public_a_web" {
    subnet_id      = aws_subnet.public_a_web.id
    route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "public_c_web" {
    subnet_id      = aws_subnet.public_c_web.id
    route_table_id = aws_route_table.public.id
}
##########################################################
///プライベートサブネットの定義(マルチAZ),RDSを配置する
##########################################################
resource "aws_subnet" "private_a" {
    vpc_id                  = aws_vpc.example.id
    cidr_block              = "10.0.5.0/24"
    map_public_ip_on_launch = false   #サブネットで起動したインスタンスにパブリックIPを許可する
    availability_zone       = "ap-northeast-1a"
}

resource "aws_subnet" "private_c" {
    vpc_id                  = aws_vpc.example.id
    cidr_block              = "10.0.6.0/24"
    map_public_ip_on_launch = false
    availability_zone       = "ap-northeast-1c"
}

```

#ec2.tf
```tf
##########################################################
///EC2
##########################################################

///publicサブネットaに配置

resource "aws_instance" "a" {
  ami                    ="ami-0c3fd0f5d33134a76"
  vpc_security_group_ids =[aws_security_group.for_webserver_ec2.id]
  instance_type          ="t2.micro"
  subnet_id              = aws_subnet.public_a_web.id
  user_data = <<EOF
  #!/bin/bash
  yum install -y httpd
  yum install -y mysql
  systemctl start httpd
  systemctl enable httpd
  usermod -a -G apache ec2-user
  chown -R ec2-user:apache /var/www
  chmod 2775 /var/www
  find /var/www -type d -exec chmod 2775 {} \;
  find /var/www -type f -exec chmod 0664 {} \;
  echo `hostname` > /var/www/html/index.html
  EOF
}

///publicサブネットcに配置

resource "aws_instance" "c" {
  ami                    ="ami-0c3fd0f5d33134a76"
  vpc_security_group_ids =[aws_security_group.for_webserver_ec2.id]
  instance_type          ="t2.micro"
  subnet_id              = aws_subnet.public_c_web.id
  user_data = <<EOF
  #!/bin/bash
  yum update -y
  yum install -y httpd
  yum install -y mysql
  systemctl start httpd
  systemctl enable httpd
  usermod -a -G apache ec2-user
  chown -R ec2-user:apache /var/www
  chmod 2775 /var/www
  find /var/www -type d -exec chmod 2775 {} \;
  find /var/www -type f -exec chmod 0664 {} \;
  echo `hostname` > /var/www/html/index.html
  EOF
}
##########################################################
///EC2用のSG
##########################################################

resource "aws_security_group" "for_webserver_ec2" {
    name ="for-ec2"
    vpc_id= aws_vpc.example.id
    ingress{
        from_port = 80
        to_port   = 80
        protocol  = "tcp"
        security_groups =[aws_security_group.alb.id]
    }
   egress{
       from_port  = 0
       to_port    = 0
       protocol   = "-1"
       cidr_blocks=["0.0.0.0/0"]
   }
}
```
#alb.tf
```tf
##########################################################
///ALBの定義
##########################################################
resource "aws_lb" "for_webserver" {
  name               = "webserver-alb"
  internal           = false             #falseを指定するとインターネット向け,trueを指定すると内部向け
  load_balancer_type = "application"
  
  security_groups    = [
    aws_security_group.alb.id
  ]

  subnets            = [
      aws_subnet.public_a.id,
      aws_subnet.public_c.id,
  ]
}
##########################################################
///ALBに付与するセキュリティグループの定義
##########################################################

resource "aws_security_group" "alb" {
    name ="alb"
    vpc_id= aws_vpc.example.id
    ingress{
        from_port = 80
        to_port   = 80
        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"]
   }
}
##########################################################
///ALBのリスナーの定義
##########################################################

resource "aws_lb_listener" "for_webserver" {
  load_balancer_arn = aws_lb.for_webserver.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.for_webserver.arn
  }

}

///リスナールールの定義

resource "aws_lb_listener_rule" "forward" {
  listener_arn = aws_lb_listener.for_webserver.arn
  priority     = 99

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.for_webserver.arn
  }

  condition {
      path_pattern{
        values = ["/*"]
      }
  }
}


##########################################################
///ALBのターゲットグループの定義
##########################################################
resource "aws_lb_target_group" "for_webserver" {
  name        = "for-webserver-lb-tg"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.example.id

  health_check {
        path        = "/index.html"
  }
}

///ターゲットグループをインスタンスに紐づける

resource "aws_lb_target_group_attachment" "for_webserver_a" {
  target_group_arn = aws_lb_target_group.for_webserver.arn
  target_id        = aws_instance.a.id
  port             = 80
}

resource "aws_lb_target_group_attachment" "for_webserver_c" {
  target_group_arn = aws_lb_target_group.for_webserver.arn
  target_id        = aws_instance.c.id
  port             = 80
}
```
#rds.tf
```tf
##########################################################
///RDSの定義
##########################################################
resource "aws_db_instance" "default" {
  allocated_storage      = 20
  storage_type           = "gp2"
  engine                 = "mysql"
  engine_version         = "5.7"
  instance_class         = "db.t2.micro"
  name                   = "mydb"
  username               = "foo"
  password               = "foobarbaz"
  parameter_group_name   = "default.mysql5.7"
  multi_az               = true
  db_subnet_group_name   = aws_db_subnet_group.dbsubnet.name
  skip_final_snapshot    = true
  vpc_security_group_ids = [aws_security_group.for_rds.id]
}
##########################################################
///DBサブネットグループの定義
##########################################################
resource "aws_db_subnet_group" "dbsubnet" {
  name       = "main"
  subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_c.id]

  tags = {
    Name = "My DB subnet group"
  }
}
##########################################################
///RDS用のSG
##########################################################

resource "aws_security_group" "for_rds" {
    name ="for-rds"
    vpc_id= aws_vpc.example.id
    ingress{
        from_port = 3306
        to_port   = 3306
        protocol  = "tcp"
        cidr_blocks = [aws_vpc.example.cidr_block]
    }
   egress{
       from_port  = 0
       to_port    = 0
       protocol   = "-1"
       cidr_blocks = ["0.0.0.0/0"]
   }
}
```

#改善点
変数化・モジュール化
AMI作成

ALBをパブリックサブネット、EC2をプライベートサブネットに配置するようにする
→作成しようとしたらunhealthyでBadGatewayになってしまったので要検証

#参考にさせていただいた書籍・ページ
[実践Terraform　AWSにおけるシステム設計とベストプラクティス](https://nextpublishing.jp/book/10983.html)
https://dx.nissho-ele.co.jp/blog/aws-beginner-ec2-elb_20190410.html
https://www.terraform.io/