目次

はじめに

Terraformとは
本書の構成
対象読者
開発環境
サンプルコード
免責事項
表記関係について
底本について

第1章 セットアップ

1.1 AWS
1.2 Terraform
1.3 git-secrets

第2章 基本操作

2.1 リソースの作成
2.2 リソースの更新
2.3 tfstateファイル
2.4 リソースの削除

第3章 基本構文

3.1 変数
3.2 ローカル変数
3.3 出力値
3.4 データソース
3.5 プロバイダ
3.6 参照
3.7 組み込み関数
3.8 モジュール

第4章 全体設計

4.1 システム要件
4.2 アーキテクチャ設計
4.3 テクノロジースタック
4.4 ファイルレイアウト

第5章 権限管理

5.1 ポリシー
5.2 ロール

第6章 ストレージ

6.1 プライベートバケット
6.2 パブリックバケット
6.3 ログバケット

第7章 ネットワーク

7.1 パブリックネットワーク
7.2 プライベートネットワーク
7.3 マルチAZ
7.4 ファイアウォール

第8章 ロードバランサーとDNS

8.1 ALBの構成要素
8.2 HTTP用ロードバランサー
8.3 Route 53
8.4 ACM(AWS Certificate Manager)
8.5 HTTPS用ロードバランサー
8.6 リクエストフォワーディング

第9章 コンテナオーケストレーション

9.1 ECSの構成要素
9.2 ECSの起動タイプ
9.3 Webサーバーの構築
9.4 Fargateにおけるロギング

第10章 バッチ

10.1 バッチ設計
10.2 ECS Scheduled Tasks

第11章 鍵管理

11.1 KMS(Key Management Service)

第12章 設定管理

12.1 コンテナの設定管理
12.2 SSMパラメータストア

第13章 データストア

13.1 RDS(Relational Database Service)
13.2 ElastiCache

第14章 デプロイメントパイプライン

14.1 デプロイメントパイプラインの設計
14.2 コンテナレジストリ
14.3 継続的インテグレーション
14.4 継続的デリバリー

第15章 SSHレスオペレーション

15.1 オペレーションサーバーの設計
15.2 Session Manager
15.3 ローカル環境

第16章 ロギング

16.1 ロギングの種類
16.2 ログ検索
16.3 ログ永続化

第17章 Terraformベストプラクティス

17.1 Terraformバージョンを固定する
17.2 プロバイダバージョンを固定する
17.3 削除操作を抑止する
17.4 コードフォーマットをかける
17.5 バリデーションをかける
17.6 オートコンプリートを有効にする
17.7 プラグインキャッシュを有効にする
17.8 TFLintで不正なコードを検出する

第18章 AWSベストプラクティス

18.1 ネットワーク系デフォルトリソースの使用を避ける
18.2 データストア系デフォルトリソースの使用を避ける
18.3 APIの削除保護機能を活用する
18.4 暗黙的な依存関係を把握する
18.5 暗黙的に作られるリソースに注意する

第19章 高度な構文

19.1 三項演算子
19.2 複数リソース作成
19.3 リソース作成制御
19.4 主要なデータソース
19.5 主要な組み込み関数
19.6 ランダム文字列
19.7 Multipleプロバイダ
19.8 Dynamic blocks

第20章 tfstateファイルの管理

20.1 ステートバケット
20.2 Terraform Cloud

第21章 構造化

21.1 モノリス
21.2 モジュールの分離
21.3 独立した環境
21.4 コンポーネント分割
21.5 依存関係の制御

第22章 モジュール設計

22.1 モジュールの設計原則
22.2 優れたモジュールの構成要素
22.3 公開モジュール

第23章 リソース参照パターン

23.1 リテラル
23.2 リモートステート
23.3 SSMパラメータストア連携
23.4 データソースと依存関係の分離
23.5 Data-only Modules

第24章 リファクタリング

24.1 tfstateファイルのバックアップ
24.2 ステートの参照
24.3 ステートの上書き
24.4 ステートからリソースを削除
24.5 リネーム
24.6 tfstateファイル間の移動

第25章 既存リソースのインポート

25.1 terraform import
25.2 terraformer

第26章 チーム開発

26.1 ソースコード管理
26.2 ブランチ戦略
26.3 レビュー
26.4 Apply戦略
26.5 コンテキストの理解

第27章 継続的Apply

27.1 ワークフロー
27.2 apply実行環境
27.3 ビルドリポジトリ
27.4 tfnotify
27.5 Branch protection rules

第28章 落ち穂拾い

28.1 高速化
28.2 デバッグログ
28.3 JSONコメント
28.4 Terraformのアップグレード
28.5 AWSプロバイダのアップグレード
28.6 周辺ツールの探し方
28.7 公式ドキュメントを読むコツ
28.8 構成ドリフト
28.9 未知の未知

付録A 巨人の肩の上に乗る

A.1 Terraform
A.2 AWS
A.3 インフラストラクチャ
A.4 システムアーキテクチャ
A.5 ソフトウェア設計

おわりに

謝辞
あなたへ

はじめに

 『実践Terraform』では、AWSを題材にTerraformの設計と実装を学びます。ECS Fargateなどのマネージドサービスを中心にアーキテクチャ設計を行い、Terraformで実装していきます。

 変更しやすいコードを維持し、本番運用に強いシステムを構築するために必要となるプラクティスもあわせて紹介します。200以上のサンプルコードを用意したので、ぜひ手を動かしながら一緒に学びましょう。

Terraformとは

 Terraformはインフラストラクチャを安全かつ効率的に管理するためのツールです。Terraformを使うと、宣言的なコードでインフラストラクチャを記述できます。コードをバージョン管理することで、システムの変更履歴を簡単に追跡できるようになります。

 Terraformはインフラストラクチャを変更する前に、これからなにが起きるのかを教えてくれます。変更を反映する前に、本当に実行してよいか判断する機会が与えられるため、安心してシステムを変更できます。またTerraformは自動的に依存関係を解決するため、手作業にありがちなヒューマンエラーを大幅に削減します。

本書の構成

 本書は「入門編」「実践編」「運用・設計編」の3部で構成されています。

 第1章から第3章までが「入門編」です。第1章でAWSとTerraformのセットアップを行います。そして第2章と第3章で、Terraformの基礎知識を一気に学びます。

 第4章から第16章までが「実践編」です。第4章でシステムの全体設計を行います。そして第5章から第16章では、AWSの各種リソースをTerraformで実装します。

 第17章から第28章が「運用・設計編」です。コードの構造化・リファクタリング・モジュール設計・チーム開発など、本番運用に欠かせない考え方を学びます。

対象読者

 対象読者は「Terraformに関心のある人すべて」です。

 Terraformが未経験であれば、最初から読みはじめましょう。AWSの経験が多少あれば、すんなり入門できます。

 Terraformの基礎知識がすでにあるなら、第5章から第16章の気になる章をチェックしましょう。本番運用を想定した、各種AWSリソースの構築方法を習得できます。

 Terraformを本番環境で運用している人には、第17章以降が役立つでしょう。ソフトウェアの設計原則をベースにしたTerraformの設計技法や、運用ノウハウを凝縮してお届けします。

開発環境

 動作確認はすべて、macOS上で行っています。Terraformのバージョンは0.12.5、AWSプロバイダのバージョンは2.20.0です。また本書では、次のツールはインストール済みという前提で進めます。インストールされていない場合は、事前にインストールを済ませておきましょう。

 ・Homebrew1

 ・Git2

 ・Docker3

 なお本書では、たくさんのAWSのサービスを扱います。無料枠ではないリソースも多いので、不要になったリソースは適宜削除しましょう。

サンプルコード

 すべてのサンプルコードはGitHubで公開しています。

 ・https://github.com/tmknom/example-pragmatic-terraform

免責事項

 本書に記載された内容は、情報の提供のみを目的としています。したがって、本書を用いた開発、製作、運用は、必ずご自身の責任と判断によって行ってください。これらの情報による開発、製作、運用の結果について、著者はいかなる責任も負いません。

表記関係について

 本書に記載されている会社名、製品名などは、一般に各社の登録商標または商標、商品名です。会社名、製品名については、本文中では©、®、™マークなどは表示していません。

底本について

 本書籍は、技術系同人誌即売会「技術書典6」で頒布されたものを底本としています。

第1章 セットアップ

 本章ではmacOSを前提に、AWSとTerraformのセットアップを行います。Terraform専用のIAMユーザーを作成してアクセスキーを払い出し、AWS CLIとTerraformをインストールします。

1.1 AWS

 AWSでIAMユーザーを作成し、アクセスキーを払い出します。あわせてAWS CLIをインストールし、コマンドラインからAWSのAPIを操作できるようにします。

1.1.1 IAMユーザー

 Terraform用のIAMユーザーを作成します。ブラウザーからAWSマネジメントコンソールのIAM1に移動し、「(1)ユーザー」「(2)ユーザーを追加」の順にクリックします(図1.1)。

図1.1: IAM

 「(1)ユーザー名」を入力します。「(2)プログラムによるアクセス」をチェックして、「(3)次のステップ: アクセス権限」をクリックします(図1.2)。

 なお、「AWSマネジメントコンソールへのアクセス」のチェックは外します。このIAMユーザーはAWSマネジメントコンソールへのサインインには使用しないため、パスワードの生成は行いません。

図1.2: ユーザー詳細の設定

 「(1)既存のポリシーを直接アタッチ」をクリックします。「(2)AdministratorAccess」をチェックして、「(3)次のステップ: タグ」をクリックします(図1.3)。

図1.3: アクセス許可の設定





 タグは追加せず「次のステップ: 確認」をクリックします(図1.4)。

図1.4: タグの追加

 内容を確認し、「ユーザーの作成」をクリックします(図1.5)。

図1.5: 確認

 するとIAMユーザーが作成され、アクセスキーが表示されます(図1.6)。それではブラウザーを開いたまま、次に進みます。

図1.6: アクセスキーの表示

1.1.2 AWS CLI

 AWS CLIは、Pythonパッケージマネージャーのpip3からインストールできます。

$ pip3 install awscli --upgrade

 インストールできたら、AWS CLIのバージョンを確認します。

$ aws --version

aws-cli/1.16.198 Python/3.7.4 Darwin/18.6.0 botocore/1.12.188

1.1.3 クレデンシャル

 AWSマネジメントコンソール(図1.6)からアクセスキーIDとシークレットアクセスキーをコピーし、環境変数に設定します。

$ export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE

$ export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

$ export AWS_DEFAULT_REGION=ap-northeast-1

 確認しましょう。AWSアカウントIDが出力されれば、正しく設定されています。

$ aws sts get-caller-identity --query Account --output text

123456789012

 なお、「AdministratorAccess」ポリシーがアタッチされたアクセスキーの権限は強力です。間違っても流出しないよう、扱いには細心の注意を払いましょう。

1.2 Terraform

 とりあえずTerraformを試すならHomebrewが手軽です。しかし、実運用では頻繁にTerraformのバージョンアップが発生します。そこで「tfenv」や「Dockernized Terraform」で、複数のバージョンを切り替えられるようにしましょう。

 なお、TerraformでAWSを操作するには「1.1.3 クレデンシャル」の設定が必要です。

1.2.1 Homebrew

 Homebrewを使う場合、次のようにインストールできます。

$ brew install terraform

 インストールできたら、Terraformのバージョンを確認します。

$ terraform --version

Terraform v0.12.5

1.2.2 tfenv

 tfenv2はTerraformのバージョンマネージャーです。tfenvを使うと、異なるバージョンのTerraformを簡単に扱えます。

tfenvのインストール

 tfenvはHomebrewでインストールできます。

$ brew install tfenv

 インストールできたら、tfenvのバージョンを確認します。

$ tfenv --version

tfenv 1.0.1

tfenvの使い方

 「list-remote」コマンドで、インストールできるバージョンの一覧を取得できます。

$ tfenv list-remote

0.12.5

0.12.4

0.12.3

 「install」コマンドを使って、0.12.5をインストールしましょう。

$ tfenv install 0.12.5

 インストールできたら0.12.4など、他のバージョンもインストールします。いくつかインストールしたら「list」コマンドを使って、インストール済みのバージョンを確認します。すると、最後にインストールしたバージョンが自動的に選択されています。

$ tfenv list

  0.12.5

* 0.12.4 (set by /usr/local/Cellar/tfenv/1.0.1/version)

 そこで「use」コマンドを使い、バージョンを切り替えます。

$ tfenv use 0.12.5

 たったこれだけでバージョンが切り替わります。確認しましょう。

$ tfenv list

* 0.12.5 (set by /usr/local/Cellar/tfenv/1.0.1/version)

  0.12.4

.terraform-version

 チーム開発の場合は、「.terraform-version」ファイルをリポジトリに含めましょう。

$ echo 0.12.5 > .terraform-version

$ tfenv install

 このファイルにバージョンを記述すると、チームメンバーが「tfenv install」コマンドを実行するだけで、バージョンを統一できます。

1.2.3 Dockernized Terraform

 TerraformはDocker Hub3で公式イメージが配布されています。Dockerさえ入っていればどこでも実行できるシンプルさが魅力です。まずdocker pullします。

$ docker pull hashicorp/terraform:0.12.5

 バージョンを確認します。

$ docker run --rm hashicorp/terraform:0.12.5 --version

Terraform v0.12.5

 実行時にはソースコードがあるディレクトリをボリュームマウントして、作業ディレクトリを指定します。またAWSのクレデンシャルは、明示的に環境変数として渡します。

$ docker run --rm -i -v $PWD:/work -w /work \

  -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \

  -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \

  -e AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION \

  hashicorp/terraform:0.12.5 <command>

 Dockernized Terraformの欠点はコマンドが長いことです。この方法を採用する場合、エイリアスの設定やラッパーシェルの実装など、工夫が必要です。

1.3 git-secrets

 クレデンシャル流出防止のために、「git-secrets4」を導入しましょう。git-secretsはアクセスキーやパスワードのような秘匿情報をGitでコミットしようとすると、警告してくれます。git-secretsはHomebrewでインストールできます。

$ brew install git-secrets

 インストールできたらGitに設定します。

$ git secrets --register-aws --global

$ git secrets --install ~/.git-templates/git-secrets

$ git config --global init.templatedir '~/.git-templates/git-secrets'

 以後は誤って秘匿情報をコミットしようとすると、エラーになります。

$ git commit -m "sample commit"

main.tf:2:  access_key = "AKIAIOSFODNN7EXAMPLE"

main.tf:3:  secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"


[ERROR] Matched one or more prohibited patterns

第2章 基本操作

 本章ではTerraformの基本操作を学びます。TerraformでEC2インスタンスを作成し、設定変更・再作成を行います。最後にEC2インスタンスを削除し、Terraformによるリソース管理のライフサイクルを一巡します。

2.1 リソースの作成

 事前準備として、まずは適当なディレクトリに「main.tf」というファイルを作ります。

$ mkdir example

$ cd example

$ touch main.tf

2.1.1 HCL(HashiCorp Configuration Language)

 作成したmain.tfをエディターで開き、リスト2.1のように実装します。このコードではAmazon Linux 2のAMIをベースに、EC2インスタンスを作成します。

リスト2.1: EC2インスタンスの定義

 1: resource "aws_instance" "example" {
 2:   ami           = "ami-0c3fd0f5d33134a76"
 3:   instance_type = "t3.micro"
 4: }

 TerraformのコードはHCL (HashiCorp Configuration Language)という言語で実装します。HCLはTerraformを開発している、HashiCorp社が設計した言語です。EC2インスタンスのようなリソースは「resource」ブロックで定義します。

2.1.2 terraform init

 コードを書いたら「terraform init」コマンドを実行し、リソース作成に必要なバイナリファイルをダウンロードします。「Terraform has been successfully initialized!」と表示されていれば成功です。



$ terraform init

Initializing the backend...


Terraform has been successfully initialized!

2.1.3 terraform plan

 次は「terraform plan」コマンドです。このコマンドを実行すると『実行計画』が出力され、これからなにが起きるのか、Terraformが教えてくれます。

$ terraform plan

An execution plan has been generated and is shown below.

Resource actions are indicated with the following symbols:

  + create


Terraform will perform the following actions:


  # aws_instance.example will be created

  + resource "aws_instance" "example" {

      + ami                          = "ami-0c3fd0f5d33134a76"

      + id                           = (known after apply)

      + instance_type                = "t3.micro"

      ......

    }


Plan: 1 to add, 0 to change, 0 to destroy.

 『+』マークとともに「aws_instance.example will be created」というメッセージが出力されています。これは「新規にリソースを作成する」という意味です。

2.1.4 terraform apply

 今度は「terraform apply」コマンドを実行します。このコマンドでは、あらためてplan結果が表示され、本当に実行していいか確認が行われます。






$ 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:

 「Enter a value:」と表示され『yes』と入力すると、リソース作成を実行します1

aws_instance.example: Still creating... [10s elapsed]

aws_instance.example: Creation complete after 13s [id=i-0ac33b62500ad8067]


Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

 AWSマネジメントコンソールでも、インスタンスの作成を確認できます(図2.1)。

図2.1: 「EC2の作成」をAWSマネジメントコンソールで確認

2.2 リソースの更新

 リソースの作成に成功したら、変更してみましょう。

2.2.1 リソースの設定変更

 リスト2.1をリスト2.2のように変更し、タグを追加します。

リスト2.2: タグを追加

 1: resource "aws_instance" "example" {
 2:   ami           = "ami-0c3fd0f5d33134a76"
 3:   instance_type = "t3.micro"
 4:
 5:   tags = {
 6:     Name = "example"
 7:   }
 8: }




 コードを修正したら、terraform applyを実行します。

$ terraform apply

  # aws_instance.example will be updated in-place

  ~ resource "aws_instance" "example" {

      ~ tags                         = {

          + "Name" = "example"

        }

    }


Plan: 0 to add, 1 to change, 0 to destroy.

 『+』マークから『~』マークに変化して「aws_instance.example will be updated in-place」というメッセージが出力されています。これは「既存のリソースの設定を変更する」という意味です。では、変更を反映します。

aws_instance.example: Modifying... [id=i-0ac33b62500ad8067]

aws_instance.example: Modifications complete after 2s [id=i-0ac33b62500ad8067]


Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

 AWSマネジメントコンソールでも、Nameタグの追加が確認できます(図2.2)。

図2.2: 「EC2へのタグの付与」をAWSマネジメントコンソールで確認

2.2.2 リソースの再作成

 Apacheをインストールするため、リスト2.3のように変更してapplyします。

リスト2.3: User DataでApacheをインストール

 1: resource "aws_instance" "example" {
 2:   ami           = "ami-0c3fd0f5d33134a76"
 3:   instance_type = "t3.micro"
 4:
 5:   user_data = <<EOF
 6:     #!/bin/bash
 7:     yum install -y httpd
 8:     systemctl start httpd.service
 9: EOF
10: }

$ terraform apply

  # aws_instance.example must be replaced

-/+ resource "aws_instance" "example" {

        ami       = "ami-0c3fd0f5d33134a76"

      ~ id        = "i-0ac33b62500ad8067" -> (known after apply)

      + user_data = "655c303ddd9e02635f849fe2993693f147" # forces replacement

    }


Plan: 1 to add, 0 to change, 1 to destroy.

 今度は『-/+』マークがつき「aws_instance.example must be replaced」というメッセージが出力されています。これは「既存のリソースを削除して新しいリソースを作成する」という意味です。

 このメッセージは要注意です。これはリソース削除を伴うため、場合によってはサービスダウンを引き起こします。そのため、リソースが再作成される場合は念入りに確認すべきです。では、実行しましょう。

aws_instance.example: Destroying... [id=i-0ac33b62500ad8067]

aws_instance.example: Still destroying... [id=i-0ac33b62500ad8067, 10s elapsed]

aws_instance.example: Destruction complete after 20s

aws_instance.example: Creating...

aws_instance.example: Still creating... [10s elapsed]

aws_instance.example: Creation complete after 13s [id=i-0afd75edebabb1319]


Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

 AWSマネジメントコンソールで確認すると、最初のインスタンスがterminatedになり、新しいインスタンスが立ち上がっています(図2.3)。

図2.3: 「EC2の再作成」をAWSマネジメントコンソールで確認

 このように、Terraformによるリソースの更新は、「既存リソースをそのまま変更する」ケースと「リソースが作り直しになる」ケースがあります。本番運用では、意図した挙動になるか、plan結果をきちんと確認することが大切です。

2.3 tfstateファイル

 ここまでTerraformは、必要な部分だけを適切に変更してくれました。しかし、どうやってその判断をしているのでしょう。その答えが「tfstateファイル」です。

 applyを一度でも実行していれば、terraform.tfstateファイルが作成されます。ファイルの中身を確認してみましょう。

$ cat terraform.tfstate

{

  "version": 4,

  "terraform_version": "0.12.5",

  "serial": 6,

  "lineage": "4c3075a8-4c5c-73d0-5bce-f88ae39a4e3b",

  "outputs": {},

  "resources": [

    {

      "mode": "managed",

      "type": "aws_instance",

      "name": "example",

      "provider": "provider.aws",

      "instances": [

        {

          "schema_version": 1,

          "attributes": {

            "ami": "ami-0c3fd0f5d33134a76",

            "id": "i-0afd75edebabb1319",

            "user_data": "655c303ddd9e02635f849fe2993693f147f4baf1",

            ......

 tfstateファイルはTerraformが生成するファイルで、現在の状態を記録します。Terraformはtfstateファイルの状態とHCLのコードに差分があれば、その差分のみを変更するよう振る舞います。

 デフォルトではローカルでtfstateファイルを管理しますが、リモートのストレージでも管理できます。詳細は第20章「tfstateファイルの管理」で学びます。

2.4 リソースの削除

 せっかく作ったリソースですが、「terraform destroy」コマンドで削除しましょう。

$ terraform destroy

  # aws_instance.example will be destroyed

  - resource "aws_instance" "example" {

      - ami                          = "ami-0c3fd0f5d33134a76" -> null

      - id                           = "i-0afd75edebabb1319" -> null


Plan: 0 to add, 0 to change, 1 to destroy.

 ここでは『-』マークがつき「aws_instance.example will be destroyed」というメッセージが出力されました。これは「リソースを削除する」という意味です。applyコマンド同様に、実行していいか確認が行われるのでyesと入力して、削除を実行します。

aws_instance.example: Destroying... [id=i-0afd75edebabb1319]

aws_instance.example: Still destroying... [id=i-0afd75edebabb1319, 10s elapsed]

aws_instance.example: Destruction complete after 20s


Destroy complete! Resources: 1 destroyed.

 AWSマネジメントコンソールを確認すると、インスタンスがterminatedになり、きちんと削除されています(図2.4)。

図2.4: 「EC2の削除」をAWSマネジメントコンソールで確認

Terraform 0.11以前のバージョン

 2019年5月にリリースされたTerraform 0.12では、言語仕様が拡張されました。そのため、0.11以前のバージョンと互換性がありません。

 そこで調べ物をするときは、どのバージョンについての情報なのか、きちんと把握することが大切です。バージョンが明示されていないときは記事の日付を確認し、古い情報に惑わされないよう注意しましょう。

1. デフォルトVPCが削除されているとエラーになります。「VPCIdNotSpecified: No default VPC for this user」のようなエラーメッセージが出た場合、デフォルトVPCの再作成が必要です。

試し読みはここまでです。
この続きは、製品版でお楽しみください。