Slide 1

Slide 1 text

これで猛暑を乗り越えよう! 2024年真夏の最新トレンド Terraform Tips集 2024/08/14 Monthly LT kuroda naoki

Slide 2

Slide 2 text

※Terraformのバージョンはv1.9.xを想定しています。 ※タイトルは釣りタイトルであり、実際には猛暑を乗り越える方法はありません。

Slide 3

Slide 3 text

1. resource name Terraform 公式style guideによると、 スネークケース ダブルクォートで囲む resource typeを含めない ⭕️ ❌ 1 resource "aws_lambda_function" "super_hyper" { 2 function_name = super-hyper-function 3 description = "A super hyper function" 4 ... 5 } 1 resource aws_lambda_function super-hyper-lambda-function { 2 function_name = super-hyper-function 3 description = "A super hyper function" 4 ... 5 }

Slide 4

Slide 4 text

1. resource name Terraform 公式style guideによると、 For consistency and readability, use a descriptive noun and separate words with underscores. 一貫性と可読性のために、説明的な名詞を使用し、単語はアンダースコアで区切る。 引用)https://developer.hashicorp.com/terraform/language/style#resource-naming Googole Cloud公式ドキュメントのTerraform on Google Cloud - Best practices for general style and structureによると、 This practice ensures consistency with the naming convention for resource types, data source types, and other predefined values. この慣習は、resource type、data source type、その他の定義済みの値の命名規則との一貫性を保証します。 引用) https://cloud.google.com/docs/terraform/best-practices/general-style-structure#naming-convention

Slide 5

Slide 5 text

2. comment 1行でも複数行のコメントであっても、 # を使う。 // や /* */  は idiomaticではない。 だが、以前のバージョンの後方互換性のためにサポートされている。

Slide 6

Slide 6 text

3. depends_on Terraformが自動的に推測できないリソースやモジュールの依存関係を扱える。 依存を宣言しているオブジェクトに対するアクションを実行する前に、依存オブジェクトに対するすべての アクション(読み込みアクションを含む)を完了する。 ただし、なるべく使わない方がいい。 必要以上に多くのplanを実行することになって、時間がかかる可能性があるから。 Terrafromがより多くの値を unknown “(known after apply)” として扱うことになるから。 できる限り普通の参照を使う。例) aws_instance.example.id 1 resource "google_cloud_run_service" "this" { 2 name = "super cloud run service" 3 location = local.region 4 depends_on = [google_storage_bucket_object.rules] 5 ... 6 }

Slide 7

Slide 7 text

4. version constraints = :1つの正確なバージョンのみ許可 ! = :特定のバージョンを除外 > , > = , < , < = :指定されたバージョンとの比較 ~> :一番右の数字のみインクリメントできる範囲内のバージョンを許可。 例) version = "~> 1.2.0" : 1.2.xの範囲内のパッチバージョンを許可(1.2.0, 1.2.1, 1.2.9など) 、但し1.3.0 は許可されない。 version = "~> 1.2" : 1.xの範囲内のマイナーバージョンを許可(1.2.0, 1.3.0, 1.9.9など) 、但し2.0.0は 許可されない。

Slide 8

Slide 8 text

4. version constraints (provider) ルートディレクトリ(terrafrom applyをするディレクトリ)で、 ~> で範囲を指定する。 terraform { required_providers { mycloud = { source = "hashicorp/azurerm" version = "~> 3.1.0" } } } いろんな場所で使われているmoduleの場合、 > = で最小限のバージョンを指定するのもあり。 terraform { required_providers { mycloud = { source = "hashicorp/azurerm" version = "> = 1.0" } } } ※そもそもmoduleにバージョンの設定はあまりしないと思いますが、 、

Slide 9

Slide 9 text

4. lifecycle 全てのresourceブロックにおいて使える。 resourceのライフサイクルを制御する。 ①create_before_destroy 例えば、AMIを更新する場合、新しいインスタンスを作成してから古いインスタンスを削除する。 resource "aws_instance" "example" { ami = "ami-a1b2c3d4" instance_type = "t2.micro" lifecycle { create_before_destroy = true } }

Slide 10

Slide 10 text

4. lifecycle ②prevent_destroy 例えば、このdb instanceを削除しようとすると、エラーを返す。 resource "aws_db_instance" "example" { engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" name = "mydb" lifecycle { prevent_destroy = true } }

Slide 11

Slide 11 text

4. lifecycle ③ignore_changes 例えば、AWSコンソールからtagを変更しても、Terraformがその変更を無視する。 resource "aws_instance" "example" { ami = "ami-a1b2c3d4" instance_type = "t2.micro" tags = { Name = "ExampleInstance" } lifecycle { ignore_changes = [ tags, ] } }

Slide 12

Slide 12 text

4. lifecycle ④replace_triggered_by 例えば、セキュリティグループを変更すると、このEC2インスタンスも置き換えられる。 resource "aws_instance" "example" { ami = "ami-a1b2c3d4" instance_type = "t2.micro" lifecycle { replace_triggered_by = [ aws_security_group.example.id ] } } resource "aws_security_group" "example" { name = "example" description = "Example security group" }

Slide 13

Slide 13 text

5. removed block Terraformのstateからリソースを削除する時、 terraform state rm を叩く or removed block を使う ←おすすめ terraform plan、applyを実行する。 removed { from = aws_s3_bucket.main lifecycle { destroy = false # falseにすると、stateから削除されるだけで、実際のリソースは削除されない。 } }

Slide 14

Slide 14 text

6. moved block 別のモジュールに移動したいorリソース名を変えたい等の時に、 (単純にリソース名を変えると、Terraform は古いリソースを削除し、新しいリソースを作成してしまう。 ) terraform state mv を叩く or moved block を使う ←おすすめ terraform plan、applyを実行する。 moved { from = aws_instance.a to = aws_instance.b }

Slide 15

Slide 15 text

7. import block 既存のリソースをTerraformで管理したい時に使う。 terraform import を叩く or import block を使う ←おすすめ terraform plan、applyを実行する。 import { to = snowflake_grant_privileges_to_account_role.new_resource_role_a #idはimportしたいリソースを識別するためのもの。各providerによって形式は異なる。 id = "\"${snowflake_role.a.name}\"|false|false|USAGE|OnAccountObject|DATABASE|\"${snowflake_database.test.name}\"" } resource "snowflake_grant_privileges_to_account_role" "new_resource_role_a" { account_role_name = snowflake_role.a.name privileges = ["USAGE"] .... }

Slide 16

Slide 16 text

8. terraform plan -generate-config-out importブロックのtoの宛先となるリソースを生成する時でかつ大量にimportしたい時に便利。 import { to = snowflake_grant_privileges_to_account_role.new_resource_role_a id = "\"${snowflake_role.a.name}\"|false|false|USAGE|OnAccountObject|DATABASE|\"${snowflake_database.test.name}\"" } を記述した後に、 terraform plan -generate-config-out=generated.tf を叩くと、 generated.tf に to の宛先となるリソースが生成される。 ↓みたいな resource "snowflake_grant_privileges_to_account_role" "new_resource_role_a" { account_role_name = snowflake_role.a.name privileges = ["USAGE"] .... } ※ただし、Experimentalな機能で、ちょっと制約もある。 。 。

Slide 17

Slide 17 text

9. TF_LOG 環境変数の TF_LOG を使うと、Terraformのログの出力レベルを選べる。 TRACE , DEBUG , INFO , WARN , ERROR のレベルで。 OFF にすると、TF_LOGが設定されていない場合と同じ。 TF_LOG=DEBUG terraform plan みたいにしてもいいし、CIでログを出したいときにも使える。 例) 1 log_level: 2 description: "Log Level" 3 type: choice 4 default: "off" 5 options: 6 - "off" 7 - "debug" 8 ..... 9 steps: 10 - name: Terraform Plan 11 run: terraform plan 12 env: 13 TF_LOG: ${{ inputs.log_level }}

Slide 18

Slide 18 text

10. terraform test terraform test コマンドを叩くとテストができる。 テストの中で実際にplanやapplyを実行する。 applyの場合、テストが終わったら、テストの中で作成したリソースを削除する。 テストのためにproviderやresource、data sourceをmock化することもできる。

Slide 19

Slide 19 text

10. terraform test # テストの中でinputとして渡すvariables variables { env = "test" project = "fuga" } # テスト実行前にセットアップもできる。テスト実行後には、sample⇨setupの順で削除される。 run "setup" { module{ source = "./testing/setup" } } run "sample" { command = apply # planも指定できる。 module { source = "../../../modules/sample" } # 検証 assert { condition = data.http.index.status_code == 404 error_message = "error: not found" } }

Slide 20

Slide 20 text

参考 Terraform Style Guide Terraform Language Documentation Terraform on Google Cloud - Best practices for general style and structure