1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TerraformでVPC+EC2の環境を構築~構築編~

1
Posted at

はじめに

前回の記事の続きです。
基礎学習が完了したので、Terraformで実際に構築します。

構成

構成は前回の記事と同じです。

image.png

構築

事前準備

まずは、EC2に接続するためのSSHキーペアを作成します。

ssh-keygen -t ed25519 -f ~/.ssh/terraform-sample-key

踏み台のセキュリティグループに設定するため、IPを控えます。

curl ifconfig.me

早速main.tfを書いていきましょう

段階に沿って定義していきます。

1. providerブロック

AWSの東京リージョンを使う。という宣言です。

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

2. VPC

VPCの定義です。
「10.0.0.0/16のアドレス範囲でVPCを作る」 という定義です。

main.tf
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  tags = { Name = "terraform-vpc" }
}

3. サブネット

サブネットの定義です。
パブリックサブネットとプライベートサブネットは同じ定義です。
後ほど登場するルートテーブルにて、Internet Gatewayにつなげる事でパブリックサブネットになり、区別できます。

main.tf
resource "aws_subnet" "public_1a" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
  tags = { Name = "terraform-public-1a" }
}

resource "aws_subnet" "public_1c" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.2.0/24"
  availability_zone = "ap-northeast-1c"
  tags = { Name = "terraform-public-1c" }
}

resource "aws_subnet" "private_1a" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.3.0/24"
  availability_zone = "ap-northeast-1a"
  tags = { Name = "terraform-private-1a" }
}

resource "aws_subnet" "private_1c" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.4.0/24"
  availability_zone = "ap-northeast-1c"
  tags = { Name = "terraform-private-1c" }
}

4. Internet Gateway

Internet Gatewayの定義です。
アタッチするVPCのみを記載します。

main.tf
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags = { Name = "terraform-igw" }
}

5. Nat Gateway + Elastic IP

Nat GatewayとElastic IPの定義です。
NAT Gatewayは、パブリックサブネット1aに設定します。

main.tf
resource "aws_eip" "nat" {
  domain = "vpc"
}

resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id = aws_subnet.public_1a.id
  tags = { Name = "terraform-nat"}
}

6. ルートテーブル

まずは、パブリックサブネット用のルートテーブルを定義して、各パブリックサブネットに紐づけを行います。
ここで、Internet Gatewayと紐づけることによって、パブリックであると定義されるわけです。

main.tf
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

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

  tags = { Name = "terraform-public-tr" }
}

resource "aws_route_table_association" "public_1a" {
  subnet_id = aws_subnet.public_1a.id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "public_1c" {
  subnet_id = aws_subnet.public_1c.id
  route_table_id = aws_route_table.public.id
}

続いては、プライベートサブネット用のルートテーブルです。
プライベートサブネットは、NATゲートウェイに紐づけを行います。

main.tf
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main.id
  }

  tags = { Name = "terraform-private-tr" }
}

resource "aws_route_table_association" "private_1a" {
  subnet_id = aws_subnet.private_1a.id
  route_table_id = aws_route_table.private.id
}

resource "aws_route_table_association" "private_1c" {
  subnet_id = aws_subnet.private_1c.id
  route_table_id = aws_route_table.private.id
}

7. セキュリティグループ

セキュリティグループの定義です。
ALBと踏み台とWebサーバー用の3つが必要です。
ingressは、インバウンド(外→中)。egressは、アウトバウンド(中→外)の定義です。

from_port/to_portは、許可するポートの範囲。
cidr_blocksは、許可するIP。
security_groupsは、許可するSGです。

踏み台のingressは、IPで制限を掛け、IP制限不要の際は全て許可(0.0.0.0/0)にします。
protocolが-1の際は、全プロトコルを許可する設定です。

main.tf
// ALB用
resource "aws_security_group" "alb" {
  vpc_id = aws_vpc.main.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"]
  }

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

// 踏み台用
resource "aws_security_group" "bastion" {
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["xxx.x.xxx.xx/32"] // 事前準備で取得したIP
  }

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

  tags = { Name = "terraform-sg-bastion" }
}

// Webサーバー用
resource "aws_security_group" "web" {
  vpc_id = aws_vpc.main.id

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

  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [aws_security_group.bastion.id]
  }

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

  tags = { Name = "terraform-sg-web" }
}

8.キーペア

事前準備で作成した公開鍵をAWSに登録します。

main.tf
resource "aws_key_pair" "main" {
  key_name   = "terraform-key"
  public_key = file("~/.ssh/terraform-sample-key.pub")
}

9. EC2

EC2の定義です。
今回は特に拘りが無いので、Amazon Linux 2023のt2.microにしました。
associate_public_ip_addressにtrueを設定し、踏み台サーバーにのみパブリックIPを持たせます。

main.tf
// 踏み台
resource "aws_instance" "bastion" {
  ami = "ami-0ebdce61e922f7e5b"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.public_1a.id
  vpc_security_group_ids = [aws_security_group.bastion.id]
  associate_public_ip_address = true
  key_name = aws_key_pair.main.key_name

  tags = { Name = "terraform-bastion" }
}

# WebサーバーEC2(AZ1)
resource "aws_instance" "web_1a" {
  ami = "ami-0ebdce61e922f7e5b"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.private_1a.id
  vpc_security_group_ids = [aws_security_group.web.id]
  associate_public_ip_address = false
  key_name = aws_key_pair.main.key_name

  tags = { Name = "terraform-web-1a" }
}

# WebサーバーEC2(AZ2)
resource "aws_instance" "web_1c" {
  ami = "ami-0ebdce61e922f7e5b"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.private_1c.id
  vpc_security_group_ids = [aws_security_group.web.id]
  associate_public_ip_address = false
  key_name = aws_key_pair.main.key_name

  tags = { Name = "terraform-web-1c" }
}

10. ALB

最後にALBの定義です。
internalがfalseの場合、インターネット向け。trueの場合、内部向けです。
load_balancer_typeをapplicationにすることで、ALBになります。
ターゲットグループでヘルスチェックとEC2のアタッチを行います。
リスナーを設定することによって、ポート80でリクエストを受け付け、ターゲットグループに転送する訳ですね。

main.tf
# ALB本体
resource "aws_lb" "main" {
  name = "terraform-alb"
  internal = false
  load_balancer_type = "application"
  security_groups = [aws_security_group.alb.id]
  subnets = [
    aws_subnet.public_1a.id,
    aws_subnet.public_1c.id,
  ]

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

# ターゲットグループ
resource "aws_lb_target_group" "web" {
  name = "terraform-tg"
  port = 80
  protocol = "HTTP"
  vpc_id = aws_vpc.main.id

  health_check {
    path = "/"
  }
}

# ターゲットグループにEC2を登録(AZ1)
resource "aws_lb_target_group_attachment" "web_1a" {
  target_group_arn = aws_lb_target_group.web.arn
  target_id = aws_instance.web_1a.id
  port = 80
}

# ターゲットグループにEC2を登録(AZ2)
resource "aws_lb_target_group_attachment" "web_1c" {
  target_group_arn = aws_lb_target_group.web.arn
  target_id = aws_instance.web_1c.id
  port = 80
}

# リスナー
resource "aws_lb_listener" "web" {
  load_balancer_arn = aws_lb.main.arn
  port = 80
  protocol = "HTTP"

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

実行

後は以下を実行して完了です!

terraform init
terraform plan
terraform apply

確認

一旦マネジメントコンソール上で、定義したものが作成されたか確認してみましょう。

確認後、以下の手順でEC2にSSH接続してみましょう。

踏み台サーバーのパブリックIPを確認します。

aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=terraform-bastion" \
  --query "Reservations[0].Instances[0].PublicIpAddress" \
  --output text \
  --region ap-northeast-1

続いてWebサーバーのプライベートIPを確認します。

aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=terraform-web-1a" \
  --query "Reservations[0].Instances[0].PrivateIpAddress" \
  --output text \
  --region ap-northeast-1

踏み台を経由して、Webサーバーに接続します。

ssh -i ~/.ssh/terraform-sample-key \
    -o "ProxyCommand ssh -i ~/.ssh/terraform-sample-key -W %h:%p ec2-user@<踏み台のパブリックIP>" \
    ec2-user@<WebサーバーのプライベートIP>

無事EC2に接続確認出来ましたら終了です!

放置すると料金が発生するため、学習用として構築した際は削除推奨です。
terraform destroy で削除できます。

最後に

ほぼ模写で構築しましたが、覚えることも多く大変ですね...
ですが基礎は何とか学習できたので、プライベート開発する際は、これをベースに構築してみたいと思います。
今回はmain.tfに全て記載したので、変数化したりモジュール化したり、簡略化する方法を学習し、次回の記事でまとめます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?