Terraformで作るAWSシステムパターン② Web3層構成

#本記事の目的
Terraformを用いて、AWS上でよく見るシステムパターンを作成し、アウトプットとして記録する。
今回は基本的なWeb3層構成を作成してみる。

#設計
###システム要件
・Web/APサーバー及びDBサーバを各2台ずつ構築し、ロードバランサーで通信を振り分ける。
・ロードバランサーはパブリックサブネットに配置し、https通信でアクセスできるようにする。
・Web/APサーバー及びDBサーバはプライベートサブネットに配置し、ロードバランサーを介してアクセスできるようにする。
・ロードバランサーのアクセスログは、別途専用ストレージに保存する。

###構成図
![web3_configuration.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559588/cac78bcd-fb3d-d01e-e0b8-c06828e7c5a4.png)



#事前準備
###ソフトウェア要件
- [AWS CLI](https://aws.amazon.com/cli) - v2.1.26　※1
- [Terraform](https://www.terraform.io/downloads.html) - v1.0.0
- [jq](https://stedolan.github.io/jq/) -v1.6 ※2

※1 今回はTerraform用のAWS CLIプロファイルを、`tf-demo`という名前で作成し使用する。
※2 Terraform実施時に出力されるjsonデータ加工用のコマンド。


###EC2キーペア用の公開鍵/秘密鍵の作成
```
$ ssh-keygen -t rsa -f ~/.ssh/web3_configuration.pem
```

###ホストゾーンの作成
Route53等で、事前にドメインを購入する。

```
$ aws route53 create-hosted-zone --name <購入したドメイン名> --caller-reference $(date +%Y-%m-%d_%H-%M-%S)
```

#ファイル構成
```
$ tree
web3_configuration/
├── certificate.tf
├── compute.tf
├── config.tf
├── dns.tf
├── firewall.tf
├── lb.tf
├── network.tf
├── output.tf
├── rd.tf
├── storage.tf
├── variable.tf
└── src
     └── user_data.sh
```

#ファイル内容
###certificateファイル
ALB用のSSL証明書の設定を定義する。

```certificate.tf
# ====================
#
# ACM Certificate
#
# ====================
resource "aws_acm_certificate" "example_cert" {
  domain_name               = "*.${var.registered_domain}"
  subject_alternative_names = ["${var.registered_domain}"]
  validation_method         = "DNS"

  tags = {
    Name = "${var.project}-${var.environment}-wildcard-sslcert"
  }

  lifecycle {
    create_before_destroy = true
  }

  depends_on = [
    data.aws_route53_zone.example_route53_zone
  ]
}

# ====================
#
# ACM DNS Verifycation
#
# ====================

resource "aws_route53_record" "example_route53_acm_dns_resolve" {
  for_each = {
    for dvo in aws_acm_certificate.example_cert.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      type   = dvo.resource_record_type
      record = dvo.resource_record_value
    }
  }

  allow_overwrite = true
  zone_id         = data.aws_route53_zone.example_route53_zone.zone_id
  name            = each.value.name
  type            = each.value.type
  ttl             = 600
  records         = [each.value.record]
}

resource "aws_acm_certificate_validation" "cert_valid" {
  certificate_arn         = aws_acm_certificate.example_cert.arn
  validation_record_fqdns = [for record in aws_route53_record.example_route53_acm_dns_resolve : record.fqdn]
}
```

###computeファイル
AMI、EC2インスタンス、EC2キーペアの設定を定義する。

```compute.tf
# ====================
#
# AMI
#
# ====================
# 最新版のAmazonLinux2のAMI情報
data "aws_ami" "example_ami" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  filter {
    name   = "root-device-type"
    values = ["ebs"]
  }

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "block-device-mapping.volume-type"
    values = ["gp2"]
  }

  filter {
    name   = "state"
    values = ["available"]
  }
}


# ====================
#
# EC2 Instance
#
# ====================

resource "aws_instance" "example_instance_1a" {
  ami                         = data.aws_ami.example_ami.image_id
  instance_type               = var.instance_type
  subnet_id                   = aws_subnet.example_subnet_1.id
  associate_public_ip_address = true
  vpc_security_group_ids      = [aws_security_group.example_sg_ec2.id]

  root_block_device {
    volume_type           = var.volume_type
    volume_size           = var.volume_size
    delete_on_termination = true
  }

  key_name  = aws_key_pair.example_key.id
  user_data = file(var.user_data_file)

  tags = {
    Name    = "${var.project}-${var.environment}-ec2-1a"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_instance" "example_instance_1c" {
  ami                         = data.aws_ami.example_ami.image_id
  instance_type               = var.instance_type
  subnet_id                   = aws_subnet.example_subnet_2.id
  associate_public_ip_address = true
  vpc_security_group_ids      = [aws_security_group.example_sg_ec2.id]

  root_block_device {
    volume_type           = var.volume_type
    volume_size           = var.volume_size
    delete_on_termination = true
  }

  key_name  = aws_key_pair.example_key.id
  user_data = file(var.user_data_file)

  tags = {
    Name    = "${var.project}-${var.environment}-ec2-1c"
    Project = var.project
    Env     = var.environment
  }
}

# ====================
#
# Key Pair
#
# ====================

resource "aws_key_pair" "example_key" {
  key_name   = var.key_name
  public_key = file(var.public_key_file)

  tags = {
    Name    = "${var.project}-${var.environment}-keypair"
    Project = var.project
    Env     = var.environment
  }
}
```

###dnsファイル
Route53の設定を定義する。

```dns.tf
# ====================
#
# Route53
#
# ====================

data "aws_route53_zone" "example_route53_zone" {
  name = var.registered_domain
}
resource "aws_route53_record" "example_route53_record" {
  zone_id = data.aws_route53_zone.example_route53_zone.id
  name    = "www.example.${var.domain}"
  type    = "A"

  alias {
    name                   = aws_lb.example_alb.dns_name
    zone_id                = aws_lb.example_alb.zone_id
    evaluate_target_health = true
  }
}
```

###firewallファイル
セキュリティグループの設定を定義する。

```firewall.tf
# ====================
#
# Security Group
#
# ====================

# ALB用セキュリティグループ
resource "aws_security_group" "example_sg_alb" {
  name   = "example_sg_alb"
  vpc_id = aws_vpc.example_vpc.id

  tags = {
    Name    = "${var.project}-${var.environment}-sg-alb"
    Project = var.project
    Env     = var.environment
  }
}

# インバウンドルール(http接続用)
resource "aws_security_group_rule" "in_http_alb" {
  security_group_id = aws_security_group.example_sg_alb.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
}

# インバウンドルール(https接続用)
resource "aws_security_group_rule" "in_https_alb" {
  security_group_id = aws_security_group.example_sg_alb.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
}

# アウトバウンドルール(全開放)
resource "aws_security_group_rule" "out_all_alb" {
  security_group_id = aws_security_group.example_sg_alb.id
  type              = "egress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
}

# EC2用セキュリティグループ
resource "aws_security_group" "example_sg_ec2" {
  name   = "example_sg_ec2"
  vpc_id = aws_vpc.example_vpc.id

  tags = {
    Name    = "${var.project}-${var.environment}-sg-ec2"
    Project = var.project
    Env     = var.environment
  }
}

# インバウンドルール(ssh接続用)
resource "aws_security_group_rule" "in_ssh_ec2" {
  security_group_id = aws_security_group.example_sg_ec2.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
}

# インバウンドルール(http接続用)
resource "aws_security_group_rule" "in_http_ec2" {
  security_group_id = aws_security_group.example_sg_ec2.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
}

# インバウンドルール(https接続用)
resource "aws_security_group_rule" "in_https_ec2" {
  security_group_id = aws_security_group.example_sg_ec2.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
}

# アウトバウンドルール(全開放)
resource "aws_security_group_rule" "out_all_ec2" {
  security_group_id = aws_security_group.example_sg_ec2.id
  type              = "egress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
}

# RDS用セキュリティグループ
resource "aws_security_group" "example_sg_rds" {
  name   = "example_sg_rds"
  vpc_id = aws_vpc.example_vpc.id

  tags = {
    Name    = "${var.project}-${var.environment}-sg-rds"
    Project = var.project
    Env     = var.environment
  }
}

# インバウンドルール(mysql接続用)
resource "aws_security_group_rule" "in_mysql_rds" {
  security_group_id = aws_security_group.example_sg_rds.id
  type              = "ingress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 3306
  to_port           = 3306
  protocol          = "tcp"
}

# アウトバウンドルール(全開放)
resource "aws_security_group_rule" "out_all_rds" {
  security_group_id = aws_security_group.example_sg_rds.id
  type              = "egress"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
}
```

###lbファイル
ALBの設定を定義する。

```lb.tf
# ====================
#
# ALB
#
# ====================
resource "aws_lb" "example_alb" {
  name                       = "${var.project}-${var.environment}-app-alb"
  internal                   = false #falseを指定するとインターネット向け,trueを指定すると内部向け
  load_balancer_type         = "application"
  idle_timeout               = var.idle_timeout
  enable_deletion_protection = false

  access_logs {
    bucket  = aws_s3_bucket.example_log_bucket.id
    enabled = true
  }

  subnets = [
    aws_subnet.example_subnet_1.id,
    aws_subnet.example_subnet_2.id,
  ]

  security_groups = [
    aws_security_group.example_sg_alb.id
  ]
}

# ====================
#
# Listener
#
# ====================

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

  default_action {
    type = "redirect"

    redirect {
      port        = 443
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

resource "aws_lb_listener" "example_alb_lsnr_https" {
  load_balancer_arn = aws_lb.example_alb.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = aws_acm_certificate.example_cert.arn

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


# ====================
#
# Target Group
#
# ====================

resource "aws_lb_target_group" "example_alb_tg" {
  name                 = "${var.project}-${var.environment}-alp-tg"
  target_type          = "instance"
  port                 = 80
  protocol             = "HTTP"
  vpc_id               = aws_vpc.example_vpc.id
  deregistration_delay = var.deregistration_delay

  health_check {
    path                = "/"
    healthy_threshold   = var.healthy_threshold
    unhealthy_threshold = var.unhealthy_threshold
    timeout             = var.timeout
    interval            = var.interval
    matcher             = var.matcher
    port                = "traffic-port"
    protocol            = "HTTP"
  }

  depends_on = [
    aws_lb.example_alb
  ]

  tags = {
    Name    = "${var.project}-${var.environment}-alp-tg"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_lb_target_group_attachment" "example_alb_tgec2_1a" {
  target_group_arn = aws_lb_target_group.example_alb_tg.arn
  target_id        = aws_instance.example_instance_1a.id
}

resource "aws_lb_target_group_attachment" "example_alb_tgec2_1c" {
  target_group_arn = aws_lb_target_group.example_alb_tg.arn
  target_id        = aws_instance.example_instance_1c.id
}

data "aws_elb_service_account" "example_log_service_account" {}
```

###networkファイル
VPC/サブネット/インターネットゲートウェイ/Elastic IP/Natゲートウェイ/ルーティングの設定を定義する。

```network.tf
# ====================
#
# VPC
#
# ====================

resource "aws_vpc" "example_vpc" {
  cidr_block           = var.vpc_cidr_block
  enable_dns_support   = true # DNS解決有効化
  enable_dns_hostnames = true # DNSホスト名有効化

  tags = {
    Name    = "${var.project}-${var.environment}-vpc"
    Project = var.project
    Env     = var.environment
  }
}

# ====================
#
# Public Subnet
#
# ====================

resource "aws_subnet" "example_subnet_1" {
  vpc_id                  = aws_vpc.example_vpc.id
  cidr_block              = var.subnet_cidr_block_1
  map_public_ip_on_launch = true #インスタンス起動時におけるパブリックIPアドレスの自動割り当ての有効化
  availability_zone       = var.availability_zone_1

  tags = {
    Name    = "${var.project}-${var.environment}-subnet-1"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_subnet" "example_subnet_2" {
  vpc_id                  = aws_vpc.example_vpc.id
  cidr_block              = var.subnet_cidr_block_2
  map_public_ip_on_launch = true #インスタンス起動時におけるパブリックIPアドレスの自動割り当ての有効化
  availability_zone       = var.availability_zone_2

  tags = {
    Name    = "${var.project}-${var.environment}-subnet-2"
    Project = var.project
    Env     = var.environment
  }
}

# ====================
#
# Private Subnet
#
# ====================


resource "aws_subnet" "example_subnet_3" {
  vpc_id                  = aws_vpc.example_vpc.id
  cidr_block              = var.subnet_cidr_block_3
  map_public_ip_on_launch = false #インスタンス起動時におけるパブリックIPアドレスの自動割り当ての無効化
  availability_zone       = var.availability_zone_1

  tags = {
    Name    = "${var.project}-${var.environment}-subnet-3"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_subnet" "example_subnet_4" {
  vpc_id                  = aws_vpc.example_vpc.id
  cidr_block              = var.subnet_cidr_block_4
  map_public_ip_on_launch = false #インスタンス起動時におけるパブリックIPアドレスの自動割り当ての無効化
  availability_zone       = var.availability_zone_2

  tags = {
    Name    = "${var.project}-${var.environment}-subnet-4"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_subnet" "example_subnet_5" {
  vpc_id                  = aws_vpc.example_vpc.id
  cidr_block              = var.subnet_cidr_block_5
  map_public_ip_on_launch = false #インスタンス起動時におけるパブリックIPアドレスの自動割り当ての無効化
  availability_zone       = var.availability_zone_1

  tags = {
    Name    = "${var.project}-${var.environment}-subnet-5"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_subnet" "example_subnet_6" {
  vpc_id                  = aws_vpc.example_vpc.id
  cidr_block              = var.subnet_cidr_block_6
  map_public_ip_on_launch = false #インスタンス起動時におけるパブリックIPアドレスの自動割り当ての無効化
  availability_zone       = var.availability_zone_2

  tags = {
    Name    = "${var.project}-${var.environment}-subnet-6"
    Project = var.project
    Env     = var.environment
  }
}


# ====================
#
# Internet Gateway
#
# ====================
resource "aws_internet_gateway" "example_igw" {
  vpc_id = aws_vpc.example_vpc.id

  tags = {
    Name    = "${var.project}-${var.environment}-igw"
    Project = var.project
    Env     = var.environment
  }
}

# ====================
#
# Elastic IP
#
# ====================

resource "aws_eip" "example_eip_1a" {
  vpc        = true
  depends_on = [aws_internet_gateway.example_igw]

  tags = {
    Name    = "${var.project}-${var.environment}-eip-1a"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_eip" "example_eip_1c" {
  vpc        = true
  depends_on = [aws_internet_gateway.example_igw]

  tags = {
    Name    = "${var.project}-${var.environment}-eip-1c"
    Project = var.project
    Env     = var.environment
  }
}

# ====================
#
# Nat Gateway
#
# ====================

resource "aws_nat_gateway" "example_ngw_1a" {
  allocation_id = aws_eip.example_eip_1a.id
  subnet_id     = aws_subnet.example_subnet_3.id
  depends_on    = [aws_eip.example_eip_1a]

  tags = {
    Name = "${var.project}-${var.environment}-ngw-1a"
  }
}

resource "aws_nat_gateway" "example_ngw_1c" {
  allocation_id = aws_eip.example_eip_1c.id
  subnet_id     = aws_subnet.example_subnet_3.id
  depends_on    = [aws_eip.example_eip_1c]

  tags = {
    Name = "${var.project}-${var.environment}-ngw-1c"
  }
}

# ====================
#
# Public Route Table
#
# ====================
resource "aws_route_table" "example_public_rt" {
  vpc_id = aws_vpc.example_vpc.id
  tags = {
    Name    = "${var.project}-${var.environment}-public-rt"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_route" "example_public_route" {
  route_table_id         = aws_route_table.example_public_rt.id
  gateway_id             = aws_internet_gateway.example_igw.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "example_public_subrt_1" {
  subnet_id      = aws_subnet.example_subnet_1.id
  route_table_id = aws_route_table.example_public_rt.id
}

resource "aws_route_table_association" "example_public_subrt_2" {
  subnet_id      = aws_subnet.example_subnet_2.id
  route_table_id = aws_route_table.example_public_rt.id
}

# ====================
#
# Private Route Table
#
# ====================
resource "aws_route_table" "example_private_rt_1a" {
  vpc_id = aws_vpc.example_vpc.id
  tags = {
    Name    = "${var.project}-${var.environment}-private-rt-1a"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_route" "example_private_route_1a" {
  route_table_id         = aws_route_table.example_private_rt_1a.id
  gateway_id             = aws_nat_gateway.example_ngw_1a.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "example_private_subrt_3" {
  subnet_id      = aws_subnet.example_subnet_3.id
  route_table_id = aws_route_table.example_private_rt_1a.id
}


resource "aws_route_table" "example_private_rt_1c" {
  vpc_id = aws_vpc.example_vpc.id
  tags = {
    Name    = "${var.project}-${var.environment}-private-rt-1c"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_route" "example_private_route_1c" {
  route_table_id         = aws_route_table.example_private_rt_1c.id
  gateway_id             = aws_nat_gateway.example_ngw_1c.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "example_private_subrt_4" {
  subnet_id      = aws_subnet.example_subnet_4.id
  route_table_id = aws_route_table.example_private_rt_1c.id
}
```

###rdファイル
RDSの設定を定義する。

```rd.tf
# ====================
#
# RDS Parameter Group
#
# ====================
resource "aws_db_parameter_group" "example_db_parametergroup" {
  name   = "${var.project}-${var.environment}-db-parametergroup"
  family = "mysql8.0"

  parameter {
    name  = "character_set_database"
    value = "utf8mb4"
  }

  parameter {
    name  = "character_set_server"
    value = "utf8mb4"
  }
}


# ====================
#
# RDS Option Group
#
# ====================

resource "aws_db_option_group" "example_db_optiongroup" {
  name                 = "${var.project}-${var.environment}-db-optiongroup"
  engine_name          = "mysql"
  major_engine_version = "8.0"
}


# ====================
#
# RDS Subnet Group
#
# ====================
resource "aws_db_subnet_group" "example_db_subnetgroup" {
  name = "${var.project}-${var.environment}-db-subnetgroup"
  subnet_ids = [
    aws_subnet.example_subnet_5.id,
    aws_subnet.example_subnet_6.id
  ]

  tags = {
    Name    = "${var.project}-${var.environment}-db-subnetgroup"
    Project = var.project
    Env     = var.environment
  }
}


# ====================
#
# RDS instance
#
# ====================

resource "aws_db_instance" "example_db" {
  engine         = "mysql"
  engine_version = "8.0.20"

  identifier = "${var.project}-${var.environment}-db"

  username = "admin"
  password = var.db_password

  instance_class = var.instance_class

  storage_type          = var.storage_type
  allocated_storage     = var.allocated_storage
  max_allocated_storage = var.max_allocated_storage
  storage_encrypted     = false

  multi_az               = true
  db_subnet_group_name   = aws_db_subnet_group.example_db_subnetgroup.name
  vpc_security_group_ids = [aws_security_group.example_sg_rds.id]
  publicly_accessible    = false
  port                   = 3306

  name                 = "${var.project}${var.environment}db"
  parameter_group_name = aws_db_parameter_group.example_db_parametergroup.name
  option_group_name    = aws_db_option_group.example_db_optiongroup.name

  backup_window              = var.backup_window
  backup_retention_period    = var.backup_retention_period
  maintenance_window         = var.maintenance_window
  auto_minor_version_upgrade = false

  deletion_protection = false
  skip_final_snapshot = true
  apply_immediately = true

  tags = {
    Name    = "${var.project}-${var.environment}-db"
    Project = var.project
    Env     = var.environment
  }
}

```

###storageファイル
S3バケットの設定を定義する。

```storage.tf
resource "random_string" "example_unique_key" {
  length  = 6
  upper   = false
  lower   = true
  number  = true
  special = false
}

# ====================
#
# S3 log bucket
#
# ====================

resource "aws_s3_bucket" "example_log_bucket" {
  bucket        = "${var.project}-${var.environment}-log-bucket-${random_string.example_unique_key.result}"
  force_destroy = true

  lifecycle_rule {
    enabled = true

    expiration {
      days = var.expiration_days
    }
  }

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }

  tags = {
    Name    = "${var.project}-${var.environment}-log-bucket"
    Project = var.project
    Env     = var.environment
  }
}

resource "aws_s3_bucket_public_access_block" "example_log_bucket_access_block" {
  bucket                  = aws_s3_bucket.example_log_bucket.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
  depends_on              = [aws_s3_bucket_policy.example_log_bucket_policy]
}

resource "aws_s3_bucket_policy" "example_log_bucket_policy" {
  bucket = aws_s3_bucket.example_log_bucket.id
  policy = data.aws_iam_policy_document.example_log_bucket_policy_document.json
}

data "aws_iam_policy_document" "example_log_bucket_policy_document" {
  statement {
    effect    = "Allow"
    actions   = ["s3:PutObject"]
    resources = ["${aws_s3_bucket.example_log_bucket.arn}/*"]
    principals {
      type        = "AWS"
      identifiers = [data.aws_elb_service_account.example_log_service_account.id]
    }
  }
}

```


###variableファイル
Terraform実施時のグローバル変数を定義する。必要に応じてterraform.tfvarsファイルを作成する。

```variable.tf
# ====================
#
# Variables
#
# ====================


variable "aws_region" {
  default = "ap-northeast-1"
}

variable "project" {
  default = "availability"
}

variable "environment" {
  default = "test"
}

# IAM #
variable "aws_profile" {
  default = "tf-demo" # AWSプロファイル
}

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

variable "volume_type" {
  default = "gp2"
}

variable "volume_size" {
  default = "8"
}

variable "user_data_file" {
  default = "./src/user_data.sh"
}

variable "key_name" {
  default = "availability"
}

variable "public_key_file" {
  default = "~/.ssh/availability.pem.pub"
}

# VPC #
variable "vpc_cidr_block" {
  default = "10.0.0.0/16"
}

variable "subnet_cidr_block_1" {
  default = "10.0.1.0/24"
}

variable "subnet_cidr_block_2" {
  default = "10.0.2.0/24"
}

variable "subnet_cidr_block_3" {
  default = "10.0.3.0/24"
}

variable "subnet_cidr_block_4" {
  default = "10.0.4.0/24"
}

variable "subnet_cidr_block_5" {
  default = "10.0.5.0/24"
}

variable "subnet_cidr_block_6" {
  default = "10.0.6.0/24"
}

variable "availability_zone_1" {
  default = "ap-northeast-1a"
}

variable "availability_zone_2" {
  default = "ap-northeast-1c"
}


# RDS #
variable "db_password" {}

variable "instance_class" {
  default = "db.t2.micro"
}

variable "storage_type" {
  default = "gp2"
}

variable "allocated_storage" {
  default = 20
}

variable "max_allocated_storage" {
  default = 50
}

variable "backup_window" {
  default = "04:00-05:00"
}

variable "backup_retention_period" {
  default = 7
}

variable "maintenance_window" {
  default = "Mon:05:00-Mon:08:00"
}

# ALB #

variable "idle_timeout" {
  default = 60
}

variable "deregistration_delay" {
  default = 300
}

variable "healthy_threshold" {
  default = 5
}

variable "unhealthy_threshold" {
  default = 2
}

variable "timeout" {
  default = 5
}


variable "interval" {
  default = 30
}

variable "matcher" {
  default = 200
}

# Route53 #

variable "registered_domain" {}

# S3 #
variable "expiration_days" {
  default = 10
}
```

###outputファイル
`terraform apply`コマンド実行時の出力内容を定義する。

```output.tf
# ====================
#
# Output
#
# ====================

output "domain_name" {
  value = aws_route53_record.example_route53_record.name
}

output "instance_1a_private_ip" {
  value = aws_instance.example_instance_1a.private_ip
}

output "instance_1c_private_ip" {
  value = aws_instance.example_instance_1c.private_ip
}

output "rds_endpoint" {
	value = aws_db_instance.example_db.endpoint
}

output "db_password" {
  value = var.db_password
}
```


###configファイル
Terraformのプロバイダー及びバージョン設定を定義する。

```config.tf
# ====================
#
# Terraform
#
# ====================

terraform {
  required_version = ">=1.0.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

# ====================
#
# Provider
#
# ====================

provider "aws" {
  profile = var.aws_profile
  region  = var.aws_region
}
```

###user_dataファイル
EC2(Web/APサーバ)用ユーザデータの内容を記述する。
今回は例としてapacheを導入し、ホスト名が記載された静的コンテンツを表示させる。
またMySQLクライアントを導入し、RDSに接続できるようにする。
必要に応じてtomcat等も合わせて導入する。

```user_data.sh
#!/bin/bash
yum update -y

#Apacheインストール
yum install -y httpd

#Apache権限設定
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 {} \;

#Apache用indexファイル書き換え
echo $(hostname) > /var/www/html/index.html

#Apache起動
systemctl enable httpd
systemctl start httpd

#mysqlクライアントインストール
yum install -y mysql
```

#リソース作成
###Terraform実施
```
$ terraform init
$ terraform plan
$ terraform apply
```

###ドメイン名を使用したALBへのhttpsアクセス
```
$ curl https://`terraform output -json | jq -r .domain_name.value` --insecure
```

###EC2からRDSへの接続(踏み台サーバ上で実施)
```
$ ssh -i ~/.ssh/web3_configuration.pem ec2-user@<instance_1a_private_ip/instance_1c_private_ip>
$ mysql -u admin -p <db_password> -h <rds_endpoint>
```
※対象のインスタンスはプライベートサブネットに存在するため、パプリックサブネットに踏み台サーバを別途起動し、踏み台サーバを介してssh接続する。

#感想
今回は基本的なWeb3層構成を作成してみた。
クラウドの場合、PaaSを使用する事でロードバランサーやデータベースサーバを容易に構築/冗長化する事ができ、オンプレミスと比較して手軽に構築できるのは大きなメリットであると感じた。
次回はコンテナオーケストレーション基盤構成を構築する予定である。

#GitHub
https://github.com/kuraboshi/iac/tree/main/terraform/web3_configuration


#参考文献
- [実践Terraform　AWSにおけるシステム設計とベストプラクティス](https://www.amazon.co.jp/dp/B07XT7LJLC/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)