はじめに
前回の記事の続きです。
前回はTerraformでVPC内に踏み台サーバーとWebサーバーを構築しました。
今回は、Terraformの文法を学習すると同時に前回書いたコードを整形してみます。
前回で作成したコード
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
- main.tfに集約されたソースコードを分割します
- ハードコーディングされたIP等をvariables.tfで変数化します
- 自分のIPはterraform.tfvarsで外出し
- locals.tfに共通タグを定義します
- 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
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で変数化します
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に含めて、コミットしないようにしましょう。
my_ip = "自分のIP"
locals.tfに共通タグを定義します
現在、各リソースにNameだけを設定しましたが、共通のタグ属性を追加します。
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実行時、出力するようにします
# 踏み台のパブリック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化したり、サブネット定義をループして簡略化できたりと、まだまだ出来る事はあるのですが、今回はここまでにします。