インフラのコード化：Terraformを用いたAWS環境（VPC、EC2インスタンス、ALB設定）の自動化と管理

## はじめに

普段、EC2インスタンスやApplication Load Balancer (ALB) の検証環境を手軽に作成したいときには、Terraformを利用しています。

:::note warn
**簡易的に使用する目的で活用していますが、改めて自分への備忘録として整理していきます。**
:::

この記事では、過去の記事で手動で構築した環境を、そのままコード化する方法を紹介します。

## 前回の記事の復習
前回の記事では、EC2インスタンスの起動からApplication Load Balancer (ALB) の設定、動作確認までの手順を説明しました。

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

今回は、その内容を踏まえて、以下の構成をインフラコードとして定義していきます。

![スクリーンショット 2024-11-30 11.37.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/42460462-2a7b-ec7e-94fb-942496abe424.png)
引用画像：https://canmakewakuwaku.com/elb_alb/

## 実際に完成したコード
以下は、Terraformで作成したコードです。長くなっていますが、内容としてはそれほど難しくないシンプルな構成となっています。

```main.tf
provider "aws" {
  region = "ap-northeast-1"  # 東京リージョンを指定
}

# VPC作成 (仮想プライベートクラウド)
resource "aws_vpc" "main_vpc" {
  cidr_block           = "10.0.0.0/16"  # VPCのCIDRブロック
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "terraform-main-vpc"
  }
}

# パブリックサブネット1作成
resource "aws_subnet" "public_subnet_1" {
  vpc_id                  = aws_vpc.main_vpc.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true

  tags = {
    Name = "terraform-public-subnet-1"
  }
}

# パブリックサブネット2作成
resource "aws_subnet" "public_subnet_2" {
  vpc_id                  = aws_vpc.main_vpc.id
  cidr_block              = "10.0.2.0/24"
  map_public_ip_on_launch = true

  tags = {
    Name = "terraform-public-subnet-2"
  }
}

# インターネットゲートウェイ作成
resource "aws_internet_gateway" "main_igw" {
  vpc_id = aws_vpc.main_vpc.id

  tags = {
    Name = "terraform-main-igw"
  }
}

# ルートテーブル作成 (パブリックルート)
resource "aws_route_table" "public_route_table" {
  vpc_id = aws_vpc.main_vpc.id

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

  tags = {
    Name = "terraform-public-route-table"
  }
}

# サブネット1とルートテーブルを関連付ける
resource "aws_route_table_association" "public_association_1" {
  subnet_id      = aws_subnet.public_subnet_1.id
  route_table_id = aws_route_table.public_route_table.id
}

# サブネット2とルートテーブルを関連付ける
resource "aws_route_table_association" "public_association_2" {
  subnet_id      = aws_subnet.public_subnet_2.id
  route_table_id = aws_route_table.public_route_table.id
}

# セキュリティグループ作成 (HTTPおよびSSHアクセスを許可)
resource "aws_security_group" "public_sg" {
  vpc_id = aws_vpc.main_vpc.id

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

  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 = "terraform-public-sg"
  }
}

# EC2インスタンス1作成 (Aサイト)
resource "aws_instance" "apache_ec2_1" {
  ami                         = "ami-0b6fe957a0eb4c1b9"
  instance_type               = "t2.micro"
  subnet_id                   = aws_subnet.public_subnet_1.id
  security_groups             = [aws_security_group.public_sg.id]
  associate_public_ip_address = true

  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<html><body><h1>自分に負けるな〜（Aサイト）</h1></body></html>" > /var/www/html/index.html
  EOF

  depends_on = [aws_security_group.public_sg]

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

# EC2インスタンス2作成 (Bサイト)
resource "aws_instance" "apache_ec2_2" {
  ami                         = "ami-0b6fe957a0eb4c1b9"
  instance_type               = "t2.micro"
  subnet_id                   = aws_subnet.public_subnet_2.id
  security_groups             = [aws_security_group.public_sg.id]
  associate_public_ip_address = true

  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<html><body><h1>自分に負けるな〜（Bサイト）</h1></body></html>" > /var/www/html/index.html
  EOF

  depends_on = [aws_security_group.public_sg]

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

# アプリケーションロードバランサー(ALB)作成
resource "aws_lb" "main_alb" {
  name               = "terraform-main-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.public_sg.id]
  subnets            = [aws_subnet.public_subnet_1.id, aws_subnet.public_subnet_2.id]

  enable_deletion_protection = false

  tags = {
    Name = "terraform-main-alb"
  }
}

# ターゲットグループ作成 (EC2インスタンスをターゲットに)
resource "aws_lb_target_group" "main_target_group" {
  name     = "terraform-main-target-group"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main_vpc.id

  health_check {
    interval = 30
    path     = "/"
    protocol = "HTTP"
    timeout  = 5
    healthy_threshold   = 3
    unhealthy_threshold = 3
  }

  tags = {
    Name = "terraform-main-target-group"
  }
}

# ALBのリスナー作成 (HTTPリスナー)
resource "aws_lb_listener" "http_listener" {
  load_balancer_arn = aws_lb.main_alb.arn
  port              = "80"
  protocol          = "HTTP"

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

# EC2インスタンス1をターゲットグループに追加
resource "aws_lb_target_group_attachment" "ec2_attachment_1" {
  target_group_arn = aws_lb_target_group.main_target_group.arn
  target_id        = aws_instance.apache_ec2_1.id
  port             = 80
}

# EC2インスタンス2をターゲットグループに追加
resource "aws_lb_target_group_attachment" "ec2_attachment_2" {
  target_group_arn = aws_lb_target_group.main_target_group.arn
  target_id        = aws_instance.apache_ec2_2.id
  port             = 80
}
```

## 実際にやってみた

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

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

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: xx added, 0 changed, 0 destroyed.``」と表示され、作成が成功したことが確認できました。

![スクリーンショット 2024-11-17 1.30.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/c3356544-3934-dfa9-7e6f-8f6bf29926e6.png)

## デプロイ後の動作確認
ALBの詳細画面からDNS名を取得し、ブラウザでアクセスして、2台のApacheサーバーが正常に応答しているか確認します。

私の環境では、以下がロードバランサーのDNS名ですので、ブラウザで確認してみます。

``http://terraform-main-alb-421986447.ap-northeast-1.elb.amazonaws.com/``

![スクリーンショット 2024-11-30 11.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/f746ab88-59d0-391f-4dd3-95a33eb7de6d.png)

何度かアクセスして「``自分に負けるな〜（xxサイト）``」と表示される「``A``」「``B``」が切り替われば、動作確認は成功です。

![スクリーンショット 2024-11-30 11.28.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/51ee8bcb-a4b3-1c46-cafc-9607989ec583.png)

## まとめ
この記事では、手動で構築したインフラ環境をTerraformを使ってコード化する手順を紹介しました。

VPC、サブネット、インターネットゲートウェイ、セキュリティグループ、EC2インスタンス、ロードバランサー（ALB）、ターゲットグループ、リスナー、そしてターゲットグループへのEC2インスタンス追加までの流れをまとめました。

この記事が、誰かの技術の支えになれば嬉しいです！

## 関連記事

Terraformについての記事はいくつか投稿しているので興味のあるものがあれば、読んでみてください！

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

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

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