2
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の環境を構築~整形編~

2
Last updated at Posted at 2026-05-18

はじめに

前回の記事の続きです。

前回はTerraformでVPC内に踏み台サーバーとWebサーバーを構築しました。
今回は、Terraformの文法を学習すると同時に前回書いたコードを整形してみます。

前回で作成したコード
main.tf
provider "aws" {
  region = "ap-northeast-1"
}

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

# サブネット
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" }
}

# インターネットゲートウェイ
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags = { Name = "terraform-igw" }
}

# Elastic IP
resource "aws_eip" "nat" {
  domain = "vpc"
}

# NATゲートウェイ
resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id = aws_subnet.public_1a.id
  tags = { Name = "terraform-nat"}
}

# ルートテーブル
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
}

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
}

# セキュリティグループ
# 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" }
}

# キーペア
resource "aws_key_pair" "main" {
  key_name   = "terraform-key"
  public_key = file("~/.ssh/terraform-sample-key.pub")
}

# EC2
# 踏み台
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" }
}

# 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-sample/
├── main.tf
├── variables.tf
├── locals.tf
├── outputs.tf
├── vpc.tf
├── security_group.tf
├── ec2.tf
├── alb.tf
└── terraform.tfvars
  1. main.tfに集約されたソースコードを分割します
  2. ハードコーディングされたIP等をvariables.tfで変数化します
  3. 自分のIPはterraform.tfvarsで外出し
  4. locals.tfに共通タグを定義します
  5. aws ec2 describe-instancesコマンドで踏み台やWebサーバーのIPを取得していたのをoutputs.tfを定義し、terraform apply実行時、出力するようにします

解説

main.tfに集約されたソースコードを分割します

terraform-sample/
├── main.tf
├── vpc.tf
├── security_group.tf
├── ec2.tf
└── alb.tf

main.tfからコピペでそれぞれのリソースに分割します。
以下例(vpc.tf)

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

# サブネット
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" }
}

# インターネットゲートウェイ
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags = { Name = "terraform-igw" }
}

# Elastic IP
resource "aws_eip" "nat" {
  domain = "vpc"
}

# NATゲートウェイ
resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id = aws_subnet.public_1a.id
  tags = { Name = "terraform-nat"}
}

# ルートテーブル
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
}

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
}

ハードコーディングされたIP等をvariables.tfで変数化します

variables.tf
variable "region" {
  description = "AWSリージョン"
  default = "ap-northeast-1"
}

variable "vpc_cidr" {
  description = "VPCのCIDRブロック"
  default = "10.0.0.0/16"
}

variable "my_ip" {
  description = "踏み台サーバーに許可する自分のIP"
}

variable "ami_id" {
  description = "EC2のAMI ID"
  default = "ami-0ebdce61e922f7e5b"
}

variable "instance_type" {
  description = "EC2のインスタンスタイプ"
  default = "t2.micro"
}

variable "ssh_public_key_path" {
  description = "SSH公開鍵のファイルパス"
  default = "~/.ssh/terraform-sample-key.pub"
}

my_ipは、次項で外出しするため、defaultは設定しません。
変数は以下のように、var.変数名で使用します。

# EC2
# 踏み台
resource "aws_instance" "bastion" {
  ami = var.ami_id
  instance_type = var.instance_type
}

自分のIPはterraform.tfvarsで外出し

.gitignoreに含めて、コミットしないようにしましょう。

terraform.tfvars
my_ip = "自分のIP"

locals.tfに共通タグを定義します

現在、各リソースにNameだけを設定しましたが、共通のタグ属性を追加します。

locals.tf
locals {
  common_tags = {
    ManagedBy = "Terraform"
    Project = "terraform-sample"
  }
}

以下使用例です。
既存のName属性とマージします。

# EC2
# 踏み台
resource "aws_instance" "bastion" {
  tags = merge(local.common_tags, { Name = "terraform-bastion" })
}

variables.tfとlocals.tfの使い分けは以下の通りです。
「環境や人によって変えたい」→ variables
「内部で固定・計算して使う」→ locals

aws ec2 describe-instancesコマンドで踏み台やWebサーバーのIPを取得していたのをoutputs.tfを定義し、terraform apply実行時、出力するようにします

outputs.tf
# 踏み台のパブリックIP
output "bastion_public_ip" {
  description = "踏み台サーバーのパブリックIP"
  value = aws_instance.bastion.public_ip
}

# WebサーバーのプライベートIP(AZ1)
output "web_1a_private_ip" {
  description = "Webサーバー(AZ1)のプライベートIP"
  value = aws_instance.web_1a.private_ip
}

# WebサーバーのプライベートIP(AZ2)
output "web_1c_private_ip" {
  description = "Webサーバー(AZ2)のプライベートIP"
  value = aws_instance.web_1c.private_ip
}

# ALBのDNS名
output "alb_dns_name" {
  description = "ALBのDNS名"
  value = aws_lb.main.dns_name
}

terraform applyすると、上記で定義したoutputsがコンソール上に出力されます。
terraform apply後、以下コマンドでも確認可能です。

terraform output

最後に

ある程度整形できましたね。
terraformの基本文法では、各リソースをmodule化したり、サブネット定義をループして簡略化できたりと、まだまだ出来る事はあるのですが、今回はここまでにします。

2
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
2
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?