Terraformの基本を学習しながら、実際にVPCやサブネットを作成していきましょう。
今回使用するコードは以下になります。ご参考まで!
前提
- 前回までの記事を読んでいること!
今回の目標
AWS内にネットワークを構築します。作成するリソースが複数あるので、2回に分けて記事を書いていきます。
- VPC
- サブネット
(ルートテーブルや各ゲートウェイは次回作成します。)
Terraformの準備をしよう!
まずは、Terraformの下準備をしていきます。
ディレクトリと空のtfファイルを作成する。
まずは、以下のようにディレクトリと空のファイルをそれぞれ作成してください。
( ~.tf
はTerraformの拡張子です。)
.
└── network
├── modules
│ ├── subnet.tf
│ ├── variables.tf
│ └── vpc.tf
└── staging
├── example.tfbackend
├── main.tf
└── variables.tf
ポイント
①module機能を使って機能毎にTerraformのコードを分割している。moduleを使ってコードを分割や共通化することで、コード量を減らしたりすることができます。
②staging/variables.tfで定義した変数をmodules以下でも使用する場合は、module/variables.tfにも定義することを忘れない。
例えば、
staging/variables.tf
variable "public_subnets" {
type = map(map(string))
default = {
public_a = {
}
public_c = {
}
}
}
public_subnetsはmoduleでサブネットを作成する時に使う変数です。そのためmoduleには以下のように定義しておく必要があります。
variable "public_subnets" { type = map(map(string)) }
今回は、staging/variables.tfの値を使用するため、変数名とタイプだけ定義しておけばOKです。
これは結構忘れがちです。「変数を定義したけど、変数が使われていない?」って時はここが怪しいので覚えておくと良いかも。
それではTerraformのコードを作成していきます。
main.tfを編集する。
Stateを管理するbackendブロックを作成していきます。
network/staging/main.tf
terraform {
backend "s3" {
}
}
backendの中身は、後ほど作成するstaging.tfbackendに記載するため↑のように空の状態で大丈夫です。
続いて、providerブロックを作成します。providerブロックでは、「どこのクラウドを使用するか?を設定する項目」とイメージしてもらうと分かりやすいかと。今回はAWSを利用します。
network/staging/main.tf
provider "aws" {
}
各種リソースはデフォルトで東京リージョンに作成しようと思います。
network/staging/main.tf
provider "aws" {
region = "ap-northeast-1"
}
Terraformはスイッチロールして実行しようと思います。(以前の記事でTerraformを実行するためのIAMロールを作成しました。スイッチ先のロールはそれを使います。)
network/staging/main.tf
provider "aws" {
region = "ap-northeast-1"
assume_role {
role_arn = var.iam_role_for_terraform
}
}
assume_role {}
はスイッチロールの設定になります。
role_arn
には、スイッチするIAMロールのARNを設定します。ここでは後程作成する変数を参照しています。(変数は var.変数名
で参照することができます。)
iam_role_for_terraform
は以下のファイルで variable "変数名" {}
を使用します。
network/staging/variables.tf
# iam_role_for_terraform が変数名。
variable "iam_role_for_terraform" {
# default = [設定したい値]
default = "arn:aws:iam::XXXXXXXXXXXX:role/study-infra-terraform-role"
}
今から作成するリソースにデフォルトで付与するタグを設定していきます。
まずは設定するタグを変数として宣言しておきます。
network/staging/variables.tf
variable "common_tags" {
# { Key = Value }形式で設定していくため map(string) のtypeを設定しています。
type = map(string)
default = {
Env = "staging"
System = "study-infra-tutorial"
}
}
[main.tf](http://main.tf)
に戻り default_tags {}
の設定を行います。
tags = {}
ではmap形式のため、 var.common_tags
を設定します。
network/staging/main.tf
default_tags {
tags = var.common_tags
}
vpc.tfを作成する。
VPC用のブロックを作成し、VPCのCIDRを設定します。
VPCのアドレスレンジは最短で「/16」です。
network/modules/vpc.tf
resource "aws_vpc" "study_infra" {
cidr_block = "20.0.0.0/16"
}
VPCのアドレスレンジを「/16」で指定した場合、作成できるサブネットやプライベートIPの数は以下のような感じです。
インスタンステナンシーはdefault(専有のハードウェアを使用せずに、共用を使用する)を指定します。
instance_tenancy = "default"
コンプライアンス要件などで占有サーバの指定がある場合に有効にすることがあります。
続いて、DNSの設定を行います。今後、Route53のプライベートホストゾーンを使用する予定があるため各DNSの設定は有効にします。(無効だとプライベートホストゾーンは使用できません。)
# DNS解決を利用するかどうかの設定。
# Route53でプライベートホストゾーンを使用する予定なので有効にする必要がある。
enable_dns_support = true
# パブリックIPアドレスを持つインスタンスにパブリックDNSを付与するかどうかの設定。
# Route53でプライベートホストゾーンを使用する予定なので有効にする必要がある。
enable_dns_hostnames = true
ipv6は使用しないため、無効にします。
assign_generated_ipv6_cidr_block = false
識別しやすいようにNameタグを付与します。
tags = {
Name = var.common_tags["System"]
}
以上をまとめると以下の内容になっているかと思います。
resource "aws_vpc" "study_infra" {
cidr_block = "20.0.0.0/16"
instance_tenancy = "default"
# DNS解決を利用するかどうかの設定。
# Route53でプライベートホストゾーンを使用する予定なので有効にする必要がある。
enable_dns_support = true
# パブリックIPアドレスを持つインスタンスにパブリックDNSを付与するかどうかの設定。
# Route53でプライベートホストゾーンを使用する予定なので有効にする必要がある。
enable_dns_hostnames = true
# ipv6は使用しないため無効。
assign_generated_ipv6_cidr_block = false
tags = {
Name = var.common_tags["System"]
}
}
subnet.tfを作成する。
サブネットを作成します。後で変更しやすいように各種設定値を変数にしようと思います。変数にする部分が長いので順を追って解説します。
サブネットは2つのAZそれぞれに3種類作成します。
パブリックサブネット:ELBを配置するサブネット。インターネット〜パブリックサブネット相互に通信可能。NATGatewayはパブリックサブネットに配置する予定。
プライベートサブネット:アプリサーバを配置するサブネット。インターネット側からはアクセス不可。プライベートサブネットからインターネットへのアクセスはNATGatewayを使用することで可能。
セキュアサブネット:DBサーバを配置するサブネット。インターネット側からはアクセス不可。またセキュアサブネットからインターネットへのアクセスも不可にします。
staging/variables.tf
variable "public_subnets" {
type = map(map(string))
default = {
public_a = {
}
public_c = {
}
}
}
variable "private_subnets" {
type = map(map(string))
default = {
private_a = {
}
private_c = {
}
}
}
variable "secure_subnets" {
type = map(map(string))
default = {
secure_a = {
}
secure_c = {
}
}
}
それぞれサブネットには以下の情報を設定しようと思います。
- 識別用のNameタグ。
- サブネットそれぞれのCIDR。
- サブネットの配置AZ。
- パブリックサブネットのみ、NATGatewayの配置有無。(NATGatewayは作成しておくだけで費用が発生するため選択できるようにしておきたいため。)
variable "public_subnets" {
type = map(map(string))
default = {
public_a = {
name = "public-a"
cidr = "20.0.1.0/24"
az = "ap-northeast-1a"
nat_gateway_count = 0
}
public_c = {
name = "public-c"
cidr = "20.0.2.0/24"
az = "ap-northeast-1c"
nat_gateway_count = 0
}
}
}
variable "private_subnets" {
type = map(map(string))
default = {
private_a = {
name = "private-a"
cidr = "20.0.3.0/24"
az = "ap-northeast-1a"
}
private_c = {
name = "private-c"
cidr = "20.0.4.0/24"
az = "ap-northeast-1c"
}
}
}
variable "secure_subnets" {
type = map(map(string))
default = {
secure_a = {
name = "secure-a"
cidr = "20.0.5.0/24"
az = "ap-northeast-1a"
}
secure_c = {
name = "secure-c"
cidr = "20.0.6.0/24"
az = "ap-northeast-1c"
}
}
}
mapの中にmap(string)を入れているので少し複雑になっています。
それではサブネットのリソースを作成していきます。
パブリック、プライベート、セキュアの3つのリソースを作成しますが、そこまで違いがないため、パブリックサブネットで解説します。
パブリックサブネットは2種類(AZ毎に)作成しますが、 for_each
を利用して効率的に書きます。
modules/subnet.tf
resource "aws_subnet" "public" {
for_each = var.pulic_subnets
availability_zone = each.value.az
cidr_block = each.value.cidr
}
for_each
では、mapの1セット毎に繰り返し処理を行ってくれます。 for_each
で取り出した値は、 each.value.[キー]
で取得できます。
for_each
を使わない場合は、以下のように書く必要があります。
resource "aws_subnet" "public_a" {
availability_zone = var.public_subnets.pulic_a["az"]
cidr_block = var.public_subnets.pulic_a["cidr"]
}
resource "aws_subnet" "public_c" {
availability_zone = var.public_subnets.pulic_c["az"]
cidr_block = var.public_subnets.pulic_c["cidr"]
}
(ね。便利でしょ?)
サブネットとVPCの紐付けを行い、Nameタグを付与します。
resource "aws_subnet" "public" {
for_each = var.public_subnets
vpc_id = aws_vpc.study_infra.id
availability_zone = each.value.az
cidr_block = each.value.cidr
tags = {
Name = each.value.name
}
}
これをプライベート、セキュアそれぞれ作成すると以下のようなコードになるかと思います。(プライベート、セキュアで参照する変数名が異なるのでご注意)
modules/subnet.tf
resource "aws_subnet" "public" {
for_each = var.public_subnets
vpc_id = aws_vpc.study_infra.id
availability_zone = each.value.az
cidr_block = each.value.cidr
tags = {
Name = each.value.name
}
}
resource "aws_subnet" "private" {
for_each = var.private_subnets
vpc_id = aws_vpc.study_infra.id
availability_zone = each.value.az
cidr_block = each.value.cidr
tags = {
Name = each.value.name
}
}
resource "aws_subnet" "secure" {
for_each = var.secure_subnets
vpc_id = aws_vpc.study_infra.id
availability_zone = each.value.az
cidr_block = each.value.cidr
tags = {
Name = each.value.name
}
}
以上でコードの作成が完了です。それでは実際にAWS上に構築していきましょう。
Terraformの実行
1.Terraformを初期化する。
$ docker run --rm -v $PWD/terraform:/tf -it [コンテナID] ash
$ cd /tf/network/stating
$ terraform init -backend-config=example.tfbackend
Initializing modules...
- network in ../modules
Initializing the backend...
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v3.68.0...
- Installed hashicorp/aws v3.68.0 (signed by HashiCorp)
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.
$
2. terraform plan
を実行する。
planの結果、エラーが無いようであればOKです。
$ terraform plan
...
Plan: 7 to add, 0 to change, 0 to destroy.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
$
3. terraform apply
を実行してリソースを構築する。
$ terraform apply
...
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes ←入力してEnterキーを押下!
...
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
$
4.AWSのWebコンソール画面にログインして確認してみましょう。
以上で、VPCとサブネットが構築できました。
次回は、NATGatewayやインターネットゲートウェイ、ルートテーブルを作成していきます。
これにて本記事は終了です。
コメント