Terraformの勉強メモ

このスクラップについて
書籍『実践Terraform AWSにおけるシステム設計とベストプラクティス』の学習を開始。
Terraformを使えるようになるためにTerraformのセットアップ時など、実行するコマンドまわりの記述が少し不親切に感じたので自分用にメモ。
※このスクラップでのページ数の表記はKindle版のそれに準拠しています。
教材のサンプルコード
この教材、その時々のファイル構成やコードの全体像に関する記載がなくって、シンプルに順序立ったハンズオンの形式を成していないのがちょっと辛いですねー。
どういう状態でterraform apply
すればいいのかよくわからなくて...。自分みたいな初心者だとその行間を読むのがなかなか難しいです。
余裕があればこれもやってみようかな...。

第1章
セットアップ
1.1 AWS
1.1.3 クレデンシャル
P.19
環境変数の設定
export AWS_ACCESS_KEY_ID=(※自分のアクセスキー ID)
export AWS_SECRET_ACCESS_KEY=(※シークレットアクセスキー)
export AWS_DEFAULT_REGION=ap-northeast-1
P.20
AWSアカウントIDの確認
aws sts get-caller-identity --query Account --output text
以下のように表示されればAWSアカウントIDの設定確認はOK。
123456789012
※AWSアカウントID(12桁の半角数字)が表示される。
1.1 Terraform
1.2.1 Homebrew
P.21
Terraformのインストール(Homebrew経由)
以下のコマンドでTerraformをインストールする。
brew install terraform
P.21
インストールしたTerraformのバージョンを確認
terraform --version
以下のように表示されればTerraformのインストールはOK。
Terraform v1.1.7
on darwin_amd64
1.2.2 tfenv
P.21
tfenvのインストール(Homebrew経由)
「tfenv」とは?
Terraformのバージョン管理ツールのこと。
Rubyの場合「rbenv」というバージョン管理ツールがあるが、それのTerraform版といったイメージ。
以下のコマンドでtfenvをインストールする。
brew install tfenv
エラーが出るので指示通りにコマンドを実行する。
brew unlink terraform
再度tfenvのインストールコマンドを実行。
brew install tfenv
P.21
インストールしたtfenvのバージョンを確認
tfenv --version
以下のように表示されればtfenvのインストールはOK。
tfenv 2.2.3
P.21
tfenvのインストール(補足)
一度Terraformをunlink
してしまったので、現状だとTerraformのバージョンが確認できない上にTerraformを使うこともできない。
なので今度はtfenvを使って改めてTerraform(最新版)をインストールする。
tfenv install latest
tfenvでインストールしたTerraformのバージョンを確認。
tfenv list
以下のように表示される。
1.1.7
tfenvで、使いたいTerraformのバージョンを指定する。
tfenv use 1.1.7
※tfenv list
で表示されたバージョンの数字を選ぶこと!
改めてTerraformのバージョンを確認する。
terraform --version
以下のように表示されれば、今度こそTerraformのインストールはOK。
Terraform v1.1.7
on darwin_amd64
P.22
以下のコマンドでインストール可能なTerraformのバージョンを確認することができる。
tfenv list-remote
対して、こちらのtfenv list
コマンドの場合はすでに自分のPCにインストール済みのTerraformのバージョンだけが表示される。
tfenv list
P.24
チーム開発を考慮した場合のTerraformのバージョン指定方法
.terraform-version
という名前のファイルを作成すればいいらしい。
これも.ruby-version
のTerraform版といったところか。
ファイルの内容はバージョンの数字だけでいいのかな?
1.1.7
※これは後で使用方法等検証する必要アリ。
1.2.3 Dockernized Terraform
ここからDockerも絡んでくる。
既存のdocker-compose.yml
を編集する必要がありそう。
本来であれば自身のポートフォリオのdocker-compose.yml
にTerraformの設定を追加、DockerでTerraformを動かしたいところではあるが、ちょっと今の自分の知識だと実装する自信がないのでここは後回し。
いずれ、api、front、dbと一緒にterraformもDockerで動かすように設定したい。
1.3 git-secrets
P.25
git-secretsのインストール(Homebrew経由)
「git-secrets」とは?
Gitの管理下にあるファイルの中に、認証情報などの第三者に知られてはならないデータが含まれていた場合、そのことを警告してくれるツールのこと。
以下のコマンドでgit-secretsをインストールする。
brew install git-secrets
git-secretsのインストールが完了したら、続けて自身の開発環境(PC)のGitにgit-secretsの設定を追加する。以下3つのコマンドを実行。
git secrets --register-aws --global
git secrets --install ~/.gittemplates/git-secrets
git config --global init.templatedir '~/.git-templates/git-secrets'
※これも後で動作を検証する必要アリ。

第2章
基本操作
2.1 リソースの作成
※ここから、自身のポートフォリオにTerraformを実装する体で作業を進めていく。
P.27
Terraformのリソースの作成ことはじめ
※ターミナルで自身のアプリのルートディレクトリに移動しておく。
Terraform用のディレクトリ(今回はterraform
ディレクトリ)を作成する。
mkdir terraform
作成したterraform
ディレクトリに移動。
cd terraform
main.tf
というファイルを作成する。
touch main.tf
2.1.1 HCL(HashiCorp Configuration Language)
作成したmain.tf
ファイルをエディターで開き、以下のように編集する。
リスト2.1:EC2インスタンスの定義
resource "aws_instance" "example" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
※このサンプルコードは、Amazon Linux 2のAMIをベースにEC2インスタンスを作成する内容となっている。
2.1.2 terraform init
P.28
main.tf
を編集したら以下のコマンドを実行し、リソース作成に必要なバイナリファイルをダウンロードする。
以下のコマンドでバイナリファイルをダウンロードする。
terraform init
2.1.3 terraform plan
P.28
terraform plan
コマンドの実行。
Terraformの実行計画を出力する。
terraform plan
2.1.4 terraform apply
P.29
terraform apply
コマンドの実行。
Terraformの実行を確認する。
terraform apply
terraform apply
コマンドの実行後、以下のように表示される。
Enter a value:
yes
と入力してEnterキーを押す。
Chromeなどブラウザを立ち上げ自身のAWSアカウントでAWSマネジメントコンソールにログインし、EC2の管理画面へと移動。TerraformによってEC2インスタンスが作成されたかどうか確認する。
※このときAWSのリージョンを特に指定していないが、たとえば自身の作業地域が日本の関東圏であれば、Terraformがいい感じに判断して自動的に東京リージョン、「アジアパシフィック(東京)ap-northeast-1」にEC2インスタンスを作成してくれるっぽい。
2.2 リソースの更新
2.2.1 リソースの設定変更
P.30
main.tf
を更新する。
main.tf
ファイルを以下のように編集する。
リスト2.2:タグを追加
resource "aws_instance" "example" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
tags = {
Name = "example"
}
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
※ここではタグを追加している。
main.tf
ファイルを編集したらterraform apply
コマンドを実行。リソースに更新内容を反映させる。
terraform apply
更新の確認コマンドが表示される。
Enter a value:
ここでもyes
と入力しEnterキーを押して反映。
AWSコンソールでEC2を確認。インスタンスに「example」という名前が付いていればOK。
2.2.2 リソースの再作成
P.33
インスタンスにApacheをインストールする。
main.tf
ファイルを以下のように編集する。
リスト2.3:User DataでApacheをインストール
resource "aws_instance" "example" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
user_data = <<EOF
#!/bin/bash
yum install -y httpd
systemctl start httpd.service
EOF
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
※Apacheをインストールする設定を追加。
編集内容を反映させるためterraform apply
コマンドを実行。
terraform apply
yes
と入力しEnterキーを押して反映。
AWSコンソールでEC2を確認。最初に作成されたリソースが終了・削除され、新しいリソースが作り直されている。
2.3 tfstateファイル
2.3.1 リソースの設定変更
P.34
※tfstateファイルについての説明。
2.4 リソースの削除
P.36
作成したリソースを削除する方法。
terraform destroy
コマンドを実行することでリソースを削除することができる。
terraform destroy
yes
と入力しEnterキーを押して反映。
AWSコンソールでEC2を確認。作成したリソースがすべて終了・削除されている。

第3章
基本構文
3.1 変数
P.39
「変数」の定義。
※Terraformにおける「変数」についての説明。
variable
を使うと変数を定義することができる。
3.2 ローカル変数
P.40
「ローカル変数」の定義。
※Terraformにおける「ローカル変数」についての説明。
locals
を使うと変数を定義することができる。
3.3 出力値
P.40
「出力値」の定義。
※Terraformにおける「出力値」についての説明。
output
を使うと変数を定義することができる。
3.4 データソース
P.42
※「データソース」についての説明。
データソースを使うと外部データを参照できる。
3.5 プロバイダ
P.42
※「プロバイダ」についての説明。
AWSやGCP、Azureなど、クラウドサービスは複数存在するが、それらAPIの違いを吸収するのがプロバイダの役割。
3.6 参照
P.44
作成したインスタンスにブラウザでアクセスする。
ここまでの設定ではインスタンスにアクセスできない。
アクセスするにはセキュリティグループが必要。
main.tf
ファイルを以下のように編集する。
リスト3.6:EC2向けセキュリティグループの定義
リスト3.7:EC2にセキュリティグループを追加
resource "aws_security_group" "example_ec2" {
name = "example-ec2"
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"]
}
}
resource "aws_instance" "example" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.example_ec2.id]
user_data = <<EOF
#!/bin/bash
yum install -y httpd
systemctl start httpd.service
EOF
}
output "example_public_dns" {
value = aws_instance.example.public_dns
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
※80番ポートへのアクセスを許可する設定(セキュリティグループ)を追加。
※ここは読んでいてすごくわかり辛かったが、どうやらmain.tf
ファイルにリスト3.6のコードとリスト3.7のコードを合わせて記述すればいいらしい。
main.tf
ファイルを編集したらterraform apply
コマンドを実行。リソースを新たに作成する。
terraform apply
yes
と入力しEnterキーを押してリソースの作成を実行。
リソースの作成が完了したら、以下のように表示される。
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
example_public_dns = "ec2-XX-XX-XXX-XXX.ap-northeast-1.compute.amazonaws.com"
example_public_dns
に出力されているアドレスec2-XX-XX-XXX-XXX.ap-northeast-1.compute.amazonaws.com
(「X」の部分には実際には数字が入っている)をコピーし、ブラウザでアクセスしてみる。Apacheの「Test Page」というタイトルのページが表示されればOK。
Terraformで作成したリソースをブラウザで参照できるようになった。
3.7 組み込み関数
P.46
Terraformには、「文字列操作」や「コレクション操作」など、よくある処理が組み込み関数として用意されている。
具体例
外部ファイルを読み込むfile関数
「file関数」を試す。
試しにApacheのインストールをするためのuser_data.sh
を作成。
touch user_data.sh
これを外部ファイルとして読み込んで使ってみる。
作成したuser_data.sh
ファイルを以下のように編集する。
リスト3.8:Apacheのインストールスクリプト
#!/bin/bash
yum install -y httpd
systemctl start httpd.service
ソース:example-pragmatic-terraform/user_data.sh at main · tmknom/example-pragmatic-terraform
続けてmain.tf
ファイルを以下のように編集する。
リスト3.9:Apacheのインストールスクリプトをファイル読み込み
resource "aws_instance" "example" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
user_data = file("./user_data.sh")
}
ソース:example-pragmatic-terraform/9.tf at main · tmknom/example-pragmatic-terraform
user_data.sh
ファイルとmain.tf
ファイルの編集が済んだらterraform apply
コマンドを実行。リソースを新たに作成する。
terraform apply
yes
と入力しEnterキーを押してリソースの作成を実行。
AWSコンソールでEC2を確認。作成したリソースに...。
※なぜかここの処理はうまくいかなかった。リソースの作成処理をキャンセルして次の項目へ...。うまく確認できなかったけどしょうがないので飛ばす。
3.8 モジュール
P.47
※「モジュール」についての説明。
モジュールは別ディレクトリで作成する必要がある。
「モジュール」を試す。
試しにHTTPサーバーのモジュールを実装する。まずはhttp_server
ディレクトリを作成。
mkdir http_server
作成したhttp_server
ディレクトリに移動する。
cd http_server
http_server
ディレクトリ内にmain.tf
ファイルを作成する。
touch main.tf
3.8.1 モジュールの定義
main.tf
ファイルを以下のように編集する。
リスト3.10:HTTPサーバーモジュールの定義
variable "instance_type" {}
resource "aws_instance" "default" {
ami = "ami-0c3fd0f5d33134a76"
vpc_security_group_ids = [aws_security_group.default.id]
instance_type = var.instance_type
user_data = <<EOF
#!/bin/bash
yum install -y httpd
systemctl start httpd.service
EOF
}
resource "aws_security_group" "default" {
name = "ec2"
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"]
}
}
output "public_dns" {
value = aws_instance.default.public_dns
}
ソース:example-pragmatic-terraform/10.tf at main · tmknom/example-pragmatic-terraform
3.8.2 モジュールの利用
ターミナルのカレントディレクトリを一度terraform
ディレクトリに戻す。
cd ../
モジュールを利用する側のmain.tf
ファイルを編集する。
terraform
ディレクトリ直下のmain.tf
ファイルを以下のように編集する。
リスト3.11:HTTPサーバーモジュールの利用
module "web_server" {
source = "./http_server"
instance_type = "t3.micro"
}
output "public_dns" {
value = module.web_server.public_dns
}
ソース:example-pragmatic-terraform/11.tf at main · tmknom/example-pragmatic-terraform
main.tf
ファイルを編集したら、ターミナルのカレントディレクトリがterraform
であること(モジュールを利用する側のmain.tf
ファイルが置いてあるディレクトリ)をよく確認し、それからterraform apply
コマンドを実行。リソースを新たに作成する。
しかし今回はterraform apply
コマンドの前にterraform get
コマンドを実行する。このコマンドによってモジュールを事前に取得する必要がある。
terraform get
そしてterraform apply
コマンドを実行。
terraform apply
yes
と入力しEnterキーを押してリソースの作成を実行。
リソースの作成が完了したら、以下のように表示される。
Apply complete! Resources: 2 added, 0 changed, 2 destroyed.
Outputs:
public_dns = "ec2-XX-XX-XXX-XXX.ap-northeast-1.compute.amazonaws.com"
example_public_dns
に出力されているアドレスec2-XX-XX-XXX-XXX.ap-northeast-1.compute.amazonaws.com
(「X」の部分には実際には数字が入っている)をコピーし、ブラウザでアクセスしてみる。Apacheの「Test Page」というタイトルのページが表示されればOK。
Terraformのモジュール機能をうまく使えていることが確認できた。

第4章
全体設計
4.1 システム要件
P.53
本格的なTerraformの実装を体験する。
次の第5章から第16章にかけたハンズオンになる模様。
※以降のセクションは第5章から始まるハンズオンの説明となる。
4.2 アーキテクチャ設計
4.3 テクノロジースタック
4.4 ファイルレイアウト

第5章
権限管理
5.1 ポリシー
5.1.1 ポリシードキュメント
P.57
AWSのサービス利用権限は「ポリシー」で定義・制御する。
そしてポリシーは「ポリシードキュメント」というJSON形式のファイルで作成する。
JSON形式のポリシードキュメントの例
リスト5.1:JSON形式のポリシードキュメント
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ec2:DescribeRegions"],
"Resource": ["*"]
}
]
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
各要素の説明
- Effect:Allow(許可)、または、Deny(拒否)
- Action:何のサービスで、どんな操作が実行できるか
- Resource:操作可能なリソースは何か
5.1.2 IAMポリシー
P.58
※IAMポリシーの定義方法についての説明。
リスト5.2:ポリシードキュメントの定義
resource "aws_iam_policy" "example" {
name = "example"
policy = data.aws_iam_policy_document.allow_describe_regions.json
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
5.2 ロール
5.2.1 信頼ポリシー
P.59
※IAMロールによる権限管理の説明。「信頼ポリシー」の定義方法について。
5.2.2 IAMロール
P.60
※IAMロールによる権限管理の説明。「IAMロール」の定義方法について。
5.2.3 IAMポリシーのアタッチ
P.60
※IAMロールへのIAMポリシーの関連付けについての説明。
5.2.4 IAMロールのモジュール化
P.61
※IAMロールをモジュール化する方法の説明。
IAMロールモジュールで使われる3つの入力パラメータについて
- name:IAMロールとIAMポリシーの名前
- policy:ポリシードキュメント
- identifier:IAMロールを関連付けるAWSのサービスの識別子
terraform
ディレクトリにいる状態で、IAMロールモジュールのファイルを置くためのiam_role
ディレクトリを作成する。
mkdir iam_role
作成したiam_role
ディレクトリに移動。
cd iam_role
IAMロールのモジュール化を定義するファイルを作成する。ファイル名はiam_role.tf
とする。
touch iam_role.tf
作成したiam_role.tf
ファイルをエディターで開き、以下のように編集する。
リスト5.7:IAMロールモジュールの定義
# IAM Role Module
variable "name" {}
variable "policy" {}
variable "identifier" {}
resource "aws_iam_role" "default" {
name = var.name
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [var.identifier]
}
}
}
resource "aws_iam_policy" "default" {
name = var.name
policy = var.policy
}
resource "aws_iam_role_policy_attachment" "default" {
role = aws_iam_role.default.name
policy_arn = aws_iam_policy.default.arn
}
output "iam_role_arn" {
value = aws_iam_role.default.arn
}
output "iam_role_name" {
value = aws_iam_role.default.name
}
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
P.62
IAMロールモジュールの利用
iam.tf
ファイルで定義したIAMロールモジュールは、IAMロールを使いたいファイルで以下のように実装する。
リスト5.8:IAMロールモジュールの利用
module "describe_regions_for_ec2" {
source = "./iam_role/iam_role"
name = "describe-regions-for-ec2"
identifier = "ec2.amazonaws.com"
policy = data.aws_iam_policy_document.allow_describe_regions.json
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
このhogehoge.tf
ファイルでは、
-
source
でiam_role.tf
を呼び出して(ここは、このhogehoge.tf
ファイルから見た、iam_role.tf
の相対パス) -
name
でこの新しいIAMロールに名前を付け -
identifier
でIAMロールを関連付けるAWSサービスを選択し、 -
policy
で具体的なポリシー内容を記述
している。

第6章
ストレージ
6.1 プライベートバケット
6.1.1 S3バケット
P.64
S3バケット(外部公開しないプライベートバケット)の作成方法。
まず、iam_role
ディレクトリからterraform
ディレクトリに戻る。
cd ../
そしてterraform
ディレクトリにいる状態で、terraform
ディレクトリ直下にs3.tf
ファイルを作成する。
touch s3.tf
作成したs3.tf
ファイルをエディターで開き、以下のように編集する。
リスト6.1:プライベートバケットの定義
resource "aws_s3_bucket" "private" {
bucket = "private-pragmatic-terraform"
versioning {
enabled = true
}
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
※S3バケットの命名規則に乗っ取り、このファイルのbucket
に指定するバケット名は「全リージョンにおいてユニークな名前」にしなければならない。
6.1.2 ブロックパブリックアクセス
P.65
S3バケットへの「ブロックパブリックアクセス」の設定方法。
以下のコードを追記する。
リスト6.2:ブロックパブリックアクセスの定義
resource "aws_s3_bucket_public_access_block" "private" {
bucket = aws_s3_bucket.private.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
6.2 パブリックバケット
P.66
S3バケット(外部公開するパブリックバケット)の作成方法。
s3.tf
ファイルを以下のように編集する。
リスト6.3:パブリックバケットの定義
resource "aws_s3_bucket" "public" {
bucket = "public-pragmatic-terraform"
acl = "public-read"
cors_rule {
allowed_origins = ["https://example.com"]
allowed_methods = ["GET"]
allowed_headers = ["*"]
max_age_seconds = 3000
}
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
6.3 ログバケット
6.3.1 ログテーションバケット
P.67
※AWSの各種サービスのログを保存するための「ログバケット」についての説明。
6.3.2 バケットポリシー
P.68
※AWSのサービスからS3へのアクセス権を制御する「バケットポリシー」。それのtfファイルの作成方法についての説明。
P.69
※バケットの削除方法についての説明。

第7章(その1)
ネットワーク
7.1 パブリックネットワーク
7.1.1 VPC(Virtual Private Cloud)
P.71
VPCの作成方法。
terraform
ディレクトリ直下にnetwork.tf
ファイルを作成する。
touch network.tf
作成したnetwork.tf
ファイルをエディターで開き、以下のように編集する。
リスト7.1:VPCの定義
# VPC
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "example"
}
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
7.1.2 パブリックサブネット
P.72
パブリックサブネットの作成方法。
引き続きnetwork.tf
ファイルを編集。パブリックサブネットを作成するために以下のコードを追加する。
リスト7.2:パブリックネットの定義
# Public Subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.0.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-northeast-1a"
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
7.1.3 インターネットゲートウェイ
P.74
インターネットゲートウェイの作成方法。
引き続きnetwork.tf
ファイルを編集。インターネットゲートウェイを作成するために以下のコードを追加する。
リスト7.3:インターネットゲートウェイの定義
# Internet Gateway
resource "aws_internet_gateway" "example" {
vpc_id = aws_vpc.example.id
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
7.1.4 ルートテーブル
P.74
ルートテーブル(パブリックサブネット用)の作成方法。
引き続きnetwork.tf
ファイルを編集。パブリックサブネット用のルートテーブルを作成するために以下のコードを追加する。
リスト7.4:ルートテーブルの定義
# Route Table for Public Subnet
resource "aws_route_table" "public" {
vpc_id = aws_vpc.example.id
}
ソース:example-pragmatic-terraform/4.tf at main · tmknom/example-pragmatic-terraform
ルート
P.75
パブリックサブネットのルートの定義方法。
引き続きnetwork.tf
ファイルを編集。パブリックサブネットのルートを定義するために以下のコードを追加する。
リスト7.5:ルートの定義
# Route for Public Subnet
resource "aws_route" "public" {
route_table_id = aws_route_table.public.id
gateway_id = aws_internet_gateway.example.id
destination_cidr_block = "0.0.0.0/0"
}
ソース:example-pragmatic-terraform/5.tf at main · tmknom/example-pragmatic-terraform
ルートテーブルの関連付け
P.75
ルートテーブルの関連付け方法(パブリックサブネット)。
引き続きnetwork.tf
ファイルを編集。ルートテーブルを関連付けるために以下のコードを追加する。
リスト7.6:ルートテーブルの関連付け
# Route Table Association for Public Subnet
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
7.2 プライベートネットワーク
7.2.1 プライベートサブネット
P.77
プライベートサブネットの作成方法。
引き続きnetwork.tf
ファイルを編集。プライベートサブネットを作成するために以下のコードを追加する。
リスト7.7:プライベートサブネットの定義
# Private Subnet
resource "aws_subnet" "private" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.64.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = false
}
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
ルートテーブルの関連付け
P.78
ルートテーブル(プライベートサブネット用)の作成方法。
引き続きnetwork.tf
ファイルを編集。プライベートサブネット用のルートテーブルを作成するために以下のコードを追加する。
リスト7.8:プライベートルートテーブルと関連付けの定義
# Route Table for Private Subnet
resource "aws_route_table" "private" {
vpc_id = aws_vpc.example.id
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
P.78
ルートテーブルの関連付け方法(プライベートサブネット)。
引き続きnetwork.tf
ファイルを編集。ルートテーブルを関連付けるために以下のコードを追加する。
リスト7.8:プライベートルートテーブルと関連付けの定義
# Route Table Association for Private Subnet
resource "aws_route_table_association" "private" {
subnet_id = aws_subnet.private.id
route_table_id = aws_route_table.private.id
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
7.2.2 NATゲートウェイ
P.78
NATゲートウェイの作成方法。
引き続きnetwork.tf
ファイルを編集。NATゲートウェイを作成する下準備として、まずは「Elastic IP アドレス」を設定する必要がある。以下のコードを追加する。
リスト7.9:EIPの定義
# Elastic IP Address
resource "aws_eip" "nat_gateway" {
vpc = true
depends_on = [aws_internet_gateway.example]
}
ソース:example-pragmatic-terraform/9.tf at main · tmknom/example-pragmatic-terraform
Elastic IP アドレスを設定できたら、今度こそNATゲートウェイを作成。以下のコードを追加する。
リスト7.10:NATゲートウェイの定義
# NAT Gateway
resource "aws_nat_gateway" "example" {
allocation_id = aws_eip.nat_gateway.id
subnet_id = aws_subnet.public.id
depends_on = [aws_internet_gateway.example]
}
ソース:example-pragmatic-terraform/10.tf at main · tmknom/example-pragmatic-terraform
※NATゲートウェイは、プライベートサブネットに配置すること!
ルート
P.80
プライベートサブネットのルートの定義方法。
引き続きnetwork.tf
ファイルを編集。プライベートサブネットのルートを定義するために以下のコードを追加する。
リスト7.11:プライベートのルートの定義
# Route for Private Subnet
resource "aws_route" "private" {
route_table_id = aws_route_table.private.id
nat_gateway_id = aws_nat_gateway.example.id
destination_cidr_block = "0.0.0.0/0"
}
ソース:example-pragmatic-terraform/11.tf at main · tmknom/example-pragmatic-terraform
7.2.3 暗黙的な依存関係
P.80
- Elastic IP アドレス
- NATゲートウェイ
以上の2つは、暗黙的にインターネットゲートウェイに依存している。
そのためどちらのコードでもdepends_on = [aws_internet_gateway.example]
を定義する必要がある。
7.3 マルチAZ
7.3.1 パブリックネットワークのマルチAZ化
P.81
サブネット(パブリックサブネットのマルチAZ化)
ネットワークをマルチAZ化するために、複数のアベイラビリティゾーンにサブネットを作成する。
パブリックサブネットとプライベートサブネットとをそれぞれ追加で作成する。
パブリックサブネットの作成用に追加した以下のコードを
リスト7.2:パブリックネットの定義
# Public Subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.0.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-northeast-1a"
}
次のように変更する。
リスト7.12:プライベートサブネットのマルチAZ化
# Public Subnet 0
resource "aws_subnet" "public_0" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
}
# Public Subnet 1
resource "aws_subnet" "public_1" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-1c"
map_public_ip_on_launch = true
}
ソース:example-pragmatic-terraform/12.tf at main · tmknom/example-pragmatic-terraform
※「Public Subnet 0」はap-northeast-1a
へ、「Public Subnet 1」はap-northeast-1c
へと、それぞれ異なるAZにサブネットを作成するよう設定し直している。
P.82
ルートテーブルの関連付け(パブリックサブネットのマルチAZ化)
ネットワークをマルチAZ化するために、複数のアベイラビリティゾーンにサブネットを作成する。
パブリックサブネットとプライベートサブネットとをそれぞれ追加で作成する。
パブリックサブネットにルートテーブルを関連付けるために追加した以下のコードを
リスト7.6:ルートテーブルの関連付け
# Route Table Association for Public Subnet
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
次のように変更する。
リスト7.13:パブリックサブネットとルートテーブルの関連付けをマルチAZ化
# Route Table Association for Public Subnet 0
resource "aws_route_table_association" "public_0" {
subnet_id = aws_subnet.public_0.id
route_table_id = aws_route_table.public.id
}
# Route Table Association for Public Subnet 1
resource "aws_route_table_association" "public_1" {
subnet_id = aws_subnet.public_1.id
route_table_id = aws_route_table.public.id
}
ソース:example-pragmatic-terraform/13.tf at main · tmknom/example-pragmatic-terraform
7.3.2 プライベートネットワークのマルチAZ化
P.82
サブネット(プライベートサブネットのマルチAZ化)
プライベートサブネットの作成用に追加した以下のコードを
リスト7.7:プライベートサブネットの定義
# Private Subnet
resource "aws_subnet" "private" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.64.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = false
}
次のように変更する。
リスト7.14:プライベートサブネットのマルチAZ化
# Private Subnet 0
resource "aws_subnet" "private_0" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.65.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = false
}
# Private Subnet 1
resource "aws_subnet" "private_1" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.66.0/24"
availability_zone = "ap-northeast-1c"
map_public_ip_on_launch = false
}
ソース:example-pragmatic-terraform/14.tf at main · tmknom/example-pragmatic-terraform
P.83
NATゲートウェイ(プライベートサブネットのマルチAZ化)
マルチAZ化をする場合には、アベイラビリティゾーンごとにNATゲートウェイを作成する。
NATゲートウェイの作成用に追加した以下のコードを
リスト7.9:EIPの定義
リスト7.10:NATゲートウェイの定義
# Elastic IP Address
resource "aws_eip" "nat_gateway" {
vpc = true
depends_on = [aws_internet_gateway.example]
}
# NAT Gateway
resource "aws_nat_gateway" "example" {
allocation_id = aws_eip.nat_gateway.id
subnet_id = aws_subnet.public.id
depends_on = [aws_internet_gateway.example]
}
次のように変更する。
リスト7.15:NATゲートウェイのマルチAZ化
# Elastic IP Address 0
resource "aws_eip" "nat_gateway_0" {
vpc = true
depends_on = [aws_internet_gateway.example]
}
# Elastic IP Address 1
resource "aws_eip" "nat_gateway_1" {
vpc = true
depends_on = [aws_internet_gateway.example]
}
# NAT Gateway 0
resource "aws_nat_gateway" "nat_gateway_0" {
allocation_id = aws_eip.nat_gateway_0.id
subnet_id = aws_subnet.public_0.id
depends_on = [aws_internet_gateway.example]
}
# NAT Gateway 1
resource "aws_nat_gateway" "nat_gateway_1" {
allocation_id = aws_eip.nat_gateway_1.id
subnet_id = aws_subnet.public_1.id
depends_on = [aws_internet_gateway.example]
}
ソース:example-pragmatic-terraform/15.tf at main · tmknom/example-pragmatic-terraform
P.84
ルートテーブル(プライベートサブネットのマルチAZ化)
NATゲートウェイと同様に、マルチAZ化する場合にはルートテーブルもアベイラビリティゾーンごとに作成する。
プライベートサブネット用のルートテーブル作成用に追加した以下のコードを
リスト7.8:プライベートルートテーブルと関連付けの定義
リスト7.11:プライベートのルートの定義
# Route Table for Private Subnet
resource "aws_route_table" "private" {
vpc_id = aws_vpc.example.id
}
# Route for Private Subnet
resource "aws_route" "private" {
route_table_id = aws_route_table.private.id
nat_gateway_id = aws_nat_gateway.example.id
destination_cidr_block = "0.0.0.0/0"
}
# Route Table Association for Private Subnet
resource "aws_route_table_association" "private" {
subnet_id = aws_subnet.private.id
route_table_id = aws_route_table.private.id
}
次のように変更する。
リスト7.16:プライベートサブネットのルートテーブルのマルチAZ化
# Route Table for Private Subnet 0
resource "aws_route_table" "private_0" {
vpc_id = aws_vpc.example.id
}
# Route Table for Private Subnet 1
resource "aws_route_table" "private_1" {
vpc_id = aws_vpc.example.id
}
# Route for Private Subnet 0
resource "aws_route" "private_0" {
route_table_id = aws_route_table.private_0.id
nat_gateway_id = aws_nat_gateway.nat_gateway_0.id
destination_cidr_block = "0.0.0.0/0"
}
# Route for Private Subnet 1
resource "aws_route" "private_1" {
route_table_id = aws_route_table.private_1.id
nat_gateway_id = aws_nat_gateway.nat_gateway_1.id
destination_cidr_block = "0.0.0.0/0"
}
# Route Table Association for Private Subnet 0
resource "aws_route_table_association" "private_0" {
subnet_id = aws_subnet.private_0.id
route_table_id = aws_route_table.private_0.id
}
# Route Table Association for Private Subnet 1
resource "aws_route_table_association" "private_1" {
subnet_id = aws_subnet.private_1.id
route_table_id = aws_route_table.private_1.id
}
ソース:example-pragmatic-terraform/16.tf at main · tmknom/example-pragmatic-terraform
ここまで編集してみて、network.tf
ファイルの中身は最終的に以下のようになった。
# VPC
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "example"
}
}
# Internet Gateway
resource "aws_internet_gateway" "example" {
vpc_id = aws_vpc.example.id
}
# Public Subnet 0
resource "aws_subnet" "public_0" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
}
# Public Subnet 1
resource "aws_subnet" "public_1" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-1c"
map_public_ip_on_launch = true
}
# Route Table for Public Subnet
resource "aws_route_table" "public" {
vpc_id = aws_vpc.example.id
}
# Route for Public Subnet
resource "aws_route" "public" {
route_table_id = aws_route_table.public.id
gateway_id = aws_internet_gateway.example.id
destination_cidr_block = "0.0.0.0/0"
}
# Route Table Association for Public Subnet 0
resource "aws_route_table_association" "public_0" {
subnet_id = aws_subnet.public_0.id
route_table_id = aws_route_table.public.id
}
# Route Table Association for Public Subnet 1
resource "aws_route_table_association" "public_1" {
subnet_id = aws_subnet.public_1.id
route_table_id = aws_route_table.public.id
}
# Elastic IP Address 0
resource "aws_eip" "nat_gateway_0" {
vpc = true
depends_on = [aws_internet_gateway.example]
}
# Elastic IP Address 1
resource "aws_eip" "nat_gateway_1" {
vpc = true
depends_on = [aws_internet_gateway.example]
}
# NAT Gateway 0
resource "aws_nat_gateway" "nat_gateway_0" {
allocation_id = aws_eip.nat_gateway_0.id
subnet_id = aws_subnet.public_0.id
depends_on = [aws_internet_gateway.example]
}
# NAT Gateway 1
resource "aws_nat_gateway" "nat_gateway_1" {
allocation_id = aws_eip.nat_gateway_1.id
subnet_id = aws_subnet.public_1.id
depends_on = [aws_internet_gateway.example]
}
# Private Subnet 0
resource "aws_subnet" "private_0" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.65.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = false
}
# Private Subnet 1
resource "aws_subnet" "private_1" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.66.0/24"
availability_zone = "ap-northeast-1c"
map_public_ip_on_launch = false
}
# Route Table for Private Subnet 0
resource "aws_route_table" "private_0" {
vpc_id = aws_vpc.example.id
}
# Route Table for Private Subnet 1
resource "aws_route_table" "private_1" {
vpc_id = aws_vpc.example.id
}
# Route for Private Subnet 0
resource "aws_route" "private_0" {
route_table_id = aws_route_table.private_0.id
nat_gateway_id = aws_nat_gateway.nat_gateway_0.id
destination_cidr_block = "0.0.0.0/0"
}
# Route for Private Subnet 1
resource "aws_route" "private_1" {
route_table_id = aws_route_table.private_1.id
nat_gateway_id = aws_nat_gateway.nat_gateway_1.id
destination_cidr_block = "0.0.0.0/0"
}
# Route Table Association for Private Subnet 0
resource "aws_route_table_association" "private_0" {
subnet_id = aws_subnet.private_0.id
route_table_id = aws_route_table.private_0.id
}
# Route Table Association for Private Subnet 1
resource "aws_route_table_association" "private_1" {
subnet_id = aws_subnet.private_1.id
route_table_id = aws_route_table.private_1.id
}

第7章(その2)
ネットワーク
7.4 ファイアウォール
7.4.1 セキュリティグループ
P.86
AWSのファイアウォールの種類(2種類)
- ネットワークACL(サブネットレベルで動作する)
- セキュリティグループ(インスタンスレベルで動作する)
セキュリティグループ
「セキュリティグループ」と「セキュリティグループルール」は分けて書くこともできる。
※ここからは「セキュリティグループ」の話になる。
※これまで編集してきたnetwork.tf
ファイルとは別に、security_group_sample.tf
ファイルを新たに作成。このsecurity_group_sample.tf
ファイルを編集していく。
※セキュリティグループはインスタンスに設定するものなので、本来はec2.tf
ファイルやecs.tf
ファイルなど、インスタンスを作成するtf
ファイルに記述すべき内容である(が、今回は学習用としてsecurity_group_sample.tf
ファイルに色々書いていく)。
terraform
ディレクトリ直下にsecurity_group_sample.tf
ファイルを作成する。
touch security_group_sample.tf
まずは「セキュリティグループ」を実装する。
作成したsecurity_group_sample.tf
ファイルをエディターで開き、以下のように編集する。
リスト7.17:セキュリティグループの定義
# Security Group
resource "aws_security_group" "example" {
name = "example"
vpc_id = aws_vpc.example.id
}
ソース:example-pragmatic-terraform/17.tf at main · tmknom/example-pragmatic-terraform
セキュリティグループルール(インバウンド)
続いて「セキュリティグループルール(インバウンド)」を実装。security_group_sample.tf
ファイルに以下のコードを追記する。
リスト7.18:セキュリティグループルール(インバウンド)の定義
# Security Group Rule (Inbound)
resource "aws_security_group_rule" "ingress_example" {
type = "ingress"
from_port = "80"
to_port = "80"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.example.id
}
ソース:example-pragmatic-terraform/18.tf at main · tmknom/example-pragmatic-terraform
セキュリティグループルール(アウトバウンド)
次は「セキュリティグループルール(アウトバウンド)」を実装。security_group_sample.tf
ファイルに以下のコードを追記する。
リスト7.19:セキュリティグループルール(アウトバウンド)の定義
# Security Group Rule (Outbound)
resource "aws_security_group_rule" "egress_example" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.example.id
}
ソース:example-pragmatic-terraform/19.tf at main · tmknom/example-pragmatic-terraform
7.4.2 セキュリティグループのモジュール化
P.88
※セキュリティグループをモジュール化する方法の説明。
セキュリティグループモジュールで使われる4つの入力パラメータについて
- name:セキュリティグループの名前
- vpc_id:VPCのID
- port:通信を許可するポート番号
- cidr_blocks:通信を許可するCIDRブロック
terraform
ディレクトリにいる状態で、セキュリティグループモジュールのファイルを置くためのsecurity_group
ディレクトリを作成する。
mkdir security_group
作成したsecurity_group
ディレクトリに移動。
cd security_group
セキュリティグループのモジュール化を定義するファイルを作成する。ファイル名はsecurity_group.tf
とする。
touch security_group.tf
作成したsecurity_group.tf
ファイルをエディターで開き、以下のように編集する。
リスト7.20:セキュリティグループモジュールの定義
# Security Group Module
variable "name" {}
variable "vpc_id" {}
variable "port" {}
variable "cidr_blocks" {
type = list(string)
}
resource "aws_security_group" "default" {
name = var.name
vpc_id = var.vpc_id
}
resource "aws_security_group_rule" "ingress" {
type = "ingress"
from_port = var.port
to_port = var.port
protocol = "tcp"
cidr_blocks = var.cidr_blocks
security_group_id = aws_security_group.default.id
}
resource "aws_security_group_rule" "egress" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.default.id
}
output "security_group_id" {
value = aws_security_group.default.id
}
ソース:example-pragmatic-terraform/20.tf at main · tmknom/example-pragmatic-terraform
P.89
セキュリティグループモジュールの利用
security_group.tf
ファイルで定義したセキュリティグループモジュールは、セキュリティグループを使いたいファイルで以下のように実装する。
リスト7.21:セキュリティグループモジュールの利用
module "example_sg" {
source = "./security_group/security_group"
name = "module-sg"
vpc_id = aws_vpc.example.id
port = 80
cidr_blocks = ["0.0.0.0/0"]
}
ソース:example-pragmatic-terraform/21.tf at main · tmknom/example-pragmatic-terraform
このhogehoge.tf
ファイルでは、
-
source
でsecurity_group.tf
を呼び出して(ここは、このhogehoge.tf
ファイルから見た、security_group.tf
の相対パス) -
name
でこの新しいセキュリティグループに名前を付け -
vpc_id
でVPCのIDを設定 -
port
で通信を許可するポート番号を設定し -
cidr_blocks
で通信を許可するCIDRブロックを設定
している。

第8章
ロードバランサーとDNS
8.1 ALBの構成要素
P.91
※ALBについての説明。
8.2 HTTP用ロードバランサー
8.2.1 アプリケーションロードバランサー
P.92
※アプリケーションロードバランサーを作成する方法の説明。
terraform
ディレクトリ直下にalb.tf
ファイルを作成する。
touch alb.tf
作成したalb.tf
ファイルをエディターで開き、以下のように編集する。
リスト8.1:アプリケーションロードバランサーの定義
# ALB
resource "aws_lb" "example" {
name = "example"
load_balancer_type = "application"
internal = false
idle_timeout = 60
enable_deletion_protection = true
subnets = [
aws_subnet.public_0.id,
aws_subnet.public_1.id,
]
access_logs {
bucket = aws_s3_bucket.alb_log.id
enabled = true
}
security_groups = [
module.http_sg.security_group_id,
module.https_sg.security_group_id,
module.http_redirect_sg.security_group_id,
]
}
output "alb_dns_name" {
value = aws_lb.example.dns_name
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
アプリケーションロードバランサーの作成時に使われる入力パラメータについて
- name:アプリケーションロードバランサーの名前
-
load_balancer_type:ロードバランサーの種類(今回はALBなので
application
を指定している) -
internal:ALBが「インターネット向け(
false
)」なのか「VPC内部向け(true
)」なのかを指定 - idle_timeout:タイムアウトするまでの時間の指定。秒単位で指定する。デフォルト値は60秒。
-
enable_deletion_protection:削除保護の有効化。
true
を指定すると削除保護機能が有効になる。 - subnets:作成するロードバランサーが所属するサブネットの指定。ここで異なるAZが複数指定されることで、クロスゾーン負荷分散が実現される。
- access_logs:アクセスログ。S3などへのアクセスログの保存が有効になる。
- security_groups:セキュリティーグループの定義
alb.tf
ファイルに以下のコードを追記する。
リスト8.2:アプリケーションロードバランサーのセキュリティグループの定義
# Security Group
module "http_sg" {
source = "./security_group"
name = "http-sg"
vpc_id = aws_vpc.example.id
port = 80
cidr_blocks = ["0.0.0.0/0"]
}
module "https_sg" {
source = "./security_group"
name = "https-sg"
vpc_id = aws_vpc.example.id
port = 443
cidr_blocks = ["0.0.0.0/0"]
}
module "http_redirect_sg" {
source = "./security_group"
name = "http-redirect-sg"
vpc_id = aws_vpc.example.id
port = 8080
cidr_blocks = ["0.0.0.0/0"]
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
8.2.2 リスナー
P.95
※リスナーの設定方法の説明。
alb.tf
ファイルに以下のコードを追記する。
リスト8.3:HTTPリスナーの定義
# Listener
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.example.arn
port = "80"
protocol = "HTTP"
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "これは『HTTP』です"
status_code = "200"
}
}
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
リスナーの設定に使われる入力パラメータについて
- port:ポート番号。上記のコードではHTTPなので「80」を指定している。
- protocol:プロトコル。「HTTP」と「HTTPS」のみサポートしている。
- default_action:デフォルトアクション。リスナーは複数のルールを設定して、異なるアクションを実行できる。
8.2.3 HTTPアクセス
P.97
※リスナーの設定方法の説明。
8.3 Route 53
8.3.1 ドメインの登録
P.98
※Route 53によるドメインの登録方法の説明。
※Terraformではドメインの登録までは実行できない(まあドメイン登録まで自動でできる必要はなさそうだけど)。
terraform
ディレクトリ直下にdns.tf
ファイルを作成しておく。
touch dns.tf
このファイルにRoute 53やACMの設定を記述していく。
8.3.2 ホストゾーン
P.98
※ホストゾーンについての説明。
8.3.3 DNSレコード
P.99
※DNSレコードの設定方法の説明。
ここから本格的にDNSレコードの設定をしていく。
dns.tf
ファイルに以下のコードを追記する。
リスト8.6:ALBのDNSレコードの定義
# Route 53
resource "aws_route53_record" "example" {
zone_id = data.aws_route53_zone.example.zone_id
name = data.aws_route53_zone.example.name
type = "A"
alias {
name = aws_lb.example.dns_name
zone_id = aws_lb.example.zone_id
evaluate_target_health = true
}
}
output "domain_name" {
value = aws_route53_record.example.name
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
以上の設定により、自前のドメインでALBにアクセスできるようになる。
※恐らくexample
部分に自分が持っているドメインを記載すればいいということなんだろうけど、教材にはその辺の説明が一切ない。自分で実際に入れて試して確認しないとダメそう。
8.3.4 独自ドメインへのアクセス
※設定した独自ドメインへのアクセスをテストする。
8.4 ACM(AWS Certificate Manager)
ACMとは?
→ ドメインのSSL証明書を管理することができるAWSのマネージドサービスのこと。SSL証明書の自動更新に対応している。
8.4.1 SSL証明書の作成
P.102
※アプリケーションロードバランサーを作成する方法の説明。
SSL証明書の設定をするために、dns.tf
ファイルに以下のコードを追記する。
リスト8.7:SSL証明書の定義
# ACM
resource "aws_acm_certificate" "example" {
domain_name = aws_route53_record.example.name
subject_alternative_names = []
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
SSL証明書の設定に使われる入力パラメータについて
- domain_name:ドメイン名。ここに自前のドメイン名を記入する。
-
subject_alternative_names:ドメイン名を追加するための項目。サブドメインを設定できるみたい。特に追加したいドメインがない場合は、
[]
と書いて空のリストを渡すようにしておけばいいらしい。 - validation_method:検証方法。ドメインの所有権の検証方法を設定するための項目。「DNS検証」と「Eメール検証」のどちらかを選べるらしい。SSL証明書を自動更新したい場合はDNS検証を選択すること!
-
lifecycle:ライフサイクル。
create_before_destroy = true
というコードで、「新しいSSL証明書を作ってから、古いSSL証明書と差し替える」という処理を設定している。
8.4.2 SSL証明書の検証
P.103
※SSL証明書の検証についての説明。
「DNS検証」とは?
→ SSL証明書を自動で更新する検証方法のこと。DNS検証を使えばSSL証明書の更新漏れがなくなるらしい。DNS検証の仕組みがよくわからないけど、とりあえず「SSL証明書の更新を楽にするために必要な設定」とだけ理解した。別途ググって知識を補完しよう。
検証用DNSレコード
DNS検証の設定方法。dns.tf
ファイルに以下のコードを追記し、DNSレコードを追加する。
リスト8.8:SSL証明書の検証用レコードの定義
# ACM (DNS verification)
resource "aws_route53_record" "example_certificate" {
for_each = {
for dvo in aws_acm_certificate.example.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
name = each.value.name
type = each.value.type
records = [each.value.record]
zone_id = data.aws_route53_zone.example.id
ttl = 60
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
検証の待機
※aws_acm_certificate_validation
リソースを使うことで、terraform apply
の実行時、SSL証明書の検証が完了するまで処理の続行を待機するように設定できるらしい。教材のリスト8.9がそのサンプルコードになる。
8.5 HTTPS用ロードバランサー
HTTPSでALBにアクセスできるようHTTPSリスナーを作成する。
8.5.1 HTTPSリスナー
P.105
HTTPSリスナーの設定方法。
dns.tf
ファイルに以下のコードを追記する。
リスト8.10:HTTPSリスナーの定義
# HTTPS Listener
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.example.arn
port = "443"
protocol = "HTTPS"
certificate_arn = aws_acm_certificate.example.arn
ssl_policy = "ELBSecurityPolicy-2016-08"
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "これは『HTTPS』です"
status_code = "200"
}
}
}
ソース:example-pragmatic-terraform/10.tf at main · tmknom/example-pragmatic-terraform
8.5.2 HTTPSへのリダイレクト
P.106
HTTPをHTTPSへリダイレクトさせるための設定。「リダイレクトリスナー」を作成する。
dns.tf
ファイルに以下のコードを追記する。
リスト8.11:HTTPからHTTPSにリダイレクトするリスナーの定義
# Redirect Listener
resource "aws_lb_listener" "redirect_http_to_https" {
load_balancer_arn = aws_lb.example.arn
port = "8080"
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
ソース:example-pragmatic-terraform/11.tf at main · tmknom/example-pragmatic-terraform
8.5.3 HTTPSアクセス
P.107
※HTTPSへアクセスできるかどうかの確認作業。
8.6 リクエストフォワーディング
任意のターゲットへリクエストをフォワードできるようにする設定。
8.6.1 ターゲットグループ
P.108
「リクエストをフォワードする」の「フォワード」とは?
→ 同一サーバー内での転送処理のこと。「リダイレクト」との違いもあわせて理解しよう。
ALBではリクエストをフォワードする対象のことを「ターゲットグループ」と呼ぶ。
dns.tf
ファイルに以下のコードを追記し、ターゲットグループを定義する。
リスト8.12:ターゲットグループの定義
# Target Group
resource "aws_lb_target_group" "example" {
name = "example"
target_type = "ip"
vpc_id = aws_vpc.example.id
port = 80
protocol = "HTTP"
deregistration_delay = 300
health_check {
path = "/"
healthy_threshold = 5
unhealthy_threshold = 2
timeout = 5
interval = 30
matcher = 200
port = "traffic-port"
protocol = "HTTP"
}
depends_on = [aws_lb.example]
}
ソース:example-pragmatic-terraform/12.tf at main · tmknom/example-pragmatic-terraform
暗黙的な依存関係
ALBとターゲットグループをECSサービスと同時に作成するとエラーになってしまうらしい(ECS(Fargate)によるコンテナがまだ作成されていないのにALBの設定ができるか!ってなるからだろうか?)。そのためdepends_on = [aws_lb.example]
というコードを設定することでtfファイルの依存関係を制御する必要がある。
8.6.2 リスナールール
P.111
ターゲットグループにリクエストをフォワードするリスナールールの作成。
dns.tf
ファイルに以下のコードを追記する。
リスト8.13:リスナールールの定義
# Listener Rule
resource "aws_lb_listener_rule" "example" {
listener_arn = aws_lb_listener.https.arn
priority = 100
action {
type = "forward"
target_group_arn = aws_lb_target_group.example.arn
}
condition {
path_pattern {
values = ["/*"]
}
}
}
ソース:example-pragmatic-terraform/13.tf at main · tmknom/example-pragmatic-terraform

第9章
コンテナオーケストレーション
9.1 ECSの構成要素
P.113
※ECSについての説明。
- タスク
- ECSサービス
- ECSクラスタ
9.2 ECSの起動タイプ
ECSには以下2種類の起動タイプがある。
- EC2起動タイプ
- Fargate起動タイプ
9.2.1 EC2起動タイプ
P.113
※EC2起動タイプの説明。
9.2.2 Fargate起動タイプ
P.114
※Fargate起動タイプの説明。
※ここから先、「Fargate起動タイプ」で実装を進めていく!
9.3 Webサーバーの構築
- ECSをプライベートネットワークに配置し
- nginxコンテナを起動
- ALB経由でリクエストを受け取り
- それをECS上のnginxコンテナが処理する
9.3.1 ECSクラスタ
P.115
まずはECSの設定用にecs.tf
を作成する
terraform
ディレクトリ直下にecs.tf
ファイルを作成。
touch ecs.tf
ecs.tf
ファイルに以下のコードを追記する。
リスト9.1:ECSクラスタの定義
# ECS Cluster
resource "aws_ecs_cluster" "example" {
name = "example"
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
9.3.2 タスク定義
P.115
ECSでは、コンテナの実行単位のことを「タスク」と呼ぶ。
そしてタスクは「タスク定義」から生成される。
実際にタスク定義を実装してみる。
ecs.tf
ファイルに以下のコードを追記。
リスト9.2:タスク定義
# ECS Task
resource "aws_ecs_task_definition" "example" {
family = "example"
cpu = "256"
memory = "512"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
container_definitions = file("./tasks/container_definitions.json")
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
※この部分は教材のコードに変更を加えています。container_definitions = file("./container_definitions.json")
の箇所をcontainer_definitions = file("./tasks/container_definitions.json")
とし、tasks
ディレクトリの中のcontainer_definitions.json
ファイルを指定するように設定しています。
タスク定義に使われる入力パラメータについて
- family:ファミリー。タスク定義名のプレフィックスのこと。
- cpu、memory:タスクサイズ。タスクが使用するリソースのサイズを設定する項目。
- network_mode:ネットワークモード。今回のように、「Fargate起動タイプ」を選択している場合はこの項目に「awsvpc」を指定する。
- requires_compatibilities:起動タイプ。「FARGATE起動タイプ」にするので「FARGATE」を指定する。
-
container_definitions:コンテナ定義。
container_definitions.json
ファイルなど、タスクで実行するコンテナの定義を別途JSONファイルで用意する。このコンテナ定義用のファイルは、今後、タスクの数に応じて複数作成することになるのが予想されるため、terraform
ディレクトリ直下にtasks
などの名前のディレクトリを作成し、その中にコンテナ定義のJSONファイルを格納して管理するようにするとよさそう。
ということで、実際にコンテナ定義のJSONファイルを作成してみる。
terraform
ディレクトリにいる状態で、コンテナ定義のJSONファイルを置くためのtasks
ディレクトリを作成する。
mkdir tasks
そして作成したtasks
ディレクトリに移動。
cd tasks
container_definitions.json
というファイルを作成する。
touch container_definitions.json
作成したcontainer_definitions.json
ファイルをエディターで開き、以下のように編集する。
リスト9.3:コンテナ定義
[
{
"name": "example",
"image": "nginx:latest",
"essential": true,
"portMappings": [
{
"protocol": "tcp",
"containerPort": 80
}
]
}
]
ソース:example-pragmatic-terraform/container_definitions.json at main · tmknom/example-pragmatic-terraform
コンテナ定義のJSONファイルに使われる入力パラメータについて
- name:コンテナの名前
- image:使用するコンテナイメージ
-
essential:タスク実行に必須かどうかのフラグ。この項目では
true
かfalse
のどちらかを指定する。 - portMappings:マッピングするコンテナのポート番号
9.3.3 ECSサービス
P.118
コンテナ(タスク)の起動を維持するために「ECSサービス」を使う。
ecs.tf
ファイルに以下のコードを追記し、ECSサービスを実装する。
リスト9.4:ECSサービスの定義
# ECS Service
resource "aws_ecs_service" "example" {
name = "example"
cluster = aws_ecs_cluster.example.arn
task_definition = aws_ecs_task_definition.example.arn
desired_count = 2
launch_type = "FARGATE"
platform_version = "1.3.0"
health_check_grace_period_seconds = 60
network_configuration {
assign_public_ip = false
security_groups = [module.nginx_sg.security_group_id]
subnets = [
aws_subnet.private_0.id,
aws_subnet.private_1.id,
]
}
load_balancer {
target_group_arn = aws_lb_target_group.example.arn
container_name = "example"
container_port = 80
}
lifecycle {
ignore_changes = [task_definition]
}
}
module "nginx_sg" {
source = "./security_group"
name = "nginx-sg"
vpc_id = aws_vpc.example.id
port = 80
cidr_blocks = [aws_vpc.example.cidr_block]
}
ソース:example-pragmatic-terraform/4.tf at main · tmknom/example-pragmatic-terraform
ECSサービスに使われる入力パラメータについて
-
cluster、task_definition:ECSクラスタとタスク定義。
cluster
にはresource "aws_ecs_cluster"
で作成したクラスタ名を設定する。task_definition
にはresource "aws_ecs_task_definition"
で作成したタスク定義を設定する。 - desired_count:維持するタスク数。ECSサービスが維持するタスク数をこの項目で指定する。2以上のタスク数を指定することで、何らかの問題である1つのコンテナが異常終了したとしてもサービスが落ちないようにカバーすることができる。
- launch_type:起動タイプ。「FARGATE起動タイプ」にするので「FARGATE」を指定する。
- platform_version:プラットフォームバージョン。ここは「LATEST」は指定せず、バージョン数を明示的に指定すること!
- health_check_grace_period_seconds:ヘルスチェック猶予期間。秒単位で指定する。
- network_configuration:ネットワーク構成。「サブネット」と「セキュリティグループ」、「パブリックIPアドレスを割り当てるかどうか」を設定する。
- load_balancer:ロードバランサー。「ターゲットグループ」と「コンテナの名前」、「ポート番号」を指定し、ロードバランサーと関連付ける。
- lifecycle:ライフサイクル。今回はタスク定義の変更を無視するように設定している。
9.3.4 コンテナの動作確認
P.122
※ここまで実装してきた内容を、terraform apply
コマンドを実行して確認してみる。
9.4 Fargateにおけるロギング
9.4.1 CloudWatch Logs
P.123
- Fargateではホストサーバーにログインできず、コンテナのログを見ることができない
- そこで「CloudWatch Logs」と連携し、ログを記録・確認できるようにする
CloudWatch Logsとは?
→ 現在利用しているAWSサービスのログを収集・記録することができるAWSのマネージドサービスのこと。
CloudWatch Logsを利用するために、Terraformでcloud_watch.tf
ファイルを作成する。
terraform
ディレクトリ直下にcloud_watch.tf
ファイルを作成。
touch cloud_watch.tf
作成したcloud_watch.tf
ファイルをエディターで開き、以下のように編集する。
リスト9.5:CloudWatch Logsの定義
# CloudWatch for ECS
resource "aws_cloudwatch_log_group" "for_ecs" {
name = "/ecs/example"
retention_in_days = 180
}
ソース:example-pragmatic-terraform/5.tf at main · tmknom/example-pragmatic-terraform
9.4.2 ECSタスク実行IAMロール
P.123
ECSに権限を付与する。そのためにECSタスク実行用のIAMロールを作成する。
IAMポリシーデータソース
ecs.tf
ファイルに以下のコードを追記する。
リスト9.6:AmazonECSTaskExecutionRolePolicyの参照
# IAM Role (IAM Policy Data Source)
data "aws_iam_policy" "ecs_task_execution_role_policy" {
arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
ポリシードキュメント
P.124
ポリシードキュメントを定義する。
ecs.tf
ファイルに以下のコードを追記する。
リスト9.7:ECSタスク実行IAMロールのポリシードキュメントの定義
# IAM Role (Policy Document)
data "aws_iam_policy_document" "ecs_task_execution" {
source_json = data.aws_iam_policy.ecs_task_execution_role_policy.policy
statement {
effect = "Allow"
actions = ["ssm:GetParameters", "kms:Decrypt"]
resources = ["*"]
}
}
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
IAMロール
P.124
ECS用のIAMロールを作成する。
ecs.tf
ファイルに以下のコードを追記する。
リスト9.8:ECSタスク実行IAMロールの定義
# IAM Role (for ECS)
module "ecs_task_execution_role" {
source = "./iam_role"
name = "ecs-task-execution"
identifier = "ecs-tasks.amazonaws.com"
policy = data.aws_iam_policy_document.ecs_task_execution.json
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
9.4.3 Dockerコンテナのロギング
P.125
DockerコンテナがCloudWatch Logsにログを投げられるように設定する。
タスクの定義用に追加した以下のコードを
リスト9.2:タスク定義
# ECS Task
resource "aws_ecs_task_definition" "example" {
family = "example"
cpu = "256"
memory = "512"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
container_definitions = file("./tasks/container_definitions.json")
}
次のように変更する。
リスト9.9:タスク定義にECSタスク実行IAMロールを追加
# ECS Task
resource "aws_ecs_task_definition" "example" {
family = "example"
cpu = "256"
memory = "512"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
container_definitions = file("./container_definitions.json")
execution_role_arn = module.ecs_task_execution_role.iam_role_arn
}
ソース:example-pragmatic-terraform/9.tf at main · tmknom/example-pragmatic-terraform
※パラメータexecution_role_arn
を追加している。
続いてコンテナ定義のコードも変更。
コンテナ定義のJSONファイル、container_definitions.json
ファイルの以下のコードを
リスト9.3:コンテナ定義
[
{
"name": "example",
"image": "nginx:latest",
"essential": true,
"portMappings": [
{
"protocol": "tcp",
"containerPort": 80
}
]
}
]
次のように変更する。
リスト9.10:コンテナ定義にCloudWatch Logsのグループ名を追加
[
{
"name": "example",
"image": "nginx:latest",
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "nginx",
"awslogs-group": "/ecs/example"
}
},
"portMappings": [
{
"protocol": "tcp",
"containerPort": 80
}
]
}
]
ソース:example-pragmatic-terraform/container_definitions.json at main · tmknom/example-pragmatic-terraform
※パラメータlogConfiguration
を追加している。

第10章
バッチ
10.1 バッチ設計
10.1.1 バッチ設計の基本原則
P.129
バッチ設計で重要な4つの観点について
- ジョブ管理
- エラーハンドリング
- リトライ
- 依存関係制御
10.1.2 ジョブ管理
P.130
※ジョブ管理の手法についての説明。
10.2 ECS Scheduled Tasks
10.2.1 バッチ用タスク定義
バッチ用CloudWatch Logs
P.131
バッチ用のタスク定義から始める。
第9章で作成したcloud_watch.tf
ファイルをエディターで開き、以下のコードを追記する。
リスト10.1:バッチ用CloudWatch Logsの定義
# CloudWatch for ECS Scheduled Tasks
resource "aws_cloudwatch_log_group" "for_ecs_scheduled_tasks" {
name = "/ecs-scheduled-tasks/example"
retention_in_days = 180
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
バッチ用タスク定義
P.132
続いて、バッチ用のタスク定義を実装する。
ecs.tf
ファイルに以下のコードを追記する。
※このコードはecs.tf
ファイルに追記でいいのかどうか、ちょっと自信ない...(何てファイルでこのコードを管理すればいいのかちゃんと書いておいてよ...)。検証の必要アリ!!
リスト10.2:バッチ用タスク定義
# Batch
resource "aws_ecs_task_definition" "example_batch" {
family = "example-batch"
cpu = "256"
memory = "512"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
container_definitions = file("./batch_container_definitions.json")
execution_role_arn = module.ecs_task_execution_role.iam_role_arn
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
バッチ用コンテナ定義
P.133
コンテナ定義をbatch_container_definitions.json
というファイルに実装していく。
というわけで、まずはそれ用のディレクトリを作成する。
terraform
ディレクトリにいる状態で、バッチ用のコンテナ定義を記述するファイル群を置くためのbatch
ディレクトリを作成する。
mkdir batch
そして作成したbatch
ディレクトリに移動。
cd batch
batch_container_definitions.json
というファイルを作成する。
touch batch_container_definitions.json
作成したbatch_container_definitions.json
ファイルをエディターで開き、以下のように編集する。
リスト10.3:バッチ用コンテナ定義
[
{
"name": "alpine",
"image": "alpine:latest",
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "batch",
"awslogs-group": "/ecs-scheduled-tasks/example"
}
},
"command" : ["/bin/date"]
}
]
10.2.2 CloudWatchイベントIAMロール
P.134
CloudWatchイベントからECSを起動するためのIAMロールを作成する。
「AmazonEC2ContainerServiceEventsRole」というポリシーの役割
- 「タスクを実行する」権限の付与
- 「タスクにIAMロールを渡す」権限の付与
...どうしよう。どのファイルに書けばいいのか全然わからない!
iam_role.tf
? cloud_watch.tf
? それともecs.tf
?
頼むからファイルとコードの関係性をちゃんと明記してくれ〜!!
わかんないのでとりあえずecs.tf
ファイルに以下のコードを追記する。
リスト10.4:CloudWatchイベントIAMロールの定義
# CloudWatch Event IAM Role
module "ecs_events_role" {
source = "./iam_role"
name = "ecs-events"
identifier = "events.amazonaws.com"
policy = data.aws_iam_policy.ecs_events_role_policy.policy
}
data "aws_iam_policy" "ecs_events_role_policy" {
arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole"
}
ソース:example-pragmatic-terraform/4.tf at main · tmknom/example-pragmatic-terraform
10.2.3 CloudWatchイベントルール
P.134
ジョブの実行スケジュールを定義するため、CloudWatchのイベントルールを作成する。
cloud_watch.tf
ファイルに以下のコードを追記する。
リスト10.5:CloudWatchイベントルールの定義
# CloudWatch Event Rule
resource "aws_cloudwatch_event_rule" "example_batch" {
name = "example-batch"
description = "とても重要なバッチ処理です"
schedule_expression = "cron(*/2 * * * ? *)"
}
ソース:example-pragmatic-terraform/5.tf at main · tmknom/example-pragmatic-terraform
10.2.4 CloudWatchイベントターゲット
P.135
CloudWatchイベントターゲットで、実行対象のジョブを定義する。
cloud_watch.tf
ファイルに以下のコードを追記する。
リスト10.6:CloudWatchイベントターゲットの定義
# CloudWatch Event Target
resource "aws_cloudwatch_event_target" "example_batch" {
target_id = "example-batch"
rule = aws_cloudwatch_event_rule.example_batch.name
role_arn = module.ecs_events_role.iam_role_arn
arn = aws_ecs_cluster.example.arn
ecs_target {
launch_type = "FARGATE"
task_count = 1
platform_version = "1.3.0"
task_definition_arn = aws_ecs_task_definition.example_batch.arn
network_configuration {
assign_public_ip = "false"
subnets = [aws_subnet.private_0.id]
}
}
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
10.2.5 バッチの動作確認
P.137
※ここまで実装してきた内容を、filter-log-events
コマンドを実行して動作確認してみる。

第11章
鍵管理
11.1 KMS(Key Management Service)
KMSとは?
→ AWSで暗号鍵を管理するためのマネージドサービスのこと。
11.1.1 カスタマーマスターキー
P.139
カスタマーマスターキーを定義する。
そのためにまずはkms.tf
を作成。
terraform
ディレクトリ直下にkms.tf
ファイルを作成。
touch kms.tf
そしてkms.tf
ファイルを以下のように編集する。
リスト11.1:カスタマーマスターキーの定義
# Customer Master Key
resource "aws_kms_key" "example" {
description = "Example Customer Master Key"
enable_key_rotation = true
is_enabled = true
deletion_window_in_days = 30
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
カスタマーマスターキーの定義に使われる入力パラメータについて
- description:概要。どういった用途のためのカスタマーマスターキーなのかわかるように名前を付ける。
- enable_key_rotation:自動ローテーション
- is_enabled:有効化と無効化。カスタマーマスターキーを有効にするか/無効にするかを設定する。
- deletion_window_in_days:削除待機期間。デフォルトは30日。待機期間中であればカスタマーマスターキーの削除を取り消すことができる。
11.1.2 エイリアス
P.140
- カスタマーマスターキーにはUUIDが割り当てられている
- しかし、そのUUIDは人間からするとわかりづらい
- なので「エイリアス」を設定できる機能がある
ということで、カスタマーマスターキーのエイリアスを設定するため、kms.tf
ファイルに以下のコードを追記する。
リスト11.2:エイリアスの定義
# Customer Master Key (Alias)
resource "aws_kms_alias" "example" {
name = "alias/example"
target_key_id = aws_kms_key.example.key_id
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
※このエイリアスで設定するname
には、alias/
というプレフィックスを付ける必要がある!(なので今回のコードではexample
という名前を付けるためにalias/example
と設定している)

第12章
設定管理
12.1 コンテナの設定管理
ECSでは、設定をコンテナ起動時に注入する。
12.2 SSMパラメータストア
SSMパラメータストアとは?
→ 設定管理に特化したマネージドサービスのこと。
12.2.1 AWS CLIによる操作
P.143
AWS CLIによるSSMパラメータストアの操作に慣れよう。
平文
SSMパラメータストアに値を保存するput-parameter
コマンド
aws ssm put-parameter --name 'plain_name' --value 'plain value' --type String
値を参照するget-parameter
コマンド
aws ssm get-parameter --output text --name 'plain_name' --query Parameter.Value
暗号化
暗号化した値を保存するput-parameter
コマンド
aws ssm put-parameter --name 'encryption_name' --value 'encryption value' --type SecureString
暗号化された値を複合した状態で参照する--with-decryption
オプション
aws ssm get-parameter --output text --query Parameter.Value --name 'encryption_name' --with-decryption
12.2.2 Terraformによるコード化
P.145
SSMパラメータストアをTerraformで管理する。
平文
暗号化
※Gitなどによるバージョン管理を考慮しない場合の管理方法についての説明。
※リスト12.1とリスト12.2はその意味で実用性がないため、覚えなくてよさそう。割愛。
Terraformによる初期値の設定とAWS CLIによる暗号化
P.147
SSMパラメータストアをTerraformで管理する上で、「Terraformではダミー値を設定して、あとでAWS CLIから更新する」という方法を採用。
今回はmain.tf
ファイルに実装する。
main.tf
ファイルに以下のコードを追記する。
リスト12.3:データベースのパスワードのダミー定義
# SSM Parameter for DB
resource "aws_ssm_parameter" "db_password" {
name = "/db/password"
value = "uninitialized"
type = "SecureString"
description = "データベースのパスワード"
lifecycle {
ignore_changes = [value]
}
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
main.tf
ファイルを編集後、以下のコマンドを実行し、AWS CLIで更新をかける。
aws ssm put-parameter --name '/db/password' --type SecureString --value 'ModifiedStrongPassword!' --overwrite
12.2.3 SSMパラメータストアとECSの統合
P.148
SSMパラメータストアの値は、ECSのDockerコンテナ内で環境変数として参照できる。
※ECSからSSMパラメータストアの値を参照する権限は、リスト9.7で事前に設定済み。
コンテナ定義用に作成したbatch_container_definitions.json
ファイル(リスト10.3)のコードを
リスト10.3:バッチ用コンテナ定義
[
{
"name": "alpine",
"image": "alpine:latest",
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "batch",
"awslogs-group": "/ecs-scheduled-tasks/example"
}
},
"command" : ["/bin/date"]
}
]
次のように変更する。
リスト12.4:コンテナ定義からSSMパラメータストアの値を参照
[
{
"name": "alpine",
"image": "alpine:latest",
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "batch",
"awslogs-group": "/ecs-scheduled-tasks/example"
}
},
"secrets": [
{
"name": "DB_USERNAME",
"valueFrom": "/db/username"
},
{
"name": "DB_PASSWORD",
"valueFrom": "/db/password"
}
],
"command" : ["/usr/bin/env"]
}
]
※secrets
の値を追加している。
- name:コンテナ内での環境変数の名前
- valueFrom:SSMパラメータストアのキー名

第13章
データストア
13.1 RDS(Relational Database Service)
RDSとは?
→ AWSが提供するリレーショナルデータベースのこと。
- MySQL
- PostgresSQL
- Oracle
などをサポートしている。
13.1.1 DBパラメータグループ
P.150
データベースの設定ファイルを作成する。
※今回はMySQLを採用。
まずはRDSの設定用にrds.tf
ファイルを作成する。
terraform
ディレクトリ直下にrds.tf
ファイルを作成。
touch rds.tf
作成したrds.tf
ファイルをエディターで開き、以下のように編集する。
リスト13.1:DBパラメータグループの定義
# DB Parameter Group
resource "aws_db_parameter_group" "example" {
name = "example"
family = "mysql5.7"
parameter {
name = "character_set_database"
value = "utf8mb4"
}
parameter {
name = "character_set_server"
value = "utf8mb4"
}
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
13.1.2 DBオプショングループ
P.151
DBオプショングループは、データベースエンジンにオプション機能を追加する。
リスト13.2:DBパラメータグループの定義
resource "aws_db_option_group" "example" {
name = "example"
engine_name = "mysql"
major_engine_version = "5.7"
option {
option_name = "MARIADB_AUDIT_PLUGIN"
}
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
13.1.3 DBサブネットグループ
P.152
データベースを稼働させるサブネットを、DBサブネットグループで定義する。
リスト13.3:DBサブネットグループの定義
# DB Subnet Group
resource "aws_db_subnet_group" "example" {
name = "example"
subnet_ids = [aws_subnet.private_0.id, aws_subnet.private_1.id]
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
13.1.4 DBインスタンス
P.153
DBインスタンスを実装。データベースサーバーを作成する。
リスト13.4:DBインスタンスの定義
# DB Instance
resource "aws_db_instance" "example" {
identifier = "example"
engine = "mysql"
engine_version = "5.7.25"
instance_class = "db.t3.small"
allocated_storage = 20
max_allocated_storage = 100
storage_type = "gp2"
storage_encrypted = true
kms_key_id = aws_kms_key.example.arn
username = "admin"
password = "VeryStrongPassword!"
multi_az = true
publicly_accessible = false
backup_window = "09:10-09:40"
backup_retention_period = 30
maintenance_window = "mon:10:10-mon:10:40"
auto_minor_version_upgrade = false
deletion_protection = true
skip_final_snapshot = false
port = 3306
apply_immediately = false
vpc_security_group_ids = [module.mysql_sg.security_group_id]
parameter_group_name = aws_db_parameter_group.example.name
option_group_name = aws_db_option_group.example.name
db_subnet_group_name = aws_db_subnet_group.example.name
lifecycle {
ignore_changes = [password]
}
}
ソース:example-pragmatic-terraform/4.tf at main · tmknom/example-pragmatic-terraform
セキュリティグループ
P.156
DBインスタンスのセキュリティグループを設定する。
リスト13.5:DBインスタンスのセキュリティグループの定義
# Security Group
module "mysql_sg" {
source = "./security_group/security_group"
name = "mysql-sg"
vpc_id = aws_vpc.example.id
port = 3306
cidr_blocks = [aws_vpc.example.cidr_block]
}
ソース:example-pragmatic-terraform/5.tf at main · tmknom/example-pragmatic-terraform
13.1.5 マスターパスワードの変更
P.156
リスト13.4で仮設定したpassword
(=マスターパスワード)を変更する。
そのためにterraform apply
コマンドの実行後に、さらに次のコマンドを実行し、マスターパスワードを変更する。
aws rds modify-db-instance --db-instance-identifier 'example' --master-user-password 'NewMasterPassword!'
※実際には、NewMasterPassword!
に独自に用意した複雑なパスワードを入力する!
ここまで編集してみて、rds.tf
ファイルの中身は最終的に以下のようになった。
# DB Parameter Group
resource "aws_db_parameter_group" "example" {
name = "example"
family = "mysql5.7"
parameter {
name = "character_set_database"
value = "utf8mb4"
}
parameter {
name = "character_set_server"
value = "utf8mb4"
}
}
# DB Subnet Group
resource "aws_db_subnet_group" "example" {
name = "example"
subnet_ids = [aws_subnet.private_0.id, aws_subnet.private_1.id]
}
# Security Group
module "mysql_sg" {
source = "./security_group/security_group"
name = "mysql-sg"
vpc_id = aws_vpc.example.id
port = 3306
cidr_blocks = [aws_vpc.example.cidr_block]
}
# DB Instance
resource "aws_db_instance" "example" {
identifier = "example"
engine = "mysql"
engine_version = "5.7.25"
instance_class = "db.t3.small"
allocated_storage = 20
max_allocated_storage = 100
storage_type = "gp2"
storage_encrypted = true
kms_key_id = aws_kms_key.example.arn
username = "admin"
password = "VeryStrongPassword!"
multi_az = true
publicly_accessible = false
backup_window = "09:10-09:40"
backup_retention_period = 30
maintenance_window = "mon:10:10-mon:10:40"
auto_minor_version_upgrade = false
deletion_protection = true
skip_final_snapshot = false
port = 3306
apply_immediately = false
vpc_security_group_ids = [module.mysql_sg.security_group_id]
parameter_group_name = aws_db_parameter_group.example.name
option_group_name = aws_db_option_group.example.name
db_subnet_group_name = aws_db_subnet_group.example.name
lifecycle {
ignore_changes = [password]
}
}
RDSの削除
P.157
一度AWSに作成したRDSを削除するには、rds.tf
ファイルの以下の部分を変更する必要がある。
-
deletion_protection
をfalse
にする(削除保護の無効化) -
skip_final_snapshot
をtrue
にする(スナップショットの作成をスキップするように設定)
rds.tf
ファイルのaws_db_instance
の以上パラメータを変更したのち、一度terraform apply
コマンドを実行する。
terraform apply
それからterraform destroy
コマンドを実行することで、ようやくDBインスタンスを削除することができる。
terraform destroy
13.2 ElastiCache
P.158
ElastiCacheはMemcachedとRedisをサポートしている。
今回はRedisを作成する。
13.2.1 ElastiCacheパラメータグループ
P.158
まずはElastiCacheの設定用にelasti_cache.tf
ファイルを作成する。
terraform
ディレクトリ直下にelasti_cache.tf
ファイルを作成。
touch elasti_cache.tf
そしてクラスタモードを無効にする設定を定義。
作成したelasti_cache.tf
ファイルをエディターで開き、以下のように編集する。
リスト13.6:ElastiCacheパラメータグループの定義
# ElastiCache Parameter Group
resource "aws_elasticache_parameter_group" "example" {
name = "example"
family = "redis5.0"
parameter {
name = "cluster-enabled"
value = "no"
}
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
13.2.2 ElastiCacheサブネットグループ
P.159
ElastiCacheサブネットグループを定義する。
リスト13.7:ElastiCacheサブネットグループの定義
# ElastiCache Subnet Group
resource "aws_elasticache_subnet_group" "example" {
name = "example"
subnet_ids = [aws_subnet.private_0.id, aws_subnet.private_1.id]
}
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
13.2.3 ElastiCacheレプリケーショングループ
P.159
elasti_cache.tf
ファイルに以下のコードを追記してElastiCacheレプリケーショングループを実装し、Redisサーバーを作成する。
リスト13.8:ElastiCacheレプリケーショングループの定義
# ElastiCache Replication Group
resource "aws_elasticache_replication_group" "example" {
replication_group_id = "example"
replication_group_description = "Cluster Disabled"
engine = "redis"
engine_version = "5.0.4"
number_cache_clusters = 3
node_type = "cache.m3.medium"
snapshot_window = "09:10-10:10"
snapshot_retention_limit = 7
maintenance_window = "mon:10:40-mon:11:40"
automatic_failover_enabled = true
port = 6379
apply_immediately = false
security_group_ids = [module.redis_sg.security_group_id]
parameter_group_name = aws_elasticache_parameter_group.example.name
subnet_group_name = aws_elasticache_subnet_group.example.name
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
セキュリティグループ
P.162
ElastiCacheがVPC内からの通信のみを許可するように設定。
作成したセキュリティグループをリスト13.8のsecurity_group_ids
に設定する。
リスト13.9:ElastiCacheレプリケーショングループのセキュリティグループの定義
# Redis Security Group
module "redis_sg" {
source = "./security_group/security_group"
name = "redis-sg"
vpc_id = aws_vpc.example.id
port = 6379
cidr_blocks = [aws_vpc.example.cidr_block]
}
ソース:example-pragmatic-terraform/9.tf at main · tmknom/example-pragmatic-terraform
ここまで編集してみて、elasti_cache.tf
ファイルの中身は最終的に以下のようになった。
# ElastiCache Parameter Group
resource "aws_elasticache_parameter_group" "example" {
name = "example"
family = "redis5.0"
parameter {
name = "cluster-enabled"
value = "no"
}
}
# ElastiCache Subnet Group
resource "aws_elasticache_subnet_group" "example" {
name = "example"
subnet_ids = [aws_subnet.private_0.id, aws_subnet.private_1.id]
}
# Redis Security Group
module "redis_sg" {
source = "./security_group/security_group"
name = "redis-sg"
vpc_id = aws_vpc.example.id
port = 6379
cidr_blocks = [aws_vpc.example.cidr_block]
}
# ElastiCache Replication Group
resource "aws_elasticache_replication_group" "example" {
replication_group_id = "example"
replication_group_description = "Cluster Disabled"
engine = "redis"
engine_version = "5.0.4"
number_cache_clusters = 3
node_type = "cache.m3.medium"
snapshot_window = "09:10-10:10"
snapshot_retention_limit = 7
maintenance_window = "mon:10:40-mon:11:40"
automatic_failover_enabled = true
port = 6379
apply_immediately = false
security_group_ids = [module.redis_sg.security_group_id]
parameter_group_name = aws_elasticache_parameter_group.example.name
subnet_group_name = aws_elasticache_subnet_group.example.name
}

第14章
デプロイメントパイプライン
14.1 デプロイメントパイプラインの設計
P.164
※GitHubにコードをプッシュ→ECSへコンテナをデプロイ、の流れの説明
14.2 コンテナレジストリ
P.164
まず、Dockerイメージを補完するコンテナレジストリを作成。
そのためにECRを利用する。
14.2.1 ECRリポジトリ
P.164
Dockerイメージを保管するECRリポジトリを実装していく。
まずはECRの設定用にecr.tf
ファイルを作成する。
terraform
ディレクトリ直下にecr.tf
ファイルを作成。
touch ecr.tf
作成したecr.tf
ファイルをエディターで開き、以下のように編集する。
リスト14.1:ECRリポジトリの定義
# ECR Repository
resource "aws_ecr_repository" "example" {
name = "example"
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
14.2.2 ECRライフサイクルポリシー
P.165
ECRリポジトリに保存できるイメージの数を設定する。
イメージが増えすぎないように制限をかける。
リスト14.2:ECRライフサイクルポリシーの定義
# ECR Lifecycle Policy
resource "aws_ecr_lifecycle_policy" "example" {
repository = aws_ecr_repository.example.name
policy = <<EOF
{
"rules": [
{
"rulePriority": 1,
"description": "Keep last 30 release tagged images",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["release"],
"countType": "imageCountMoreThan",
"countNumber": 30
},
"action": {
"type": "expire"
}
}
]
}
EOF
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
14.2.3 Dockerイメージのプッシュ
P.166
Dockerクライアントを認証する。
$(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
イメージ名のレジストリをECRにして、適当なDockerfileをビルドする。
docker build -t XXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/example:latest .
※「XXXXXX」は自身の環境の文字列に書き換える!
Dockerイメージをプッシュする。
docker push XXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/example:latest
※「XXXXXX」は自身の環境の文字列に書き換える!
14.3 継続的インテグレーション
P.167
AWSの継続的インテグレーション(CI)サービスといえば...
→ CodeBuild
14.3.1 CodeBuildサービスロール
P.167
CodeBuildが使用するIAMロールを作成する。
まずはCodeBuildの設定用にcode_build.tf
ファイルを作成する。
terraform
ディレクトリ直下にcode_build.tf
ファイルを作成。
touch code_build.tf
作成したcode_build.tf
ファイルをエディターで開き、以下のように編集する。
リスト14.3:CodeBuildサービスロールのポリシードキュメントの定義
# IAM Role (Policy Document)
data "aws_iam_policy_document" "codebuild" {
statement {
effect = "Allow"
resources = ["*"]
actions = [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:GetRepositoryPolicy",
"ecr:DescribeRepositories",
"ecr:ListImages",
"ecr:DescribeImages",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage",
]
}
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
このポリシードキュメントで定義する権限
- ビルド出力アーティファクトを保存するためのS3操作権限
- ビルドログを出力するためのCloudWatch Logs操作権限
- DockerイメージをプッシュするためのECR操作権限
IAMロール
P.169
CodeBuild用のIAMロールを実装する。
リスト14.4:CodeBuildサービスロールの定義
# IAM Role (for CodeBuild)
module "codebuild_role" {
source = "./iam_role"
name = "codebuild"
identifier = "codebuild.amazonaws.com"
policy = data.aws_iam_policy_document.codebuild.json
}
ソース:example-pragmatic-terraform/4.tf at main · tmknom/example-pragmatic-terraform
14.3.2 CodeBuildプロジェクト
P.169
CodeBuildプロジェクトを作成する。
リスト14.5:CodeBuildプロジェクトの定義
# CodeBuild Project
resource "aws_codebuild_project" "example" {
name = "example"
service_role = module.codebuild_role.iam_role_arn
source {
type = "CODEPIPELINE"
}
artifacts {
type = "CODEPIPELINE"
}
environment {
type = "LINUX_CONTAINER"
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/standard:2.0"
privileged_mode = true
}
}
ソース:example-pragmatic-terraform/5.tf at main · tmknom/example-pragmatic-terraform
14.3.3 ビルド仕様
P.171
CodeBuildのビルド処理を規定するのがbuildspec.yml
ファイルとなる。
※今回自分はCIツールに「Capistrano」を使用するので、この工程は割愛...。
14.4 継続的デリバリー
14.4.1 CodePipelineサービスロール
P.173
CodePipelineが使用するIAMロールを作成する。
※こちらの工程も割愛...。
14.4.2 アーティファクトストア
P.175
※CodePipelineの各ステージでデータの受け渡しに使用するアーティファクトストアの作成方法についての説明。
14.4.3 GitHubトークン
P.176
※割愛。
14.4.4 CodePipeline
P.177
※CodePipelineについての説明。
Builldステージ
P.180
リスト14.11
14.4.5 CodePipeline Webhook
P.181
リスト14.12
14.4.6 GitHubプロバイダ
14.4.7 GitHub Webhook

第15章
SSHレスオペレーション
15.1 オペレーションサーバーの設計
P.186
EC2とSession Manager(以降、「SSM」と表記する)を組み合わせた、オペレーションサーバーの構築方法を学ぶ。
オペレーションサーバーの設計で考慮すべきこと
- 運用
- セキュリティ
- トレーサビリティ
15.1.1 運用
- EC2にはDockerだけインストールする
- ECS Fargateにデプロイするイメージを流用する
- 設定情報はSSMパラメータストアから取得し、EC2では管理しない
15.1.2 セキュリティ
- SSMを導入し、SSHログインを不要にする
- インターネットからのアクセスも遮断する
15.1.3 トレーサビリティ
- SSMで操作ログを保存する
- コマンドの実行結果も自動的に残す
15.2 Session Manager(SSM)
P.187
Session Manager(SSM)とは?
→ AWSにて、SSHログインなしにシェルアクセスを実現するサービスのこと。
15.2.1 インスタンスプロファイル
EC2にサービス権限を付与する場合は、IAMロールを関連付けるのではなく、IAMロールをラップしたインスタンスプロファイルを関連付ける。
ポリシードキュメント
P.188
ポリシードキュメントを定義する。
まずはSSMの設定用にssm.tf
ファイルを作成する。
terraform
ディレクトリ直下にssm.tf
ファイルを作成。
touch ssm.tf
作成したssm.tf
ファイルをエディターで開き、以下のように編集する。
リスト15.1:オペレーションサーバー用ポリシードキュメントの定義
# IAM Policy Document (EC2 for SSM)
data "aws_iam_policy_document" "ec2_for_ssm" {
source_json = data.aws_iam_policy.ec2_for_ssm.policy
statement {
effect = "Allow"
resources = ["*"]
actions = [
"s3:PutObject",
"logs:PutLogEvents",
"logs:CreateLogStream",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath",
"kms:Decrypt",
]
}
}
# IAM Policy (EC2 for SSM)
data "aws_iam_policy" "ec2_for_ssm" {
arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
IAMロール
P.189
オペレーションサーバー用のIAMロールを定義する。
ssm.tf
ファイルに以下のコードを追記する。
リスト15.2:オペレーションサーバー用IAMロールの定義
# EC2 for SSM Role
module "ec2_for_ssm_role" {
source = "./iam_role/iam_role"
name = "ec2-for-ssm"
identifier = "ec2.amazonaws.com"
policy = data.aws_iam_policy_document.ec2_for_ssm.json
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
インスタンスプロファイル
P.190
インスタンスプロファイルをEC2インスタンスに関連付ける。
ssm.tf
ファイルに以下のコードを追記する。
リスト15.3:インスタンスプロファイルの定義
# IAM Instance Profile (EC2 for SSM)
resource "aws_iam_instance_profile" "ec2_for_ssm" {
name = "ec2-for-ssm"
role = module.ec2_for_ssm_role.iam_role_name
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
15.2.2 EC2インスタンス
P.190
EC2インスタンスを作成し、オペレーションサーバーを構築する。
ssm.tf
ファイルに以下のコードを追記する。
リスト15.4:オペレーションサーバー用EC2インスタンスの定義
# Instance (Example for Operation)
resource "aws_instance" "example_for_operation" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
iam_instance_profile = aws_iam_instance_profile.ec2_for_ssm.name
subnet_id = aws_subnet.private_0.id
user_data = file("./user_data.sh")
}
output "operation_instance_id" {
value = aws_instance.example_for_operation.id
}
ソース:example-pragmatic-terraform/4.tf at main · tmknom/example-pragmatic-terraform
上記コードによるオペレーションサーバーの構築のため、EC2インスタンス作成時に実行するプロビジョニングスクリプトをuser_data.sh
ファイルに定義する。
user_data.sh
に以下のコードを追記する。
リスト15.5:オペレーションサーバー用User Dataの定義
#!/bin/sh
amazon-linux-extras install -y docker
systemctl start docker
systemctl enable docker
ソース:example-pragmatic-terraform/user_data.sh at main · tmknom/example-pragmatic-terraform
15.2.3 オペレーションログ
P.192
SSMの操作ログを自動保存するために、SSM Documentを作成する。
ログの保存先にはS3バケットを指定する。
ssm.tf
に以下のコードを追記。
リスト15.6:オペレーションログを保存するS3バケットの定義
# Operation Log (for S3)
resource "aws_s3_bucket" "operation" {
bucket = "operation-pragmatic-terraform"
lifecycle_rule {
enabled = true
expiration {
days = "180"
}
}
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
CloudWatch Logs
P.192
オペレーションログ保存先のCloudWatch Logsを定義する。
ssm.tf
に以下のコードを追記。
リスト15.7:オペレーションログを保存するCloudWatch Logsの定義
# Operation Log (for CloudWatch Logs)
resource "aws_cloudwatch_log_group" "operation" {
name = "/operation"
retention_in_days = 180
}
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
SSM Document
P.193
SSM Documentを定義する。
ssm.tf
に以下のコードを追記。
リスト15.8:Session Manager用SSM Documentの定義
# SSM Document
resource "aws_ssm_document" "session_manager_run_shell" {
name = "SSM-SessionManagerRunShell"
document_type = "Session"
document_format = "JSON"
content = <<EOF
{
"schemaVersion": "1.0",
"description": "Document to hold regional settings for Session Manager",
"sessionType": "Standard_Stream",
"inputs": {
"s3BucketName": "${aws_s3_bucket.operation.id}",
"cloudWatchLogGroupName": "${aws_cloudwatch_log_group.operation.name}"
}
}
EOF
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
15.3 ローカル環境
P.194
ローカル環境をセットアップする。
15.3.1 Session Manager Plugin
P.195
Session Manager Pluginをインストールする。
ターミナルで以下のコマンド(5つ)を順番に実行していく。
cd /tmp
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"
unzip sessionmanager-bundle.zip
sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
上記のsudo~
コマンドの実行後、以下のように表示される。
Password:
自身のMacのログインパスワードを入力してEnterキーを押す。
rm -rf sessionmanager-bundle sessionmanager-bundle.zip
最後に、次のコマンドを実行してSession Manager Pluginがインストールされたことを確認する。
session-manager-plugin
上記のsession-manager-plugin
コマンドの実行後、以下のように表示されればSession Manager PluginのインストールはOK。
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
15.3.2 シェルアクセス
P.196
AWS CLI経由でシェルアクセスを試してみる。
※ここでは以降のコマンドについては割愛。本番時にはコマンドを実行してちゃんと確認すること!

第16章
ロギング
16.1 ロギングの種類
P.198
どのサービスがどこにログを保存するのか把握・整理する。
16.1.1 S3へのロギング
P.198
S3へロギングを行なうサービスは以下の通り。
- ALB
- Session Manager(SSM)
- CloudTrail
- VPCフローログ
- S3アクセスログ
16.1.2 CloudWatch Logsへのロギング
P.198
CloudWatch Logsへロギングを行なうサービスは以下の通り。
- ECS
- RDS
- Route 53
- Session Manager(SSM)
- CloudTrail
- VPCフローログ
- Lambda
S3にしろ、CloudWatch Logsにしろ、
- ログを定期的に削除する設定
- ログの保持期間を指定する設定
以上の設定を必ず行なうこと!
16.2 ログ検索
P.199
ELK(Elasticsearch, Logstash, Kibana)スタックなどを使って、自前でログ検索の仕組みを構築することも可能。しかし、今回はAthenaとCloudWatch Logs Insightsを試してみる。
16.2.1 Athena
P.199
Athena(アテナ)とは?
→ S3のデータを直接SQLで検索するサービスのこと。
※AthenaはTerraformで管理するメリットが薄いため、AWSマネジメントコンソールで直接操作する。
...というわけでブラウザでAWSマネジメントコンソールを開き、Athenaのページへと移動。
Athenaのページのメニューにて「クエリエディタ」をクリック。クエリエディタの画面を表示させる。
クエリの入力欄に以下のコードを入力する。
CREATE DATABASE mylog;
[保存]ボタンを押し、クエリを保存する。
今回は、
クエリ名にtest-mylog
クエリの説明にMy log creation test.
以上を入力して...
CREATE EXTERNAL TABLE IF NOT EXISTS alb_logs (
type string,
time string,
elb string,
client_ip string,
client_port int,
target_ip string,
target_port int,
request_processing_time double,
target_processing_time double,
response_processing_time double,
elb_status_code int,
target_status_code string,
received_bytes bigint,
sent_bytes bigint,
request_verb string,
request_url string,
request_proto string,
user_agent string,
ssl_cipher string,
ssl_protocol string,
target_group_arn string,
trace_id string,
domain_name string,
chosen_cert_arn string,
matched_rule_priority string,
request_creation_time string,
actions_executed string,
redirect_url string,
lambda_error_reason string,
target_port_list string,
target_status_code_list string,
classification string,
classification_reason string
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
'serialization.format' = '1',
'input.regex' =
'([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) (.*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-_]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\s]+?)\" \"([^\s]+)\" \"([^ ]*)\" \"([^ ]*)\"')
LOCATION 's3://your-alb-logs-directory/AWSLogs/<ACCOUNT-ID>/elasticloadbalancing/<REGION>/';

第17章
Terraformベストプラクティス
この章ではTerraformの運用・設計について学習する。
17.1 Terraformバージョンを固定する
P.212
チーム開発を見越してTerraformのバージョンを固定させる。
現時点で自身の環境にインストールしているTerraformのバージョンを確認。
terraform --version
terraform
ディレクトリ直下にversion.tf
ファイルを作成する。
touch version.tf
version.tf
ファイルに以下のコードを追記。
リスト17.1:Terraformバージョンの定義
terraform {
required_version = "1.1.7"
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
17.2 プロバイダバージョンを固定する
P.212
Terraformのバージョン同様、プロバイダのバージョンも固定する。
現時点で自身の環境にインストールしているTerraform AWS Providerのバージョンを確認。
terraform providers --version
version.tf
ファイルに以下のコードを追記する。
リスト17.2:プロバイダバージョンの定義
provider "aws" {
version = "4.8.0"
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
そして、ここまでのバージョン固定の設定を行なったら、それを反映させるためにターミナルで以下のコマンドを実行する。
terraform init
17.3 削除操作を抑止する
P.213
削除されると困る重要なリソースの削除操作を抑止する設定を追加する。
s3.tf
ファイルに以下のコードを追記する。
リスト17.3:ライフサイクルによる削除抑止定義
# Suppressing delete operations
resource "aws_s3_bucket" "prevent_destroy_bucket" {
bucket = "prevent-destroy-pragmatic-terraform"
lifecycle {
prevent_destroy = true
}
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
※prevent_destroy = true
←このprevent_destroy
パラメータをtrue
にすることでリソースの削除を抑止する機能が有効になる。
17.4 コードフォーマットをかける
P.214
Terraformのコードフォーマット機能を有効化する。
ターミナルで以下のコマンドを実行すると、Terraformファイルのコードフォーマット機能が有効になる。
terraform fmt -recursive
※-recursive
オプション:サブディレクトリ配下のファイルも含め、すべてのファイルをフォーマットするためのオプション
-check
オプションを付けてterraform fmt
コマンドを実行すると、コマンドの実行時点で存在している.tf
ファイルがフォーマット済みかどうかをチェックすることができる。
terraform fmt -recursive -check
17.5 バリデーションをかける
P.214
作成したTerraformのファイルにバリデーションをかける。
バリデーションの基本コマンドは以下の通り。
terraform varidate
しかし、terraform varidate
コマンドだと、このコマンドを実行したディレクトリ直下のファイルにしかバリデーション機能が実行されない。そこで色々とオプションを付けることですべてのディレクトリ・すべてのファイルに対してバリデーションチェックがされるようにする。
その場合のコマンドは以下の通り。
find . -type f -name '*.tf' -exec dirname {} \; | sort -u | xargs -I {} terraform validate {}
17.6 オートコンプリートを有効にする
P.215
Terraformのオートコンプリート機能を有効化する。
ターミナルで以下のコマンドを実行すると、bashやzshでTerraformファイルのオートコンプリート機能が追加される。
terraform -install-autocomplete
17.7 プラグインキャッシュを有効にする
P.216
terraform init
コマンド実行時にダウンロードされるプロバイダのバイナリファイルのキャッシュを有効化する。
まずはキャッシュの設定用に.terraformrc
ファイルを作成する。
terraform
ディレクトリ直下に.terraformrc
ファイルを作成。
touch .terraformrc
作成した.terraformrc
ファイルをエディターで開き、以下のように編集する。
リスト17.4:プラグインキャッシュの有効化
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
ソース:example-pragmatic-terraform/.terraformrc at main · tmknom/example-pragmatic-terraform
.terraformrc
ファイルの編集ができたら、キャッシュを保存するディレクトリを作成する。
ターミナルで以下のコマンドを実行。
mkdir -p "$HOME/.terraform.d/plugincache"
※.terraformrc
ファイルや上記ディレクトリ作成コマンドにある$HOME
(ホームディレクトリ)は、自身のPCの、現在使用しているアカウントのホームディレクトリに相当する。
17.8 TFLintで不正なコードを検出する
17.8.1 TFLintのインストール
P.217
TFLintとは?
→ Terraformのリンターのこと。TFLintをインストールすることで、terraform plan
コマンドではエラーとして検出されないような不正コードも徹底的に検出されるようになる。
以下のコマンドでTFLintをインストールする。
brew install tflint
TFLintのインストールが完了したら、次のコマンドでインストールしたTFLintのバージョンを確認する。
tflint --version
以下のように表示されればTFLintのインストールはOK。
TFLint version 0.35.0
17.8.2 TFLintの使い方
P.218
TFLintの実行コマンドは以下の通り。
tflint
※上記コマンドでは、サブディレクトリ以下のファイルまではリントしてくれないので注意!!
17.8.3 Deep Checking
P.218
tflint
コマンドに--deep
オプションを付けると、AWS APIを実行してより詳細なチェックを行なうことができる。
tflint --deep --aws-region=ap-northeast-1
※--deep
オプションはTFLintのv0.23.0
で廃止されてしまった模様。なので、新しいバージョンのTFLintで--deep
オプションを使うには、.tflint.hcl
ファイルを作成してプラグインを追加する必要があるとのこと(.tflint.hcl
ファイルの作成方法やプラグインの追加方法等に関しては別途要調査!)。

第18章
AWSベストプラクティス
AWS固有のベストプラクティスを学ぶ。
P.220
18.1 ネットワーク系デフォルトリソースの使用を避ける
- VPC
など
18.2 データストア系デフォルトリソースの使用を避ける
- RDS
- ElastiCache
など
18.3 APIの削除保護機能を活用する
-
aws_db_instance
リソースのdeletion_protection
-
aws_lb
リソースのenable_deletion_protection
-
aws_s3_bucket
リソースのforce_destroy
など
18.4 暗黙的な依存関係を把握する
- EIP →「インターネットゲートウェイ」に依存
- NATゲートウェイ →「インターネットゲートウェイ」に依存
※暗黙的な依存関係がある場合は、depends_on
を定義することで依存関係を明示するべし!
18.5 暗黙的に作られるリソースに注意する
- サービスにリンクされたロール(Service-Linked Role)← IAMロールの特殊版
-
aws_ecs_cluster
リソースとaws_ecs_service
リソースの作成時
-

第19章
高度な構文
この章では第3章の発展として、さらに高度な構文について学習する。
※この章の内容はリファレンス的に活用するとよさそう。
19.1 三項演算子
P.224
Terraformでは、三項演算子を使うことができる。
本番環境とステージング環境でインスタンスタイプを切り替える。
リスト19.1:三項演算子によるインスタンスタイプの切り替え
# Switch instance type
variable "env" {}
resource "aws_instance" "example" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = var.env == "prod" ? "m5.large" : "t3.micro"
}
ソース:example-pragmatic-terraform/1.tf at main · tmknom/example-pragmatic-terraform
上記の設定後、terraform plan
コマンドの実行時にenv変数を切り替えれば、それに応じたplan結果が返ってくる。
本番環境
terraform plan -var 'env=prod'
ステージング環境
terraform plan -var 'env=stage'
19.2 複数リソース作成
P.224
Terraformにはcount
というメタ引数が存在する。
count
を使うことで、複数のリソースを簡単に作成することができる。
リスト19.2:coutによる複数リソースの定義
# Create 3 VPC
resource "aws_vpc" "examples" {
count = 3
cidr_block = "10.${count.index}.0.0/16"
}
ソース:example-pragmatic-terraform/2.tf at main · tmknom/example-pragmatic-terraform
19.3 リソース作成制御
P.225
三項演算子とcount
を組み合わせると、リソース作成を制御できる。
試しにsecurity_group
モジュールを実装してみる。
リスト19.3:リソース作成を制御するモジュールの定義
# Security Group Module
variable "allow_ssh" {
type = bool
}
resource "aws_security_group" "example" {
name = "example"
}
resource "aws_security_group_rule" "egress" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.example.id
}
resource "aws_security_group_rule" "ingress" {
count = var.allow_ssh ? 1 : 0
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.example.id
}
output "allow_ssh_rule_id" {
value = join("", aws_security_group_rule.ingress[*].id)
}
ソース:example-pragmatic-terraform/3.tf at main · tmknom/example-pragmatic-terraform
リスト19.4ではSSHを許可するセキュリティグループルールが作成される。
リスト19.4:「SSHを許可するセキュリティグループルール」を作成する
# Security Group (allow SSH)
module "allow_ssh" {
source = "./security_group/security_group"
allow_ssh = true
}
output "allow_ssh_rule_id" {
value = module.allow_ssh.allow_ssh_rule_id
}
ソース:example-pragmatic-terraform/4.tf at main · tmknom/example-pragmatic-terraform
SSHを許可するセキュリティグループルールを作成しない場合は、次のリスト19.5のように実装する。
リスト19.5:「SSHを許可するセキュリティグループルール」を作成しない
# Security Group (not allow SSH)
module "disallow_ssh" {
source = "./security_group/security_group"
allow_ssh = false
}
output "disallow_ssh_rule_id" {
value = module.disallow_ssh.allow_ssh_rule_id
}
ソース:example-pragmatic-terraform/5.tf at main · tmknom/example-pragmatic-terraform
19.4 主要なデータソース
19.4.1 AWSアカウントID
P.228
aws_caller_identify
データソースを使うことで、自身のAWSアカウントIDを取得することができる。
リスト19.6:AWSアカウントIDの取得
# Get My AWS Account ID
data "aws_caller_identity" "current" {}
output "account_id" {
value = data.aws_caller_identity.current.account_id
}
ソース:example-pragmatic-terraform/6.tf at main · tmknom/example-pragmatic-terraform
19.4.2 リージョン
P.228
aws_resion
データソースを使うことで、リージョンを取得することができる。
リスト19.7:リージョンの取得
# Get Region
data "aws_region" "current" {}
output "region_name" {
value = data.aws_region.current.name
}
ソース:example-pragmatic-terraform/7.tf at main · tmknom/example-pragmatic-terraform
19.4.3 アベイラビリティゾーン
P.229
aws_availability_zones
データソースを使うことで、アベイラビリティゾーンをリストで取得することができる。
リスト19.8:アベイラビリティゾーンのリストの取得
# Get Availability Zones List
data "aws_availability_zones" "available" {
state = "available"
}
output "availability_zones" {
value = data.aws_availability_zones.available.names
}
ソース:example-pragmatic-terraform/8.tf at main · tmknom/example-pragmatic-terraform
19.4.4 サービスアカウント
P.229
第6章のリスト6.5では、「582318560864」という特殊なアカウントが登場するが、これはALBの「サービスアカウント」と呼ばれるものである。aws_elb_service_account
データソースをリスト19.9のように定義すると、ALBのサービスアカウントを取得することができる。
リスト19.9:ALBのサービスアカウントの取得
# Get ELB Service Account
data "aws_elb_service_account" "current" {}
output "alb_service_account_id" {
value = data.aws_elb_service_account.current.id
}
ソース:example-pragmatic-terraform/9.tf at main · tmknom/example-pragmatic-terraform
※aws_cloudtrail_service_account
データソースやaws_redshift_service_account
データソースなど、他のサービスアカウントのデータソースも提供されている。
19.5 主要な組込関数
P.230
Terraformの組み込み関数を試すには、ターミナルでterraform console
コマンドを実行してみるといい。
terraform console
コンソールを終了するにはexit
と入力し、Enterキーを押す。
exit
19.5.1 Numeric Functions
P.231
Terraformでは、数値演算のために以下の関数が提供されている。
- max関数
- floor関数
- pow関数
max関数
実行コマンド
max(1, 100, 10)
実行結果
100
19.5.2 String Functions
P.231
Terraformでは、文字列操作のために以下の関数が提供されている。
- substr関数
- format関数
- split関数
substr関数
実行コマンド
substr("Pragmatic Terraform on AWS", 10, 9)
※部分文字列を取得する
実行結果
Terraform
19.5.3 Collection Functions
P.231
Terraformでは、コレクション操作のために以下の関数が提供されている。
- flatten関数
- concat関数
- length関数
flatten関数
実行コマンド
flatten([["Pragmatic"], ["Terraform", ["on", "AWS"]]])
※多次元配列を1次元配列に変換する
実行結果
[
"Pragmatic",
"Terraform",
"on",
"AWS"
]
19.5.4 Filesystem Functions
P.232
Terraformでは、ファイル操作のために以下の関数が提供されている。
- templatefile関数
- fileexists関数
- file関数
templatefile関数
まずはテンプレートファイルinstall.sh
を作成し
touch install.sh
作成したinstall.shファイルを以下のように編集する。
リスト19.10:テンプレートファイルの定義
#!/bin/bash
yum install -y ${package}
ソース:example-pragmatic-terraform/install.sh at main · tmknom/example-pragmatic-terraform
以上のファイルを用意した後は、templatefile関数の実行時にpackage
に変数を埋め込むことができる。
実行コマンド
templatefile("${path.module}/install.sh", { package = "httpd" })
実行結果
#!/bin/bash
yum install -y httpd
19.5.5 その他の組み込み関数
P.233
Terraformでは、他にも以下のような関数が提供されている。
- Encoding Functions:エンコード・デコードの関数
- Date and Time Functions:日時関数
- Hash and Crypto Functions:ハッシュ化・暗号化の関数
- IP Network Functions:IPネットワークの関数
- Type Conversion Functions:型変換の関数
19.6 ランダム文字列
- リスト13.2のマスターパスワード
- リスト14.12の秘密鍵
以上のようなランダム性が求められる値では、Randomプロバイダのrandom_string
リソースを使うとよい。
実際にrandom_string
リソースを使って、パスワードを自動生成してみる。
リスト19.11:パスワードのランダム生成
# Random String (Password)
provider "random" {}
resource "random_string" "password" {
length = 32
special = false
}
ソース:example-pragmatic-terraform/11.tf at main · tmknom/example-pragmatic-terraform
※DBインスタンスのマスターパスワードには一部の特殊文字が使えないため、special = false
のコードで特殊文字を抑制している。
続けて以下のコードを実装し、先ほどランダム生成したパスワードをDBインスタンスのマスターパスワードに設定してみる。
リスト19.12:DBインスタンスにRandomプロバイダが生成したパスワードを設定
resource "aws_db_instance" "example" {
engine = "mysql"
instance_class = "db.t3.small"
allocated_storage = 20
skip_final_snapshot = true
username = "admin"
password = random_string.password.result
}
ソース:example-pragmatic-terraform/12.tf at main · tmknom/example-pragmatic-terraform
19.7 Multipleプロバイダ
P.235
複数のリージョンにリソースを作成する方法。
リスト19.13のように実装し、実際に複数のリージョンにリソースを作成してみる。
リスト19.13:Multipleプロバイダの定義
# Multiple Provider
provider "aws" {
alias = "virginia"
region = "us-east-1"
}
provider "aws" {
region = "ap-northeast-1"
}
ソース:example-pragmatic-terraform/13.tf at main · tmknom/example-pragmatic-terraform
※alias
が未定義のプロバイダはデフォルトプロバイダとして扱われる。というわけで、上記実装の場合はap-northeast-1
(アジアパシフィック(東京))がデフォルトとなる。
19.7.1 リソースのマルチリージョン定義
P.235
リソースをマルチリージョンで定義するには、リスト19.14のように実装する。
リスト19.14:リソースのマルチリージョン定義
# Multiple Provider (Resource)
resource "aws_vpc" "virginia" {
provider = aws.virginia
cidr_block = "192.168.0.0/16"
}
resource "aws_vpc" "tokyo" {
cidr_block = "192.168.0.0/16"
}
output "virginia_vpc" {
value = aws_vpc.virginia.arn
}
output "tokyo_vpc" {
value = aws_vpc.tokyo.arn
}
ソース:example-pragmatic-terraform/14.tf at main · tmknom/example-pragmatic-terraform
※resource "aws_vpc" "tokyo"
の方にはprovider
の定義がないが、こちらはデフォルトとして設定しているap-northeast-1
(アジアパシフィック(東京))に自動的に作成されるため不要なのだろう。たぶん...。
19.7.2 モジュールのマルチリージョン定義
P.237
今度はvpcモジュールをリスト19.15のように実装する。
まず、terraform
ディレクトリにいる状態で、vpcモジュールのファイルを置くためのvpc
ディレクトリを作成する。
mkdir vpc
作成したvpc
ディレクトリに移動。
cd vpc
vpcのモジュール化を定義するファイルを作成する。ファイル名はvpc.tf
とする。
touch vpc.tf
作成したvpc.tf
ファイルをエディターで開き、以下のように編集する。
リスト19.15:マルチリージョン用のvpcモジュールの定義
resource "aws_vpc" "default" {
cidr_block = "192.168.0.0/16"
}
output "vpc_arn" {
value = aws_vpc.default.arn
}
ソース:example-pragmatic-terraform/15.tf at main · tmknom/example-pragmatic-terraform
vpc.tf
ファイルの編集が済んだらターミナルでterraform
ディレクトリに戻り、
cd ../
以下のコードをmain.tf
ファイルに実装し、モジュールをマルチリージョンで定義する。
リスト19.16:モジュールのマルチリージョン定義
# Multiple Provider (VPC)
module "virginia" {
source = "./vpc/vpc"
providers = {
aws = aws.virginia
}
}
module "tokyo" {
source = "./vpc"
}
output "module_virginia_vpc" {
value = module.virginia.vpc_arn
}
output "module_tokyo_vpc" {
value = module.tokyo.vpc_arn
}
ソース:example-pragmatic-terraform/16.tf at main · tmknom/example-pragmatic-terraform
※module "tokyo"
の方にproviders
の定義がないが、これもまたap-northeast-1
(アジアパシフィック(東京))がデフォルトとして設定されているおかげで、東京リージョンの方には自動的にモジュールが作成される。
19.8 Dynamic blocks
P.238
Dynamic blocksを使うと動的にブロック要素を生成することができる。

Terraformのコマンドまとめ
【一番最初に使うコマンド】
作業用ディレクトリを初期化する|terraform init
コマンド
terraform init
【よく使うコマンド】
Terraformのリントを実行する|tflint
コマンド
tflint
Terraformの実行計画を出力する|terraform plan
コマンド
terraform plan
terraform plan
コマンドによって、「今の設定でterraform apply
を実行したらこうなりますよ」というのが確認できる
TerraformでAWSにリソースを作成する|terraform apply
コマンド
terraform apply
Terraformで作成したAWSのリソースを削除する|terraform destroy
コマンド
terraform destroy
Terraformコンソールを実行する|terraform console
コマンド
terraform console