LoginSignup
1
3

Terraformの自分用まとめ

Last updated at Posted at 2023-11-07

クラウドインフラの構成管理をするデファクトスタンダードとしての地位を確立しているTerraform。
この記事は、以下についてまとめたものである。

  • Terraformの概要
    • 動作イメージ
    • 利用フロー
  • Terraformの利用
    • ローカル環境の構築方法
    • Terraformのディレクトリ・ファイル構成
    • .tfファイルの記述方法
    • 主要なterraformコマンド
    • 今後参照することが多いと思われる公式ドキュメント

(TODOになっているところも今後更新していく予定です)

1. Terraformの概要

  • Terraformは、HashiCorp社(Vagrant, Vault)によってGo言語で開発されたOSSで、Infratructure as Codeを実現するツール。
  • AWS,GCP,Azureの他、DatadogやHerokuなど様々なクラウドインフラの構成管理ができる
    • 類似ツールにCloudFormationがあるが、こちらはAWSのインフラリソースのみが対象
    • ちなみに、クラウドインフラ以外にローカルのDocker環境の構成管理もできる
  • インフラ構成の定義は、.tfという拡張子のファイルに、各インフラのリソースの定義をコードで記述することによって行う。
    • 具体的には、各インフラの初期化、更新、削除といった内容を記述する。
    • .tfファイルに記述されたコードに基づいてTerraformが自動でインフラを構築してくれる。
      • 利用者は、インフラリソースの構築・設定順を気にする必要はあまりなく、インフラリソース間の依存関係は基本的にTerraform側でいい感じに考慮して制御してくれる。
      • 冪等性の担保もしてくれて、コードとして記述している内容が対象のインフラリソースですでに実現されている場合、その構築処理をスキップしてくれる。
  • Terraformの主な構成要素は、TERRAFORM CORETERRAFORM PROVIDERである。

1.1. 動作イメージ

[.tfファイル]
↓
↓
↓
---------------- Terraform ------------------
[TERRAFORM CORE]
↓
↓(gRPC)
↓
[TERRAFORM PROVIDER]
↓
↓(Golang)
↓
[各社クラウドインフラが提供するSDK(API)]
---------------------------------------------
↓
↓(HTTP(s))
↓
[各社クラウドインフラのリソース]

TERRAFORM PROVIDER

  • HassiCorp社とTerraformコミュニティによってGolangで開発されているプラグインである。以下単にプロバイダと呼ぶ。
  • .tfファイルの設定に基づき、各社クラウドから提供されているAPI, SDKを利用していい感じに各社クラウドインフラリソースを操作してくれるものである。
  • TERRAFORM PROVIDERは、ローカルでgRPCサーバとして動作する。

1.2. 利用フロー

Terraformを利用して、インフラリソースの構築・設定反映をする工程は、以下の通り。

  • ①インフラリソースの設定ファイル(.tf)を記述
  • ②設定ファイルの検証
    • terraform validateコマンドを実行する
  • ③作業用ディレクトリの初期化
    • terraform initコマンドを実行する
      →設定ファイルのインフラリソース定義の反映に必要な分のプロバイダがDLされる
  • ④インフラリソースへ設定を適用するための具体的な操作を確認
    • terraform planコマンドを実行する
      →「クラウドリソースに対して、Terraformがこれからどんな操作をするのか?」の内容が表示されるので、問題ないかを確認する
  • ⑤インフラリソースへ設定を適用
    • teffaform applyコマンドを実行する

2. Terraformの利用

2.1. ローカル環境構築

公式ドキュメントを参考に、以下をインストール。

  • Terraform(※)
  • terraformコマンド補完機能

※terraformのバージョンを管理しながら使いたいならば、tfenv経由でインストールして利用する。

# tfenvをインストール
brew install tfenv

# バージョン指定してterraformをインストールし、tfenv管理下におく
tfenv install <バージョン>

# 使用するterraformのバージョンを指定する
tfenv use <バージョン>

2.2. Terraformのディレクトリ・ファイル構成

設定ファイル(.tf or .tf.json)群を同じディレクトリにまとめたものを、モジュールという。

  • モジュールは、基本的にディレクトリ直下の設定ファイル群のみで構成される。
  • サブディレクトリは完全に別のモジュールとして扱われる
    • モジュール呼び出しという仕組みを使わない限り、自動的に読み込まれたりすることはない。

サブディレクトリの扱い.drawio.png

  • Terraformはモジュールのディレクトリ直下の設定ファイル内の記述すべてを評価し、それが1つの設定ファイルであるかのように動作する。
    • ブロックを別ファイルにわけるのは、純粋に読む人・メンテナンスする人にとってわかりやすくするためだけで、動作には全く影響しない。
  • Terraformは単一のルートモジュールを起点として動作する。
    • Terraform CLIでは、Terraformを起動する作業用ディレクトリ = ルートモジュール。
      • コマンドライン引数で、別ディレクトリをルートモジュールにすることもできるが、実際にはほぼやらないらしい。

2.3. .tfファイルの記述方法

  • 文字コード: UTF-8
  • 改行コード: 慣習的にLF(CRLFでも可)
  • コメント: #で一行コメントを記述

基本的な構造、構文と用語

example.tf
# --- ブロック ----
<ブロックタイプ> "<ラベル>" "<ラベル>" {
  # --- ボディ ---
  ...
  # 引数
  <引数名> = <値> or <式>
  # --- ボディここまで ---
}
# --- ブロックここまで ---

  • ブロック: 設定内容のコンテナであり、ブロックタイプ, 任意の数のラベル, ボディ({}で囲まれる部分。引数、他のブロックを含む)から構成される。
  • 引数: 引数名に「値」もしくは「式」を紐付ける形で記述する。
  • 式: 他の値を参照したり、組み合わせたりするもの。

例えば、以下のような形になる。

example.tf
resource "aws_vpc" "main" {
  cidr_block = var.base_cidr_block
}

2.3.1. ブロックタイプ

terraformブロック

Terraform自体の基本的な設定をするブロック。
モジュールに必要なプロバイダ(required_providersブロック)や、tfstateの置き場(backendブロック)をボディに記述する。

例.

example.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 1.0.4"
    }
  }
  backend "gcs" {
    bucket  = "tf-state-prod"
    prefix  = "terraform/state"
  }
}
required_providersブロック

モジュールで利用する全てのプロバイダを列挙するブロック。

sample.tf
required_providers {
  <プロバイダのローカル名> = {
    source = "<プロバイダのソースアドレス>",
    version = "<プロバイダのバージョン>"
  }
}

プロバイダのローカル名はproviderブロックで使われる。

backendブロック

ステートファイル(.tfstate)の保存方法(バックエンド)を設定するブロック。

backend "<バックエンドタイプ>" {
  <バックエンドタイプ固有の設定>
}

ステートファイルは、その名の通りTerraformの管理対象であるインフラ構成の状態を保存するもの。
backendブロックで指定できるバックエンドタイプや、その固有の設定については、使用するTerraformバージョンのリファレンスを参照する必要がある。例えばgcsバックエンドタイプでは、以下のような設定をする。

terraform {
  backend "gcs" {
    bucket  = "tfstate" # 保存先のGCSバケット名
    prefix  = "terraform/production" # <保存先のバケット名以下のディレクトリ名>/<tfstateファイル名>
  }
}

valiable

TODO

providerブロック

プロバイダを使って、各種インフラリソースの詳細設定をするにあたっての、前提条件を設定するブロック。

sample.tf
provider <プロバイダのローカル名> {
  <プロバイダ固有の設定>
}

ボディにはプロバイダ固有の設定を記述する。
例えばgoogleプロバイダだとproject, regionという固有の設定がある。

sample.tf
provider "google" {
  # resourceブロックで設定するインフラリソースが所属するGoogle Cloudプロジェクトのデフォルト値
  project = "acme-app" 
  # resourceブロックで設定するインフラリソースが所属するGoogle Cloudリージョンのデフォルト値
  region  = "us-central1" 
}

ボディで設定できる項目は、各プロバイダのリファレンスを参照する必要がある。

resourceブロック

仮想ネットワークなどのインフラリソースの設定をするブロック。

sample.tf
resource "<リソースタイプ>" "<リソースのローカル名>" { # リソースタイプとローカル名の組み合わせはモジュール内で一意
    <リソースタイプ固有の設定>
}

対象のリソース(1番目のラベル)に対する詳細設定を記述(ボディ)し、任意のローカル名(2番目のラベル)を与える。
指定できるリソースタイプは、required_providersブロックで設定したプロバイダから提供されるもの。
ローカル名はモジュール内で一意である必要があり、そのモジュール内だけで有効である。
ボディで設定できる項目は、providerブロック同様、各プロバイダのリファレンスを参照する必要がある。

for_each

記述したresourceブロックをstringのmapやset(配列)を使ってループさせつつ、別のブロックインスタンスとして扱えるという機能。map, setの要素となったブロックは、以下のように参照可能。

  • mapの場合: <リソースタイプ>.<リソースのローカル名>[mapのキー名]
  • setの場合: <リソースタイプ>.<リソースのローカル名>[setの要素値]

module

TODO

data

TODO

2.4. terraform コマンド

主要なものをTerraformを使用する際の工程順に並べると以下の通り。

  • terraform validate: .tfファイルの検証
  • terraform init: .tfファイルに基づくクラウドインフラへの設定反映に必要なPROVIDERのダウンロード
  • terraform plan: クラウドリソースに対する現設定からの変化点の確認
  • terraform apply: クラウドリソースに.tfファイルの設定を反映
  • terraform destory: .tfファイルで定義したクラウドリソースを削除

また、terraform fmtという.tfファイル内の標準の記述形式に則ってフォーマットしてくれるコマンドもある。

plan,apply,destroy について

差分に現れる記号の意味

  • +: 追加
  • -: 削除
  • -/+: 置換 (削除からの追加)
  • ~: 更新 (削除からの追加じゃなくて、単純な更新)
  • <=: 読み込み? (データリソースへの適用時のみ発生しうるらしい)

tfstate
Terraformで適用したインフラリソースの状態を保存しておくファイル。
terraform planを実行すると、「実際の環境の状態」、「過去にTerraformで適用した際の状態(tfstate)」、「これから適用しようとしている内容(.tf)」を加味し、結果どういう変更がなされるのか?の結果が出力される。
(Terraformを介さず手動で手を加てしまった場合、以下のようなメッセージで指摘される

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

特定のリソースに対してだけインフラ設定を反映させたい場合

以下のようにする。

terraform plan -target='<対象のリソース1>' -target='<対象のリソース2>' ...

リソースは、'module.<モジュール名>.<リソースタイプ>.<リソースのローカル名>'という形で指定するのが無難。
for_eachで暗黙的に生成されるresourceオブジェクトを指定する場合、
'module.<モジュール名>.<リソースタイプ>.<リソースのローカル名>["キー名"]'
みたいな形になるが、'でくくらずに実行して以下のエラーに遭遇したことがあった

Error: Invalid target "module.<モジュール名>.<リソースタイプ>.<リソースのローカル名>[<キー名>]"
│
│ Index brackets must contain either a literal number or a literal string.
╵

2.5. 公式ドキュメント

以下、特に有用と思われるドキュメント。

3. 参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3