Terraform学習記録

Terraformを学習していく。
公式のintroductionから。

AWS・GCPみたいなクラウドプラットフォームから、他のSaas設定のIaC化にも使える。
背後ではそれぞれのサービスのAPIを叩いている。
Terraform provider: 各サービスをTfに対応させる設定(?)

Tfのworkflowは大体以下のような感じ。
Write: .tf
ファイルにresourceを定義する。
Plan: Tfがplan(実行計画)を作成する
Apply: planに基づいてAPIを叩く。

Tf languageの最終目標はresourceを宣言すること。
他の言語機能はすべてそれを補助するためのもの。
Tf languageは以下のような構文を基本ブロックとして構成される。
<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
<IDENTIFIER> = <EXPRESSION>
}
# 例
resource "aws_vpc" "main" {
cidr_block = var.base_cidr_block
}

blockは0個以上のlabelを持てる。
上の代入式をargument、expressionはリテラルや変数によるなにかしらの値を指す。
var.[var_name]
でvar_name
の値を使える。
ドキュメントの例を見てもなにが予約語でなにが変数名かわからない。provider特有の予約語とかもあるのか?

同じディレクトリにある.tf
ファイルの集合をmoduleという(tf.json
ファイルもあるらしいけどそれは一旦おいておく)。
同じmoduleにある設定はすべて対等に扱われる。子ディレクトリは別のmoduleとして扱われる。
別のファイルに置いておくのは管理を容易にする以外の意味はない。

module callというものを用いることで他のmoduleの設定を取り込むことができる。
Tfは単一のroot moduleを持つ。1つのroot moduleと、root moduleが取りこむ他のmoduleという木構造でモデル化できる。
CLIではterraform *
をするディレクトリがroot moduleに対応する。

_override.tf
をsuffixに持つ.tf
ファイルを使うことで、設定をoverrideできるらしい。
いろいろ細かい挙動があるらしいけどとりあえず飛ばす。

Tfの設定にはproviderやexternal moduleといったdependencyがありうる。
そのversionを.terraform.lock.hcl
に記述する。
.terraform.lock.hcl
はterraform
コマンドを実行するディレクトリに置く。
terraform init
の際に自動的にlockファイルが作成される。
詳細についてはまたあとで。

block文はtypeを持つ(resourceとかdataとか)。typeによってlabelの数が決まる。

resourceはTfの基本となる要素。各resourceブロックは1つのinfrastructure object(例: virtual network, compute instances, DNS record)に対応する。

resource文は2つのlabelを持つ。それぞれresource type (例: "aws_instance")とresourceのlocal nameに対応する。
各resourceは1つのresource typeを持つ。それぞれのresource typeが具体的になにを表すかはproviderによって定義されている。
argumentという名前の通り、resourceを表す関数(arguments |-> concrete resource)に引数を渡してると考えるとよさそう。
各resource typeに必要なargumentはproviderが定義している。例えばlambdaならこれ。

precondition, postcondition blockでassert的なものができる。
.tf
に書かれたlocalなconfigと、現状のインフラの状態を表すstateがある。apply時はこれらを比較して必要な操作をする。デフォルトではstateはlocalのterraform.tfstate
に記述されるが、Terraform Cloudに置いたほうがバージョン管理・暗号化・共有がしやすい。

meta arguments
リソースの性質を表さない、メタ変数(引数)。
depends_on
Tfが推論できない依存関係を明示するのに使う。これ書かなかったらapplyでエラー出るって認識でいいのかな?
count
同じresourceを複数作りたい場合につかう。
for_each
for_eachしたいときに使う。
provider
providerの設定を変えたいときに使う。
lifecycle
applyの処理の順番を変えたいときに使う。
デフォルトでは以下
- create: configにはあるがstateにはないresourceをcreateする
- destroy: configにはないがstateにはあるresourceをdestroyする
- update in-place: (可能なら)現存するresourceの設定を変更する
- destroy and re-create: update in-placeができないresourceを削除&作成する

data sources
data blockを使って外部のデータを変数として使える。
すでにあるresourceの情報とかを使えるってことでいいのかな→あってそう
filterというargumentを使ってresourceを特定するっぽい?
filterとは限らないっぽい、S3だとbucket
というargumentでバケットを特定している。

provider
blockでproviderの設定ができる。例えば、AWSのregionとか

なんかterraform
blockとかいうのが出てきた、なにこれ
Tfそのものの挙動をいじるために使うらしい、Tfのバージョンとか、experimentalなTfの機能を使うかとか。
terraform
block内にrequired_providers
というblockもあるな、なんだこれ。
すべてのTf moduleはどのproviderを使うかを指定するためにrequired_providers
が必要らしい。その中でproviderやそのバージョンを指定する。

input variable
variable
blockで、「moduleの引数」のようなものを宣言できる。
子moduleが定義したinput variableは、親moduleがその値を渡して使う。
variable内には変数名やその型、デフォルトの値を定義する、validationとかnullableの定義もできる。
親moduleはどうやって値を渡すんだ?→ module
blockを使う。
output variableはoutput
blockを使って子モジュール内の値を親モジュールにexposeできる。
local variableは同じmoduleの中で使える値。DRYにするため。

module
block
超重要っぽい。
module "servers" {
source = "./app-cluster"
servers = 5
}
local name: 上の例ではservers
の部分
source
: 必須。moduleを定義する.tf
ファイルを含むlocal directoryかremote moduleへのパスを書く。
version
: module registryから拾ってくる場合に使う。
上の例ではmodule.servers.*
という形でmoduleのoutput variableにアクセスできる。

moduleの構成
いつもの。flatな構造推奨