独自ドメインでアクセス：Terraformを使ったVPC設定、Apacheサーバーのデプロイ、ALBおよびACM証明書の設定

## はじめに

この記事では、前回CloudFormationでインフラをコード化した作業を、Terraformで実現した内容について紹介します。

https://qiita.com/free-honda/items/1d8810054b556739afaf

CloudFormationとTerraformはどちらも「Infrastructure as Code」に対応しており、どちらを使用してもインフラの構築が可能です。

内容は簡易的なAWS構成ですが、自分への備忘録として整理しています。

## テンプレート概要
このTerraformテンプレートでは、簡易的な構成ではありますが、以下のリソースをコードで構築しています。

:::note
- **VPC (Virtual Private Cloud)：**
ネットワーク基盤
- **サブネット：**
2つのパブリックサブネット
- **インターネットゲートウェイとルートテーブル：**
外部アクセスを可能にする
- **EC2インスタンス：**
Apacheサーバーをホスト
- **ALB：**
トラフィックの分散
- **ACM証明書：**
HTTPS接続を実現
:::

補足事項として、今回は事前に取得しておいた独自ドメインでのRoute53への登録作業は手動で行います。

## テンプレートのセクションごとの説明

以下に、Terraformコードを6つの重要なセクションに分けて、簡潔に説明します。

### 1. AWSプロバイダ設定
AWSリージョン（``ap-northeast-1``）を設定し、リソースを管理します。

```yaml
provider "aws" {
  region = "ap-northeast-1"
}
```

### 2. VPCとサブネット
VPC（仮想プライベートクラウド）と2つのパブリックサブネットを作成します。

```yaml
resource "aws_vpc" "honda_vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "honda-vpc"
  }
}

resource "aws_subnet" "public_1" {
  vpc_id                  = aws_vpc.honda_vpc.id
  cidr_block              = "10.0.0.0/20"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = true
  tags = {
    Name = "honda-subnet-public1-ap-northeast-1a"
  }
}

resource "aws_subnet" "public_2" {
  vpc_id                  = aws_vpc.honda_vpc.id
  cidr_block              = "10.0.16.0/20"
  availability_zone       = "ap-northeast-1c"
  map_public_ip_on_launch = true
  tags = {
    Name = "honda-subnet-public2-ap-northeast-1c"
  }
}
```

### 3. インターネットゲートウェイ
VPCにインターネット接続を提供するために、インターネットゲートウェイを作成します。

```yaml
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.honda_vpc.id
  tags = {
    Name = "honda-igw"
  }
}

resource "aws_route_table" "public_rtb" {
  vpc_id = aws_vpc.honda_vpc.id
  tags = {
    Name = "honda-rtb-public"
  }
}

resource "aws_route" "internet_route" {
  route_table_id         = aws_route_table.public_rtb.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.igw.id
}
```

### 4. セキュリティグループ
ALB（アプリケーションロードバランサ）とEC2インスタンス用のセキュリティグループを作成します。

```yaml
resource "aws_security_group" "alb_sg" {
  vpc_id = aws_vpc.honda_vpc.id
  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"]
  }
  tags = {
    Name = "honda-alb-sg"
  }
}

resource "aws_security_group" "ec2_sg" {
  vpc_id = aws_vpc.honda_vpc.id
  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb_sg.id]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    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"]
  }
  tags = {
    Name = "honda-ec2-sg"
  }
}
```

ALBにはHTTPS、EC2インスタンスにはHTTPとSSHアクセスを許可します。

### 5. EC2インスタンス
Apacheウェブサーバーをインストールし、簡単なHTMLページを表示するEC2インスタンスを作成します。

```yaml
resource "aws_instance" "apache_instance" {
  ami           = "ami-0b6fe957a0eb4c1b9"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public_1.id
  key_name      = "honda"
  vpc_security_group_ids = [aws_security_group.ec2_sg.id]

  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<html><body><h1>Terraformでデプロイ成功！</h1></body></html>" > /var/www/html/index.html
  EOF

  tags = {
    Name = "terraform-apache-ec2"
  }
}
```

### 6. ALBとターゲットグループ
ALBを設定し、HTTPSリスナーを作成してEC2インスタンスにトラフィックを分散させます。

```yaml
resource "aws_lb" "honda_alb" {
  name               = "honda-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb_sg.id]
  subnets            = [aws_subnet.public_1.id, aws_subnet.public_2.id]
  enable_deletion_protection = false
  tags = {
    Name = "honda-alb"
  }
}

resource "aws_lb_target_group" "honda_target_group" {
  name        = "honda-alb-target-group"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.honda_vpc.id
  target_type = "instance"
}

resource "aws_lb_target_group_attachment" "target_group_attachment" {
  target_group_arn = aws_lb_target_group.honda_target_group.arn
  target_id        = aws_instance.apache_instance.id
}

resource "aws_lb_listener" "honda_listener" {
  load_balancer_arn = aws_lb.honda_alb.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = var.acm_certificate_arn
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.honda_target_group.arn
  }
}
```

## 実際に完成したコード

ここまでの上記内容を踏まえて、最終的に完成したコードは以下になります。

```yaml
provider "aws" {
  region = "ap-northeast-1"
}

# Variables
variable "acm_certificate_arn" {
  default     = "arn:aws:acm:ap-northeast-1:340823193247:certificate/4c8e465b-6926-47c7-87e8-06ae27b895bd"
  description = "ARN of the ACM Certificate"
}

# VPC
resource "aws_vpc" "honda_vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

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

# Public Subnet 1
resource "aws_subnet" "public_1" {
  vpc_id                  = aws_vpc.honda_vpc.id
  cidr_block              = "10.0.0.0/20"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "honda-subnet-public1-ap-northeast-1a"
  }
}

# Public Subnet 2
resource "aws_subnet" "public_2" {
  vpc_id                  = aws_vpc.honda_vpc.id
  cidr_block              = "10.0.16.0/20"
  availability_zone       = "ap-northeast-1c"
  map_public_ip_on_launch = true

  tags = {
    Name = "honda-subnet-public2-ap-northeast-1c"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.honda_vpc.id

  tags = {
    Name = "honda-igw"
  }
}

# Route Table
resource "aws_route_table" "public_rtb" {
  vpc_id = aws_vpc.honda_vpc.id

  tags = {
    Name = "honda-rtb-public"
  }
}

# Default Route
resource "aws_route" "internet_route" {
  route_table_id         = aws_route_table.public_rtb.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.igw.id
}

# Subnet Route Table Associations
resource "aws_route_table_association" "public_1_association" {
  subnet_id      = aws_subnet.public_1.id
  route_table_id = aws_route_table.public_rtb.id
}

resource "aws_route_table_association" "public_2_association" {
  subnet_id      = aws_subnet.public_2.id
  route_table_id = aws_route_table.public_rtb.id
}

# Security Group for ALB
resource "aws_security_group" "alb_sg" {
  vpc_id = aws_vpc.honda_vpc.id

  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"]
  }

  tags = {
    Name = "honda-alb-sg"
  }
}

# Security Group for EC2
resource "aws_security_group" "ec2_sg" {
  vpc_id = aws_vpc.honda_vpc.id

  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb_sg.id]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    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"]
  }

  tags = {
    Name = "honda-ec2-sg"
  }
}

# EC2 Instance
resource "aws_instance" "apache_instance" {
  ami           = "ami-0b6fe957a0eb4c1b9"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public_1.id
  key_name      = "honda"
  vpc_security_group_ids = [
    aws_security_group.ec2_sg.id
  ]

  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<html><body><h1>Terraformでデプロイ成功！</h1></body></html>" > /var/www/html/index.html
  EOF

  tags = {
    Name = "terraform-apache-ec2"
  }
}

# Load Balancer
resource "aws_lb" "honda_alb" {
  name               = "honda-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb_sg.id]
  subnets            = [aws_subnet.public_1.id, aws_subnet.public_2.id]

  enable_deletion_protection = false

  tags = {
    Name = "honda-alb"
  }
}

# Target Group
resource "aws_lb_target_group" "honda_target_group" {
  name        = "honda-alb-target-group"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.honda_vpc.id
  target_type = "instance"
}

resource "aws_lb_target_group_attachment" "target_group_attachment" {
  target_group_arn = aws_lb_target_group.honda_target_group.arn
  target_id        = aws_instance.apache_instance.id
}

# Listener
resource "aws_lb_listener" "honda_listener" {
  load_balancer_arn = aws_lb.honda_alb.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"

  certificate_arn = var.acm_certificate_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.honda_target_group.arn
  }
}
```

Route 53 のホストゾーンに A レコードを追加する作業は手動で行っていますので、やり方が分からない方は、前回の記事を参考にしてみてください。

https://qiita.com/free-honda/items/1d8810054b556739afaf#route-53-%E3%81%AE%E3%83%9B%E3%82%B9%E3%83%88%E3%82%BE%E3%83%BC%E3%83%B3%E3%81%AB-a-%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0

## 実際にやってみた

ここでは、CloudShellでの Terraform のセットアップが完了していることを前提に進めます。

:::note warn
**まだセットアップが完了していない方は、過去の記事を参考にしてセットアップを実施してください。**
:::

https://qiita.com/free-honda/items/19b4e7db8b8f24d41277

まず、以下のコマンドを入力して、**main.tf**というファイルを作成します。

```bash
vi main.tf
```

``vi``では、ファイル編集後にESCキーを押し、``:wq``で保存して終了します。

```main.tf
# 上記の完成した全体のコードをコピーして貼り付けてください。
```

次に、以下のコマンドで Terraform を初期化します。

```bash
terraform init
```

実際に、以下のように「``Terraform has been successfully initialized!``」と表示され、成功が確認できました。

```bash
Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/plugins/signing.html

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.
```

次に、以下のコマンドを入力して、確認メッセージが表示されたら「``yes``」と入力します。

```bash
terraform apply
```

その後、以下のように「``Apply complete! Resources: xxx added, 0 changed, 0 destroyed.``」と表示され、作成が成功したことが確認できました。

![スクリーンショット 2024-11-24 20.13.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/e73cdcc9-adfc-d73b-412a-9df90814285d.png)

## ブラウザからアクセスしてみる

すべての手順が完了したら、ブラウザで取得したドメイン名（例: ``https://honda333.blog/``）にアクセスします。

![スクリーンショット 2024-11-24 20.00.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/61b7edbf-ae75-5d33-f6f4-94bb3586958f.png)


また、ALBやACMも問題なくインフラのコード化が成功していることが確認できました。

![スクリーンショット 2024-11-24 20.00.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/83e605f3-c5a6-1392-1d80-c317dffdcc93.png)


## まとめ

今回ご紹介したTerraformテンプレートでは、基本的なネットワーク構成の作成からウェブサーバーのデプロイ、HTTPS対応のALB設定までを一貫してコードで構築するプロセスを解説しました。

:::note
**このテンプレートを活用すれば、再現性のある効率的なインフラ構築が可能です。**
:::

この技術検証が、誰かの技術的な支えとなれば嬉しいです！

## 関連記事

Terraformによるインフラのコード化についても過去の記事で解説しているため、より深く知りたい方は読んでみてください！

https://qiita.com/free-honda/items/7770f57db0b9e619cfb4

https://qiita.com/free-honda/items/4cadfd1bafa6c71c85fb

https://qiita.com/free-honda/items/fb49c3000aaa79639265

https://qiita.com/free-honda/items/19b4e7db8b8f24d41277