#author("2025-05-26T16:32:23+09:00","default:dex","dex") #author("2025-06-02T18:11:53+09:00","default:dex","dex") #contents #ls2 *Terraform [#n7dda3d6] -https://www.terraform.io/ -- [[CHANGELOG.md:https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md]] -- HashiCorp製ツール(Vagrant, Packer, Serf等の開発元) -- AWS, Heroku等の複数のクラウドサービスに対応している ''個人的な印象'': -- Terraformの不具合の多さを理解しつつ、Terraformの不具合報告/修正等の''オープンソース活動''を行える余裕がある人向け。 -- https://github.com/hashicorp/terraform/issues を見ると分かるが1000 issue以上が作成されており、不具合の修正が間に合っていない印象 -- terraform本体と、provider(AWS, Azure, ...)は別。providerが最新のterraformに対応していない場合もあるので、要チェック -- ''新規作成・破棄''を繰り返す用途向き -- 既存リソースの管理はコードを書いて、リソースID毎にimportコマンドの実行が必要なので大変。import非対応のリソースもある。''向いていない'' -- 状態をステートファイル(terraform.tfstate)に持ち、これを元に差分更新する。 ---無くすと大変。更新ができなくなる。(既存リソースのimportも一部は出来るが、数が多いと大変) ---ステートファイルはAWS S3等に置く事はできる。 ---ステートファイルの管理が面倒。リソースの差分がうまく比較できないようで、''リソースの更新には不向き''。 -- ドキュメント・サンプルが不足している。値の説明が書いてあるだけで、具体的にどのような値を入れれば良いのか不明なリソースが沢山ある。 --- ドキュメントに機能追加/変更されたバージョンが書いてないため、いつのバージョンが対応してるのかわからない --- ドキュメントにはimportが書いて無いが、実はインポートできるリソースもある -- マイナーバージョンが0.0.1変わっても、仕様が大きく変わり、コードの互換性が無くなる事も多い。 --- コードの互換性がない場合もあり、修正にも時間がかかるため、tfenv等で複数バージョンの管理が必要。 --- 0.11と0.12でコードの互換性が無くなった。 0.12upgradeコマンドである程度は修正してくれるが、リソース名はそのままなので手動修正が必要 -- 開発方針がdevしかなく、stable等の安定版が無い。 - 構文 -- 同じリソースを作成する方法が複数存在する場合があり混乱する。リソースA + attach + リソースBが変更には強い感じはある。v0.12からfor eachやdynamic blockが追加された事でループ処理ができ、どちらを使えば良いのかさらに混乱するようになった。 --- 例: aws_instance + ebs + attachと分ける方法、aws_instanceだけの方法(ebs拡張が効かない) --- 例: aws_security_group, aws_security_group_rule。 混在させると1回目のapplyでは成功するように見えるが、2回目のapplyでは片方消える。 -- ループ処理が分かりにくい。v0.11: list内にmapがあると要素が取れなかったりする。 -- if等の制御構造が無い。count=0でリソースを作成しないように、三項演算子で制御は一応できるが分かりにくい。 -- AWS --- aws_instance count=4で作った後に、count=0,1をterminateする方法が %%destroy -target 指定するしか無さそう。しかし、planで毎回count=0,1が新規作成扱いになるため辛い。state rm/importでやり直せば可能だが、リソースID毎にimportが必要なため面倒。%%-> ''0.12でfor_eachが追加''された --- 対応しているリソースの新規立ち上げは楽だが、非対応の項目がかなりあるため、自分が必要としている機能をサポートしているか要確認 --遭遇したトラブル --- v0.11.13: EC2セキュリティグループの書き方が複数あり、古い書き方+新しい書き方(aws_security_group_rule)を混在した場合、applyは成功する。2回目のplanでも差分が出て、どちらかが消える。 --- v0.0.1上がるとコードに無いheroku configを消すように仕様変更 --- API GatewayのSSL証明書の登録が成功したように見えるが、実は成功していない。 --- destroy時は削除確認があるが、EC2セキュリティグループの変更で、EC2が作り直される時には警告はない。plan時に予期しない変更が無いか強く確認が必要。さもなければ簡単にリソースが消える --- セキュリティグループに変更を加えると、EC2を作り直そうとする --- ELBにリスナー追加しようとすると、リスナー全て消えた --- SSL証明書のアップロードが成功したように見えて壊れている --- EC2にEBSを追加しようすると、EC2を作り直そうとする --- %%aws_instanceリソースでEBSを追加できる。作成後、aws_instanceリソース内のroot, EBSのサイズ等を変更できない。%% awscliでは可能。provider.aws v5 ではaws_instance内のroot volumeサイズは変更可能だった。ebs blockは不可。 -記事 -- [[Terraformをしばらく書いて覚えた個人的なTipsについて | Developers.IO:https://dev.classmethod.jp/articles/my-terraform-tips/]] --[[Terraformのエッセンスが凝縮された「Pragmatic Terraform on AWS」が素晴らしい | DevelopersIO:https://dev.classmethod.jp/cloud/aws/bookreview-terraformon-aws-md/]] --[[Terraform職人入門: 日々の運用で学んだ知見を淡々とまとめる - Qiita:https://qiita.com/minamijoyo/items/1f57c62bed781ab8f4d7]] --[[Terraform Best Practices in 2017 - Qiita:https://qiita.com/shogomuranushi/items/e2f3ff3cfdcacdd17f99]] --[[【30分で動かすシリーズ】まだCloudFormationで消耗してるの?TerraformでAWS環境を構築してみる(その1:TerraformでAWS環境を構築する準備) | サーバーワークス エンジニアブログ:http://blog.serverworks.co.jp/tech/2016/09/26/terraform-aws-provisioning-01/]] --[[【参考訳】Terraform 0.7 · Pocketstudio Technology Log:https://pocketstudio.net/2016/08/06/terraform-0-7-translate/]] --[[TerraformでGithubのチーム管理を自動化して事故った話 | ツチノコブログ:http://tsuchinoko.dmmlabs.com/?p=4265]] --[[Terraform 0.4 (参考訳) | Pocketstudio.jp log3:http://pocketstudio.jp/log3/2015/04/04/terraform-0-4-release-translated/]] ---- ** digger: GitHub actions上で動くCI/DI [#d018edbe] - [[GitHub - diggerhq/digger: Digger is an open source IaC orchestration tool. Digger allows you to run IaC in your existing CI pipeline:https://github.com/diggerhq/digger]] 記事: - [[Digger + GitHub Actionsで作るTerraform/OpenTofuのCI/CD #Terraform - Qiita:https://qiita.com/minamijoyo/items/b61806b570d9d1257f0b]] ---- ** 改善 [#w205984a] 記事: - [[コードレビューでよくあったコメント9選 〜Terraform編〜:https://zenn.dev/mixi/articles/terraform-code-review-tips]] ---- ** terraformでDB user作成 [#m7039ec1] 注意点: - 通常RDSは、private subnetにあるため、localhostからは接続できない。そのため、ssh踏み台(bastion-host)経由でsshポートフォワーディングする。 #geshi(bash){{ ssh -F ssh-config -fNg -L 5432:<RDS ID>.****.ap-northeast-1.rds.amazonaws.com:5432 bastion-host }} - postgresqlでSSL有効の場合、以下のエラーが出る。 -- docを参照して「scheme = "awspostgres"」を入れると同じエラーが出たのでコメントアウト。(cyrilgdn/postgresql v1.22.0) #geshi(text){{ Error: Error connecting to PostgreSQL server 127.0.0.1 (scheme: awspostgres): tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs }} -- /etc/hostsを書き換えてlocalhostへ接続するように #geshi(text){{ sudo vim /etc/hosts -- 127.0.0.1 <RDS ID>.****.ap-northeast-1.rds.amazonaws.com -- }} 記事: - [[TerraformでAWS RDSのDBユーザを管理する #PostgreSQL - Qiita:https://qiita.com/yoshuuua/items/ebeefffc04d966d238e4]] ---- ** Infracos Cloud: terraformコードからコスト見積もり/差分を出してくれるSaaS [#jecc5f64] - [[Infracost Plans & Pricing:https://www.infracost.io/pricing/]] -- 有料 -- trialは1000回/月まで無料? - [[GitHub - infracost/infracost: Cloud cost estimates for Terraform in pull requests💰📉 Shift FinOps Left!:https://github.com/infracost/infracost?tab=readme-ov-file]] ---- ** ガイドライン [#ked73464] - [[Terraform設計ガイドライン | Future Enterprise Arch Guidelines:https://future-architect.github.io/arch-guidelines/documents/forTerraform/terraform_guidelines.html]] - 公式ガイドラインを含んでいて分かりやすい - [[Style Guide - Configuration Language | Terraform | HashiCorp Developer:https://developer.hashicorp.com/terraform/language/style]] -- [[Terraform 公式がスタイルガイドを出したので読んで要約した #Terraform - Qiita:https://qiita.com/ogatango/items/9cd57fe8c48b1c03b2bd]] ---- ** terraform本体のコードリーディング [#f87e6c17] 記事: - [[Terraformの実装コードを、動かしながら読む | フューチャー技術ブログ:https://future-architect.github.io/articles/20240326a/index.html]] ---- ** mysql user/password等を作成する [#d4cb6ac2] - [[hashicorp製mysql repo:https://github.com/hashicorp/terraform-provider-mysql]]は更新停止している。そのrepoをforkし、機能追加したものが複数ある -- [[mysql_user | Resources | petoju/mysql | Terraform | Terraform Registry:https://registry.terraform.io/providers/petoju/mysql/latest/docs/resources/user]] -- importに失敗した。(mysql_user, mysql_grant) - [[Docs overview | okkez/mysql | Terraform | Terraform Registry:https://registry.terraform.io/providers/okkez/mysql/latest/docs]] -- [[MySQL8 以降の新しい機能に一部対応した terraform-provider-mysql を開発しました:https://zenn.dev/okkez/articles/ec92caf8494765]] -- import成功まで確認した。(mysql_user, mysql_grant) - RDSは基本VPC内にあるので、localからは直接アクセスできない。そのため、sshポートフォワード等が必要になる -- [[RDS内のデータベースやユーザーをterraformで管理する #Terraform - Qiita:https://qiita.com/yuta_vamdemic/items/698e149edf8ea89c24dc]] ---- ** OpenTofu: OSS版terraform [#vc752629] - [[Memo/OpenTofu]] ---- ** optional(type): 省略可能な変数定義 [#o436e48d] - terraform v1.3以降でoptional(type)構文が使える #geshi(text){{ variable "with_optional_attribute" { type = object({ a = string # a required attribute b = optional(string) # an optional attribute c = optional(number, 127) # an optional attribute with default value }) } }} 記事: - [[Terraform 1.3でoptional()によるobject attributes(variable)へのデフォルト値設定が楽になる!:https://zenn.dev/jrsyo/articles/83bbcff7e08ab8]] ---- ** さくらのクラウド(さくらインターネット) [#qa158d9b] - [[Docs overview | sacloud/sakuracloud | Terraform | Terraform Registry:https://registry.terraform.io/providers/sacloud/sakuracloud/latest/docs]] 記事: - [[さくらの開発チームにおけるTerraform/Ansibleの活用 | さくらのナレッジ:https://knowledge.sakura.ad.jp/38635/]] - [[さくらのクラウドでTerraformを使ってみる | さくらのナレッジ:https://knowledge.sakura.ad.jp/31560/]] ---- ** 変数の外部定義と優先度 [#rdb510e2] 上から順に読み込まれ、同名は上書きされる [[Input Variables - Configuration Language | Terraform | HashiCorp Developer:https://developer.hashicorp.com/terraform/language/values/variables#variable-definition-precedence]] - 環境変数 - terraform.tfvars - terraform.tfvars.json - *.auto.tfvars, *.auto.tfvars.json - -var "var1=val1", -var-file file.tfvars ---- ** Pluralith: terraformコードから構成図を作図するサービス [#g23a647a] - https://app.pluralith.com/ -- まだアルファ版 -- local実行は無料、IaCでの実行は有料 記事: - [[Terraform構成をビジュアライズできるツール Pluralithを使ってAWS構成図を自動作成してみる | DevelopersIO:https://dev.classmethod.jp/articles/terraform-visualise-pluralith/]] ---- ** blockを動的に定義し、パラメータ化する [#x6e03098] - 例: heroku_appのorganization blockはデフォルトは省略可で、任意でパラメータを与える。moduleにする時にパラメータにしたい #geshi(){{{ variable "heroku_app_organization" { description = "" type = object({ name = string }) default = { name = null } } resource "heroku_app" "main" { ... dynamic "organization" { for_each = var.heroku_app_organization.name == null ? [] : [var.heroku_app_organization] content { name = organization.value.name } } } }}} ---- ** 機密情報を暗号化して保存する [#v15c706f] - git repoに機密情報(password, secret, token等)をplain textで保存するのはセキュリティ上リスクが高い - tfファイルに直接機密情報は書かない - secrets.tfvars に分けて書く。または、secrets.json, yaml等別ファイルにする - AWSの場合 -- KMS: secrets.yaml.encrypted 暗号化。secrets.yaml.encryptedはgitにコミットして良い。data.aws_kms_secrets でdecodeして取得 -- secret manager: 機密情報をyaml, jsonにして保存。 data.aws_secretsmanager_secret_version で取得 sensitiveの変更を出力したい時: - [[Memo/Terraform#g133d3f5]] - [[terraform planでsensitive属性が原因で非表示になる差分を見る方法:https://zenn.dev/shonansurvivors/articles/4940cbea023970]] 記事: - [[A comprehensive guide to managing secrets in your Terraform code | by Yevgeniy Brikman | Gruntwork:https://blog.gruntwork.io/a-comprehensive-guide-to-managing-secrets-in-your-terraform-code-1d586955ace1]] - [[Terraform で秘密情報を扱う – もばらぶエンジニアブログ:https://engineering.mobalab.net/2021/03/25/handling-secrets-with-terraform/]] ---- ** 排他仕様の変数に代入 [#i8900921] - nullを代入すると、変数は未定義扱いとなった - try()で変数が未定義の場合、nullを返すように - 例: aws_kinesis_firehose_delivery_stream で domain_arn, cluster_endpoint は排他 #geshi(text){{ ... elasticsearch_configuration { domain_arn = try(each.value.es_cluster_endpoint, null) == null ? each.value.es_domain_arn : null cluster_endpoint = try(each.value.es_cluster_endpoint, null) ... }} ---- ** data.http: httpリクエストを送る [#mbceb1c5] - [[http_http | Data Sources | hashicorp/http | Terraform Registry:https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http]] -- HEAD, GET, POSTメソッド対応 - 例: JSONファイルをpostする。plan時でもpostされる。 #geshi(text){{ data "http" "example" { url = "https://example.com/api1" method = "POST" request_headers = { Content-Type = "application/json" } request_body = file("/path/to/example.json") } output "http_os_example_result" { value = "status_code: ${data.http.example.status_code}, response_body: ${data.http.example.response_body}" } }} ---- ** tfcmt: plan結果を整形してCI/DIの改善 [#r26c9516] - https://github.com/suzuki-shunsuke/tfcmt 記事: - [[tfcmt で Terraform の CI/CD を改善する:https://zenn.dev/shunsuke_suzuki/articles/improve-terraform-cicd-with-tfcmt]] ---- ** templatefile(): ファイル中の変数を展開 [#g122d345] - [[templatefile - Functions - Configuration Language | Terraform | HashiCorp Developer:https://developer.hashicorp.com/terraform/language/functions/templatefile]] -- file() の代わりに使えば、変数を展開できる -- [[data.template_file:https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file]] と違い、分けて書く必要がないのでシンプル - 例: #geshi(hcl){{ resource "elasticsearch_opensearch_ism_policy" "example" { policy_id = "example" body = templatefile( "${path.module}/files/opensearch/ism-policy-example.json", { min_index_age = "30d", min_size = "50gb", } ) } }} ---- ** moved: リソース名変更等のリファクタリング [#nbb6597a] - [[Refactoring | Terraform | HashiCorp Developer:https://developer.hashicorp.com/terraform/language/modules/develop/refactoring]] -- terraform v1.1から利用可能 - movedを使わない場合以前のリソース名の変更 -- 「terraform state mv <old resource> <new resource>」 -> ソース変更 -- または「terraofrm state rm <old resource>」-> ソース変更 -> 「terraofrm import <new resource> <resource name>」 記事: - [[movedブロックを使ってリファクタリングしてみた | DevelopersIO:https://dev.classmethod.jp/articles/terraform-moved-block-resource-refactoring/]] --- ** rangeでloopさせる [#i56d08c2] - terraform v1.3 - resourceを複数作成したい場合、for_eachが使えるがmapか、string型のlistしか受け付けない。formatlistでstring型に変換する。 #geshi(text){{ for_each = toset(formatlist("%s", range(1, 10))) }} ---- ** for: ループ [#u7ce101d] - [[For Expressions - Configuration Language | Terraform by HashiCorp:https://www.terraform.io/language/expressions/for]] -- 戻り値の型指定が決まっている。array「[for ...]」、hash「{for ...}」 -- 複数の配列を1行の中で返す等はできない? -- mapをループする場合「[for key, value ...]」 - 例: 複雑な変数から、s3 policyでよくある ["arn:aws:s3:::example1", "arn:aws:s3:::example1/*"] を作る。 #geshi(text){{ locals { var1 = { bucket1 = { s3_bucket_arn = "arn:aws:s3:::example1" } bucket2 = { s3_bucket_arn = "arn:aws:s3:::example2" } } } data "aws_iam_policy_document" "s3" { statement { effect = "Allow" actions = [ "s3:GetObject", ] resources = concat([for value in local.var1 : value.s3_bucket_arn], [for value in local.var1 : "${value.s3_bucket_arn}/*"]) } } }} ---- ** v1.0以降の対応 [#w500bda9] - [[Upgrading to Terraform v1.0 | Terraform by HashiCorp:https://www.terraform.io/language/upgrade-guides/1-0]] - v0.13でapplyしてるtfsateが必要 - 例: v0.11からv1.0へアップデートしたい場合、かなり手間がかかる -- v0.11 -> v0.12 #geshi(bash){{ rm versions.tf echo 'latest:^0.12' > .terraform-version rm -rf .terraform teraform init terraform 0.12upgrade teraform plan # コードのエラー修正 }} -- v0.12 -> v0.13 #geshi(bash){{ echo 'latest:^0.13' > .terraform-version rm -rf .terraform teraform init terraform 0.13upgrade teraform plan # コードのエラー修正 # applyしないとv1.0でエラーが出る teraform apply }} -- v0.13 -> v1.0 #geshi(bash){{ echo 'latest:^1.0' > .terraform-version rm -rf .terraform teraform init teraform plan # コードのエラー修正 }} ---- ** URLからOIDC fingerprintを取得 [#f01aad21] - [[data.http:https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http]] URLの結果を取得 - [[data.tls_certificate:https://registry.terraform.io/providers/hashicorp/tls/latest/docs/data-sources/tls_certificate]] TLSのsha1 fingerprintを取得 記事: - [[Terraform だけを使って GitHub Actions OIDC ID プロパイダの thumbprint を計算する方法:https://zenn.dev/yukin01/articles/github-actions-oidc-provider-terraform]] ---- ** 機密情報を出力したい時 [#g133d3f5] - [[How-to output sensitive data with Terraform – HashiCorp Help Center:https://support.hashicorp.com/hc/en-us/articles/5175257151891-How-to-output-sensitive-data-with-Terraform]] -- password, token, access key等、標準出力に表示しないほうが良い場合。 -- どの値を隠すべきかの参考に --- AWS: https://github.com/secretlint/secretlint/tree/master/packages/@secretlint/secretlint-rule-aws - v0.15から「sensitive = true」を付けないと、apply時にエラーになった。planやapply時は「example_password = <sensitive>」表示になる。 #geshi(text){{ output example_password { value = aws_db_instance.example.password sensitive = true } }} -- 生の値を表示したい場合: #geshi(text){{ # json terraform output -json terraform output example_password # raw value terraform output -raw example_password }} - 常時表示したい場合。 - [[nonsensitive - Functions - Configuration Language - Terraform by HashiCorp:https://www.terraform.io/docs/language/functions/nonsensitive.html]] #geshi(text){{ output example_password { value = nonsensitive(aws_db_instance.example.password) } }} 記事 - [[Terraform 0.15の変更点を調べた | DevelopersIO:https://dev.classmethod.jp/articles/terraform-015/]] ---- ** セキュリティチェック [#gcdc931d] - 記事 -- [[Terraform, Dockerfile, KubernetesなどIaCの脆弱な設定をCI/CDで検知する - knqyf263's blog:https://knqyf263.hatenablog.com/entry/2021/07/13/063729]] ---- ** untaint: 失敗したstate fileを解決済みとしてマークする [#k34623e9] - [[Command: untaint - Terraform by HashiCorp:https://www.terraform.io/docs/commands/untaint.html]] 例: - RDSは作成に時間がかかり、timeout(create: 40min)する事がある。そうすると、stateファイルが汚染(taint)?状態になり、terraform planで余計な差分(destroy/add)が出る状態になる 解決: #geshi(bash){{ terraform untaint aws_db_instance.main }} ---- ** 算術演算子 [#ld3d780d] - 簡単な計算(+, -, *, /, %, -1)ができる - [[Arithmetic Operators:https://www.terraform.io/docs/configuration/expressions.html#arithmetic-operators]] #geshi(text){{ byte = "${100*1024*1024}" # 100MB }} ---- ** plan時に色を付けない [#p590953b] - plan結果をテキストファイルにしたい場合、デフォルトでは色(制御文字列)がテキストファイルに出て見難い - -no-color を付ける #geshi(bash){{ terraform plan -no-color > plan.txt }} ---- ** GitHub actionsでの連携 [#af3fe099] - 記事 -- [[GitHub Actionsでsetup-terraformを試す | Developers.IO:https://dev.classmethod.jp/articles/try-github-actions-setup-terraform/]] ---- ** console: 対話型で式を検証 [#va0ed923] - [[Command: console - Terraform by HashiCorp:https://www.terraform.io/docs/commands/console.html]] - 対話型 #geshi(bash){{ terraform console > concat(["a", "b"], ["c"]) [ "a", "b", "c", ] > exit }} - stdinから渡す #geshi(bash){{ echo 'concat(["a", "b"], ["c"])' | terraform console }} ---- ** dynamic block: リソース内の同名blockのループ [#qe9a135b] - [[dynamic blocks:https://www.terraform.io/docs/configuration/expressions.html#dynamic-blocks]] -- リソース内で同じ名前のブロックが複数ある場合、for_eachでループできる。 -- list(string)はfor_each = toset(var.name)で指定、.keyで参照する。''項目が増減すると、リソースの生成/破棄が発生する。'' -- map(string)は、.key, .valueで参照できる -- 複雑なlist(map(string))は、.value.key2で参照できる - 環境 -- Terraform v0.12.21 - 例: aws_db_parameter_groupの場合、"parameter {}" ブロックを複数設定する #geshi(text){{ variable "mysql_57_parameters" { default = { character_set_server = "utf8" character_set_client = "utf8" } } resource "aws_db_parameter_group" "mysql_57" { name = "mysql-57" family = "mysql5.7" dynamic "parameter" { for_each = var.mysql_57_parameters content { name = parameter.key value = parameter.value } } } }} -- plan結果 #geshi(bash){{ terraform plan -target aws_db_parameter_group.mysql_57 ... # aws_db_parameter_group.mysql_57 will be created + resource "aws_db_parameter_group" "mysql_57" { + arn = (known after apply) + description = "Managed by Terraform" + family = "mysql5.7" + id = (known after apply) + name = "mysql-57" + name_prefix = (known after apply) + parameter { + apply_method = "immediate" + name = "character_set_client" + value = "utf8" } + parameter { + apply_method = "immediate" + name = "character_set_server" + value = "utf8" } } }} - 例: aws_security_group に使う。aws_security_group_rule の方が差分を確認できて良いかもしれない。 -- メリット: --- 内部的にソートしているようで、記述の順序を気にして無く良い。 -- デメリット: --- 一部だけ変えても、delete/new扱い。changeで表示されない。 --- 一つでも必要な項目は、全変数に入れる必要がある。(パラメータが無かったら省略するという構文が無いように見える) #geshi(text){{{ variable "sg_ssh_ingress" { default = [ { cidr_blocks = ["xxx.xxx.xxx.xxx/32"] description = "ssh from AAA" security_groups = [] self = false }, { cidr_blocks = ["xxx.xxx.xxx.xxx/32"] description = "ssh from BBB" security_groups = [] self = false }, ... ] } resource "aws_security_group" "bastion" { ... dynamic "ingress" { for_each = var.sg_ssh_ingress content { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ingress.value.cidr_blocks security_groups = ingress.value.security_groups description = ingress.value.description self = ingress.value.self } } ... } }}} ---- ** for_each: ループ [#o3334932] v0.12.6以降のループ処理について。 countの問題点: - countが変わると、count.indexに紐付いたidも変わるため、リソースが壊れる。countが増える分には対応できるが、countが減る場合に問題が出る - 例: count=4で、count.index=0,1だけを削除/変更したい場合に対応できない。count.index=2,3が削除/変更される。 [[Manage Similar Resources with For Each | Terraform - HashiCorp Learn:https://learn.hashicorp.com/tutorials/terraform/for-each#define-a-map-to-configure-each-project]]: - 変数を元に、リソース自体をループできる。keyを静的なuniq idで指定すれば、要素数が変わっても問題が無い。 - map型は each.key, each.valueで参照可能 - list型は [[toset():https://www.terraform.io/docs/configuration/functions/toset.html]]を指定する。each.key, each.valueで同じ値が参照可能。''項目数が増減すると、リソースの生成/破棄が発生する。'' -- plan&applyでkeyが動的な値だとエラーが出るようになった。 -- [[Terraform の for_each では静的な値を利用する|デロイト トーマツ ウェブサービス株式会社(DWS)公式ブログ:https://blog.mmmcorp.co.jp/2024/02/27/terraform-%E3%81%AE-for_each-%E3%81%A7%E3%81%AF%E9%9D%99%E7%9A%84%E3%81%AA%E5%80%A4%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B/]] - 記事 -- [[Terraformで配列をloopする時はfor_eachを使った方がいい - cloudfishのブログ:https://cloudfish.hatenablog.com/entry/2020/02/19/183548]] -- [[Terraform0.12 のfor_eachで幸せになれるってよ - Qiita:https://qiita.com/micci184/items/e0723cce6c18b81a9de0]] - 環境 -- Terraform v0.12.24 - for_eachの例1: リソース自体が2回実行される #geshi(text){{{ locals { users = [ "user01", "user02", ] } resource null_resource user { for_each = toset(local.users) triggers = { user = each.value } } }}} -- 実行結果 #geshi(bash){{{ terraform plan -target null_resource.user ... # null_resource.user["user01"] will be created + resource "null_resource" "user" { + id = (known after apply) + triggers = { + "user" = "user01" } } # null_resource.user["user02"] will be created + resource "null_resource" "user" { + id = (known after apply) + triggers = { + "user" = "user02" } } }}} - for_eachでindex値が欲しい場合 #geshi(text){{ index(local.users, each.value) }} ---- *** key, value以上の複数のパラメータを指定したい場合 [#u57ce057] - valueの参照は以下のいずれかで可能 -- each.value.home -- lookup(each.value, "home") - example.tf #geshi(text){{ variable users { type = map(map(string)) default = { user01 = { home = "/home/user01" shell = "/bin/bash" } www01 = { home = "/var/www/html" shell = "/bin/false" } } } resource null_resource user { for_each = var.users triggers = { user = each.key home = each.value.home shell = each.value.shell } } }} -- 実行結果 #geshi(shell){{ terraform plan ... # null_resource.user["user01"] will be created + resource "null_resource" "user" { + id = (known after apply) + triggers = { + "home" = "/home/user01" + "shell" = "/bin/bash" + "user" = "user01" } } # null_resource.user["www01"] will be created + resource "null_resource" "user" { + id = (known after apply) + triggers = { + "home" = "/var/www/html" + "shell" = "/bin/false" + "user" = "www01" } } Plan: 2 to add, 0 to change, 0 to destroy. }} ---- *** for_eachリソースをoutputしたい場合 [#nc77c772] - resource["string"].attr が必要。 #geshi(shell){{ output "user_home" { value = [ for value in null_resource.user : value.home ] } }} ---- *** list(map) 型をfor_eachでループ [#m9003245] - 「for_each = local.ses_dkim」はエラーになる #geshi(text){{ locals { # ses dkimのdataリソースで参照したほうが良いが、まだ存在しない ses_dkim = [ { "type" = "CNAME" "name" = "dummy1._domainkey.example.com" "value" = "dummy1.dkim.amazonses.com" }, { "type" = "CNAME" "name" = "dummy2._domainkey.example.com" "value" = "dummy2.dkim.amazonses.com" }, { "type" = "CNAME" "name" = "dummy3._domainkey.example.com" "value" = "dummy3.dkim.amazonses.com" }, ] } resource "aws_route53_record" "ses_dkim" { for_each = { for i in local.ses_dkim : i.name => i } zone_id = data.aws_route53_zone.example_com.zone_id name = each.value.name type = each.value.type ttl = "600" records = [each.value.value] } }} ---- ** Registry: 公開moduleの使用 [#h3a18059] - [[Terraform Module Registry:https://registry.terraform.io/]] -- HashiCorp Verified Modulesマークが付いたものが参考になる - 記事 -- [[Terraform RegistryのModuleを使ってAWSリソースを作成してみた | Developers.IO:https://dev.classmethod.jp/etc/terraform_registry_module/]] ---- ** 肥大化した.terraformディレクトリの削除 [#v1451677] - 「terraform init」すると、依存providerを.terraformディレクトリへダウンロードする。 - aws providerだけでも150MBある。複数のディレクトリでterraform initすると、サイズが膨れ上がる。 #geshi(bash){{ find . -name .terraform -type d | xargs du -sh }} - .terraformディレクトリをすべて消す。「terraform init」で復帰するので問題ない #geshi(bash){{ # 対象ディレクトリ一覧 find . -name .terraform -type d # 削除実行 find . -name .terraform -type d | xargs -i rm -rf {} }} ---- ** CodePipelineから実行 [#mc004805] - 個人的に確認したいポイント -- stateファイルをs3 backendに置く -- stateファイルのロック処理 -> DynamoDB table -- コードの書き方によっては、毎回「force new resource」が発生し、既存リソースを破壊してしまうので、その確認が必要と思う - 記事 -- [[CodePipeline で承認プロセスを設けた Terraform workspace の CI/CD パイプライン実装 | Developers.IO:https://dev.classmethod.jp/cloud/aws/terraform-workspace-cicd-pipeline-with-aws-codepipeline/]] -- [[CodePipeline で簡単 Terraform CI/CD パイプラインの実装 | Developers.IO:https://dev.classmethod.jp/cloud/aws/simple-terraform-cicd-pipeline-with-aws-codepipeline/]] ---- ** public ipの取得 [#e0009f57] - サービスを提供しているサイト -- https://ifconfig.co/ip -- http://checkip.amazonaws.com/ - 記事 -- [[Terraformで自分のパブリックIPを使う方法 | Developers.IO:https://dev.classmethod.jp/cloud/reference-my-pubic-ip-in-terraform/]] ---- ** リファクタリング [#tffc2b28] - 記事 -- [[実録!Terraform セルフリファクタリング | Developers.IO:https://dev.classmethod.jp/cloud/aws/terraform-self-refactoring/]] ---- ** planやapplyの高速化 [#cdf2e141] リソースが増えてくると、planやapplyがどんどん遅くなる。 - [[Command: plan - Terraform by HashiCorp:https://www.terraform.io/docs/commands/plan.html]] - 並列度の指定: デフォルト10 #geshi(bash){{ export TF_CLI_ARGS_plan="--parallelism=20" export TF_CLI_ARGS_apply="--parallelism=20" }} ---- ** listやmapの入れ子 [#s4a88ee4] v0.11とv0.12では挙動がまったく異なる場合がある。また、関数も入れ子だと動かない場合がある。~ - 例: list[ list[ map {} ] ] のケース #geshi(text){{{ list[ list[ map { }, map { } ] ] }}} - 例: aws_instanceのebs_block_deviceのvolume_idを取得 - v0.12: #geshi(text){{{ aws_instance.web[count.index].ebs_block_device[*].volume_id[*] }}} - v0.11: #geshi(text){{{ [count.index] # idが欲しい場合 id = aws_instance.web.*.id[count.index] # 一度localへ入れる場合 locals { web_ebs1 = "${flatten(aws_instance.web.*.ebs_block_device)}" } ... ${lookup(local.web_ebs1[count.index], "volume_id")} }}} - flatten() -- v0.11: list[list[map{}]] -> list[map{}] 構造になる。 - element() -- v0.11: list[map{}] 構造はエラー - slice() -- v0.11: list[map{}] 構造は成功する ---- ** Terraform Cloud: [#l0b8f68d] - https://www.hashicorp.com/products/terraform/ - 記事 -- [[5人まで無料! Terraform Cloudを使ってみた | DevelopersIO:https://dev.classmethod.jp/cloud/aws/terraform-cloud/]] -- [[Terraform Cloud は AWS の credentials を持たせずに tfstate だけ管理することができる | DevelopersIO:https://dev.classmethod.jp/cloud/aws/manage-tfstate-terraform-cloud/]] ---- ** graph: リソースの依存関係を画像で出力 [#r3a030ed] Cycleエラーの解消などに利用できる。 - 記事 -- [[TerraformでCycleエラーが起きてるリソースだけを画像で表示して修正する | DevelopersIO:https://dev.classmethod.jp/etc/terraform-securitygroup-cycle-fix/]] ---- ** count: ループ処理 [#d723c375] EC2をN台作る等、同じ種類のリソースを複数定義する時に使える。 - [[count: Multiple Resource Instances By Count:https://www.terraform.io/docs/configuration/resources.html#count-multiple-resource-instances-by-count]] - 「<resource>.count = 2」のように設定する。「count=0」にするとリソースは生成されない。 - 「<resource>.count」 はドキュメントに無くても設定できる。 - 「count.index」でループカウンタとして参照できる。 - 記事 -- [[terraform で入れ子ループ - Qiita:https://qiita.com/raki/items/ddb58b9b71f1e8d412a5]] -- [[Terraformで超サクッとループでリソースを用意する方法 | DevelopersIO:https://dev.classmethod.jp/cloud/aws/terraform-network-variable/]] ---- ** csv, json, yaml を読む [#aa769be5] - [[csvdecode - Functions - Configuration Language | Terraform | HashiCorp Developer:https://developer.hashicorp.com/terraform/language/functions/csvdecode]] - [[yamldecode - Functions - Configuration Language - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/functions/yamldecode.html]] -- v0.12以降で使える -- jsonとは違い、コメントが書けるので「=jsonencode(yamldecode(file("vars.yaml")))」のようにjsonに変換させる事も可能。 -- vars.yml #geshi(yaml){{{ string01: string01 string02: string02 list01: - "aaa" - "bbb" dict01: key1: val1 key2: val2 }}} -- example.tf #geshi(text){{{ output "external_yamldecode" { value = yamldecode(file("./vars.yml")) } }}} -- 実行 #geshi(bash){{ terraform init terraform plan terraform apply ... Outputs: external_yamldecode = { "dict01" = { "key1" = "val1" "key2" = "val2" } "list01" = [ "aaa", "bbb", ] "string01" = "string01" "string02" = "string02" } }} - [[jsondecode - Functions - Configuration Language - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/functions/jsondecode.html]] -- v0.11以降 ---- ** data.external: 外部コマンドの結果を取り込む [#f126b743] - [[External Data Source - Terraform by HashiCorp:https://www.terraform.io/docs/providers/external/data_source.html]] -- stging型はOKだが、listやdict型は非対応なようだ。v0.11.14, v0.12.6で確認。 -- 暫定対応として、listはスペースやカンマ区切りでjoinして返す等。dictは不明。 -- [[Feature Request - Allow list/array in 'query' in 'external' data source · Issue #2 · terraform-providers/terraform-provider-external:https://github.com/terraform-providers/terraform-provider-external/issues/2]] -- read-from-yaml.py #geshi(python){{{ #!/usr/bin/env python # -*- coding: utf-8 -*- # # read yaml file to json # # Requirements: # python 2.7 # pip install pyyaml from __future__ import print_function import sys import json import yaml from pprint import pprint def read_stdin(): return {x.strip() for x in sys.stdin} def main(): """ main """ try: stdin_lines = read_stdin() for line in stdin_lines: if line: jsondata = json.loads(line) f = open(jsondata["in_file"], "r") data = yaml.load(f, Loader=yaml.SafeLoader) f.close() sys.stdout.write(json.dumps(data)) except Exception, e: import traceback traceback.print_exc() sys.exit(1) if __name__ == '__main__': main() # vim: ts=4 sw=4 expandtab }}} -- read-from-yaml.py 単体の実行結果 #geshi(bash){{{ echo '{"in_file":"./vars.yml"}' | ./read-from-yaml.py {"string02": "string02", "string01": "string01"} }}} -- example.tf #geshi(text){{{ data "external" "yaml" { program = ["python", "${path.module}/read-from-yaml.py"] query = { in_file = "./vars.yml" } } output "external_yaml" { value = "${data.external.yaml.result}" } output "external_yamldecode" { value = yamldecode(file("./vars.yml")) } }}} -- terraform実行 #geshi(bash){{ terraform init terraform plan terraform apply ... Outputs: external_yaml = { string01 = string01 string02 = string02 } }} ---- ** data.remote_state: 他のtfstateのリソースを参照する [#h11a9202] - [[Terraform: terraform_remote_state - Terraform by HashiCorp:https://www.terraform.io/docs/providers/terraform/d/remote_state.html]] -- outputリソースのvalueを取得できる。 #geshi(text){{ output "name" {value="..."} }} - 参照先 #geshi(text){{ data.terraform_remote_state.example.outputs.name }} - 記事 -- [[【Terraform】terraform_remote_stateデータソースの使い方 | DevelopersIO:https://dev.classmethod.jp/cloud/aws/how-to-use-terraform-remote-state/]] ---- ** Atlantis: terraformをpull requestベースで駆動する [#w4a0983f] - [[Terraform Pull Request Automation | Atlantis:https://www.runatlantis.io/]] - 記事 -- [[TerraformをPull Request上のコマンドで実行!Atlantisを試してみた | DevelopersIO:https://dev.classmethod.jp/cloud/aws/atlantis-terraform/]] ---- ** providerのバージョン固定 [#d7b872f2] - [[Providers - Configuration Language - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/providers.html]] - [[Releases · terraform-providers/terraform-provider-heroku:https://github.com/terraform-providers/terraform-provider-heroku/releases]] -- 2.0.0から互換性がなくなって、heroku_appに書いたconfig_varsが差分として出るようになった。 -- 1.9.0に固定したい場合 #geshi(){{ provider "heroku" { version = "= 1.9.0" ... } }} ---- ** providerの更新 [#k7a27d84] - https://developer.hashicorp.com/terraform/cli/commands/init#upgrade-1 - .terraformに保存されているので、最新に更新したい場合 #geshi(bash){{ terraform init -upgrade # or rm -rf .terraform terraform init }} ---- ** terraformバージョンの固定 [#w79789b2] terraformはバージョンが0.0.1でも違うとコードの互換性が無くなる事が多い。~ そのためバージョンを固定したい場合がある。 - [[Specifying a Required Terraform Version:https://www.terraform.io/docs/configuration/terraform.html#specifying-a-required-terraform-version]] - versions.tf: v0.12以上を使うように明示する。このファイル名は「terraform 0.12upgarade」で自動的に作られる #geshi(text){{ terraform { required_version = ">= 0.12.0" } }} - v0.11.0以上、0.12.0未満を指定。ただし、v0.12.3で試した所、構文チェックをバージョンチェックよりも先に行うようで、v0.11の構文エラーだけ表示されて、バージョンのエラーは出ない。 #geshi(text){{ terraform { required_version = ">= 0.11.0, < 0.12.0" } }} ---- ** tfenv: 複数バージョンの切替 [#b13dc1f4] - [[tfutils/tfenv: Terraform version manager:https://github.com/tfutils/tfenv]] - 記事 -- [[tfenvでTerraformのバージョン管理をする - Qiita:https://qiita.com/kamatama_41/items/ba59a070d8389aab7694]] - [[Memo/Linux/anyenv]]がtfenvに対応しているので便利。 - CentOS 7.xの場合: [[Memo/Linux/Bash#a4d5a585]] が設定済みである事 #geshi(bash){{ git clone https://github.com/tfutils/tfenv.git ~/.tfenv echo '[[ -d ~/.tfenv/bin ]] && export PATH="${PATH}:~/.tfenv/bin"' > ~/.bash.d/tfenv.sh chmod +x ~/.bash.d/tfenv.sh source ~/.bashrc # v0.11.xの最新をインストール tfenv install latest:^0.11 # カレントディレクトリをv0.11.xにする。 .terraform-version が書き換わる。~/で実行すればデフォルト値になる。 cd ~/ tfenv use 0.11.14 }} - v0.12.xと併用 #geshi(bash){{ # 特定ディレクトリだけ0.12に変更 tfenv install latest:^0.12 # カレントディレクトリの .terraform-version が0.12.xへ変わる # 特定ディレクトリのバージョンを0.12.xへ固定 echo latest:^0.12 > .terraform-version }} terraformはサイズが大きく、バージョンアップも頻繁なので古いバージョンを消したい。 - 最新の0.12だけ残し、古い0.12だけ削除 #geshi(bash){{ tfenv list | grep -o -E '0.12.[0-9]+' | sort -n | head -n -1 | xargs -i bash -c 'tfenv uninstall {}' }} ---- ** v0.14 [#dc590c2e] - [[Upgrading to Terraform v0.14 - Terraform by HashiCorp:https://www.terraform.io/upgrade-guides/0-14.html]] -- terraform plan時に差分のみ表示されるようになった。v0.14.7: ブロックの中の属性が一つでも変わったら、ブロック全体が差分として表示される。 -- terraform init時に「.terraform.lock.hcl」が作成されるようになった。commitする必要がある - 記事 -- [[terraform 0.14.0 terraform.lock.hclはレビュー時に理解必須なので要約してみた - Qiita:https://qiita.com/nyamada43/items/b8becb672ad572897c25]] -- [[Terraform 0.14 GA!したので新機能幾つか試してみた | Developers.IO:https://dev.classmethod.jp/articles/terraform-0-14-ga/]] ---- *** upgrade [#t5ca7261] - 0.13まであった「terraform 0.XXupgrade」のコマンドは無い - 0.12から0.14への直接アップグレードはできない? 0.13をインストールしてupgradeする必要がありそう。[[Memo/Terraform#sbbca418]] ---- ** v0.13 [#a73a4500] - [[Upgrading to Terraform v0.13 - Terraform by HashiCorp:https://www.terraform.io/upgrade-guides/0-13.html]] -- 「terraform 0.13upgrade」がある。0.12から0.13への構文変換に使える。0.12upgradeは無くなっている - 記事 -- [[祝GA! Terraform 0.13 新機能を使ってみた | Developers.IO:https://dev.classmethod.jp/articles/terraform013/]] -- [[Announcing HashiCorp Terraform 0.13 General Availability:https://www.hashicorp.com/blog/announcing-hashicorp-terraform-0-13/]] ---- *** エラーの修正 [#fd34a683] - init時のエラー #geshi(text){{ terraform init This configuration or its associated state refers to the unqualified provider "aws". }} -- 修正 #geshi(text){{ terraform state replace-provider 'registry.terraform.io/-/aws' 'registry.terraform.io/hashicorp/aws' }} ---- *** upgrade [#sbbca418] - upgrade #geshi(bash){{ echo latest:^0.13 > .terraform-version rm versions.tf terraform 0.13upgrade }} - versions.tf が自動的に変更された #geshi(text){{ terraform { required_version = ">= 0.13" required_providers { aws = { source = "hashicorp/aws" } } } }} ---- ** v0.12 [#r373b848] v0.11.x以下とコードの互換性が無くなかった。 - [[Upgrading to Terraform 0.12 - Terraform by HashiCorp:https://www.terraform.io/upgrade-guides/0-12.html]] -- 以下コマンドでソースコードを0.12に対応してくれる。ただし、幾つかのエラーは解決しなかったので、手動で直す。 #geshi(bash){{ terraform 0.12upgrade }} -- versions.tf が作成され、version >= 0.12以上に指定される。 - リソース名に"."は使えない。「0-9, A-Z, a-z, _」にする。 -- 0.11: output "public_dns.web_01" { -- 0.12: output "public_dns_web_01" { - 「=」を明示的に追加する -- 0.11: tags { ... } -- 0.12: tags = { ... } - リソース名をダブルクオートで括らない -- 0.11: value = "${aws_lb.example.dns_name}" -- 0.12: value = aws_lb.example.dns_name - リスト型を[]の中に入れない -- 0.11: value = ["${var.example_list}"] -- 0.12: value = var.example_list - 複数リソースの参照 -- 0.11: "${aws_instance.example.*.id}" -- 0.12: aws_instance.example[*].id - 複数リソースの件数 -- 0.11: "${length(aws_instance.example.*.id)}" -- 0.12: length(aws_instance.example) or length(aws_instance.example[*].id) - 複数リソース中の単一リソースの参照。indexでアクセスできない箇所でもlengthが使えるようになった。 -- 0.11: "${aws_instance.example.*.id[0]}" -- 0.12: aws_instance.example[0].id - 記事 -- [[Terraform v0.12で変わるHCLの記述について - Qiita:https://qiita.com/dd511805/items/6e8dd1cf8335d244cf78]] -- [[Terraform 0.12がリリースされたのでアップグレードしてみた | DevelopersIO:https://dev.classmethod.jp/tool/terraform-upgrade-from-0-11-to-0-12/]] -- [[Terraform 0.11→0.12で追加された新機能 | DevelopersIO:https://dev.classmethod.jp/tool/terraform-0-12-new-features/]] ---- ** ベストプラクティス [#b3890718] - [[Welcome - Terraform Best Practices:https://www.terraform-best-practices.com/]] -- ネーミングルール等分かりやすい - [[Terraform Recommended Practices - Terraform by HashiCorp:https://www.terraform.io/docs/enterprise/guides/recommended-practices/index.html]] -- ディレクトリ構造の例: [[Code structure examples:https://www.terraform-best-practices.com/examples/terraform]] Small, Medium, Largeと3パターンあった - 記事 -- [[Terraform 運用ベストプラクティス 2019 ~workspace をやめてみた等諸々~ - 長生村本郷Engineers'Blog:https://kenzo0107.github.io/2019/04/16/2019-04-17-terraform-2019-workspace/]] -- [[【Terraform】モジュールに関する個人的ベストプラクティス - Qiita:https://qiita.com/bigwheel/items/a9e8dd4e12062ac12fe8]] -- [[【Terraform】moduleのアンチパターンとそれに対するベストプラクティス5選 - Qiita:https://qiita.com/bigwheel/items/2b420183639416b5c6bb]] -- [[Serverless×Terraformモジュール設計のベストプラクティスの検討~IoTデータ収集基盤の例~ | Developers.IO:https://dev.classmethod.jp/articles/investigation-of-serverless-and-terraform-module-structure/]] -- [[Terraformのベストなプラクティスってなんだろうか | Future Tech Blog - フューチャーアーキテクト:https://future-architect.github.io/articles/20190903/]] ---- ** module: リソースのモジュール化 [#aefebc48] 同一のリソースを名前やアカウント、リージョンだけ作成したい時に、コードを共通化できる。 - [[Modules | Terraform - HashiCorp Learn:https://learn.hashicorp.com/terraform/getting-started/modules.html]] -- main.tfからmodule/側の変数を参照したい場合、module側で"output ID1 {...}"のように定義する。outputで定義すると、stateファイル内にそのID/valueが入り、terraform_remote_state で参照できる。 不便な点: - module内のリソース名は変わっていなくても、module名を変えただけで、リソースの作り直しが発生する。-> [[move {}:https://developer.hashicorp.com/terraform/cli/state/move]]リソースが使えるようになった。 - module内でdepends_onが使えないので、moduleが使うリソースは先にできていないと実行に失敗する。v0.12.xで対応予定? -- [[depends_on cannot be used in a module · Issue #10462 · hashicorp/terraform:https://github.com/hashicorp/terraform/issues/10462]] -- リソース内からmoduleへの依存は書ける。 #geshi(text){{ aws_elb "example" { ... depends_on = [ "module.example" ] }} - v0.11, v0.12: moduleを呼び出し元でループ処理(count, for_each)出来ない。module内でのループはできる。 moduleを使わない方法: - [[Terraformでmoduleを使わずに複数環境を構築する:https://zenn.dev/smartround_dev/articles/5e20fa7223f0fd]] 記事: - [[terraform_module_ Beginner - Speaker Deck:https://speakerdeck.com/yonasou/terraform-module-beginner]] - [[Terraformモジュール構成のベストプラクティス - ENECHANGE Developer Blog:https://tech.enechange.co.jp/entry/2024/07/22/170825]] - [[[Terraform]Module間の値の受け渡しについて | Developers.IO:https://dev.classmethod.jp/articles/terraform_module_coordination/]] ---- *** module内で別providerの参照 [#h6d80742] - [[Passing Providers Explicitly:https://www.terraform.io/docs/configuration/modules.html#passing-providers-explicitly]] - module側: -- providerが一つだけなら、定義不用。複数ある場合、aliasで分ける。 -- modules/tunnel.tf #geshi(text){{{ provider "aws" { alias = "src" } }}} - 呼び出し側: #geshi(text){{{ provider "aws" { alias = "usw1" region = "us-west-1" } module "example" { source = "../modules/tunnel" providers = { aws.src = "aws.usw1" } } }}} ---- *** module内で作成されたリソースの参照 [#lc97d58b] - module内でoutputで出力した値が、呼び出し元から参照できる - [[Output Value Documentation:https://www.terraform.io/docs/configuration/outputs.html#accessing-child-module-outputs]] - module/server/ec2.tf #geshi(text){{{ variable "var1" { default = "var1" } resource "aws_instance" "server" { ... } output "private_ip" { value = aws_instance.server.private_ip } }}} - module呼び出し側 #geshi(text){{{ # 変数の参照 "${module.server.var1}" # 動的リソースの参照 "${module.server.private_ip}" }}} ---- ** 便利な関数 [#s7ffc12b] - [[Interpolation Syntax - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/interpolation.html]] - compact(list): listから空の要素を削除する。 #geshi(bash){{ security_groups = ["${compact(list("sg-xxA", "sg-xxb", ""))}]" }} - replace(string, search, replace): stringをsearchで検索して、replaceで置換する。searchには[[正規表現:https://github.com/google/re2/wiki/Syntax]] も使え、"/search/" のように指定する。 -- 例: "arn:aws:iam::123456789012:role/example_global"のような文字列が欲しい場合。[[data.aws_iam_role.example.arn:https://www.terraform.io/docs/providers/aws/d/iam_role.html]]が使える場合は、そちらが良い。 #geshi(text){{{ # data.aws_caller_identity.current.arn = arn:aws:iam::123456789012:user/user01 locals { role_arn = "${replace(data.aws_caller_identity.current.arn, "/user\\/.+/", "")}role/example_global" } }}} ---- **VSCode(Visual Studio Code)拡張 [#q87022cd] - [[Terraform - Visual Studio Marketplace:https://marketplace.visualstudio.com/items?itemName=4ops.terraform]] -- 2023-04: WSL2環境でも、シンタックスハイライトは動作した - [[HashiCorp Terraform - Visual Studio Marketplace:https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform]] -- 2023-04: WSL2環境では、シンタックスハイライトが動作せず ---- **無停止での更新・デプロイ/Rolling Update [#ea5bcdff] 戦略: - 新しいインスタンスを追加して、古いインスタンスは削除。terraform自体、updateが苦手。 -記事 --[[Zero Downtime Updates with HashiCorp Terraform:https://www.hashicorp.com/blog/zero-downtime-updates-with-terraform]] --[[Terraformで始めるRolling deployment - Qiita:https://qiita.com/neko-neko/items/8ad86e733861bb784d2d]] ---- ** 複雑な変数の定義と参照 [#r2303300] - terraform v0.11.x ではmapにlistを入れたりできないため、複雑な変数を定義する場合、複数のmapを組み合わせて、lookup()等で参照する方法がある。 - yamlだとこんな感じの変数をterraformで定義したい。 #geshi(yaml){{ tables: key01: attr01: 10 key02: attr01: 20 }} -- example.tf: terraformでは複数リソースはcountでループするため、indexをわざわざ定義している #geshi(text){{ variable "tables" { default = { "0" = "key01" "1" = "key02" } } output "tables.count" { value = "${length(var.tables)}" } # 2 output "tables.key0" { value = "${lookup(var.tables, 0)}" } # key01 variable "tables_attr01" { default = { "key01" = "10" "key02" = "20" } } output "tables_attr01.key01.val.way1" { value = "${lookup(var.tables_attr01, "key01")}" } # 10 output "tables_attr01.key01.val.way2" { value = "${lookup(var.tables_attr01, lookup(var.tables, 0))}" } # 10 }} -- 実行結果 #geshi(bash){{ terraform apply ... Outputs: tables.count = 2 tables.key0 = key01 tables_attr01.key01.val.way1 = 10 tables_attr01.key01.val.way2 = 10 }} ---- **三項演算子/条件によりリソースの作成を制御する [#z25e1527] - v0.8.11現在、if文は無い。 - 「<resource>.count」 はドキュメントに無くても設定できる。「count=0」にするとリソースは生成されない。 -[[Interpolation Syntax - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/interpolation.html#conditionals]] > CONDITION ? TRUEVAL : FALSEVAL - iam_role等のglobalに一つだけあれば良いケースもこれで対応できる。 -- module側で 「create_iam_role = true」等のフラグを用意しておく。 #geshi(text){{{ # moduleを同一アカウントで、複数回使う場合、2回目以降はfalseにしておく。 variable create_iam_role { default = true } resource "aws_iam_role" "example_global" { count = "${var.create_iam_role ? 1 : 0}" name = "example_global" ... }}} - "var.something"がTRUEの時に、1台のインスタンスを立てる #geshi(c){{{ resource "aws_instance" "vpn" { count = "${var.something ? 1 : 0}" } }}} - 指定S3 bucketのreadonly用roleを作成。"var.s3_r_bucket_arns" が空の時にはpolicyを作成しない。 aws_iam_role_policy.countはドキュメントに無いが設定できる #geshi(c){{{ # example: ["arn:aws:iam::1234567890:root", "..."] variable account_id_arns { default = [] } # example: ["arn:aws:s3:::example1, "..."] variable s3_r_bucket_arns { default = [] } variable aws_iam_role_name { default = "assume-role-s3" } resource "aws_iam_role" "assume-role-s3" { name = "${var.aws_iam_role_name}" assume_role_policy = "${data.aws_iam_policy_document.assume-role.json}" } resource "aws_iam_role_policy" "s3-r" { name = "s3-r" role = "${aws_iam_role.assume-role-s3.id}" policy = "${data.aws_iam_policy_document.s3-r.json}" count = "${ length(var.allowed_r_s3_bucket_arns) > 0 ? 1 : 0 }" } data "aws_iam_policy_document" "assume-role" { # assume role statement { actions = [ "sts:AssumeRole", ] effect = "Allow" principals = { type = "AWS" identifiers = "${var.account_id_arns}" } } } data "aws_iam_policy_document" "s3-r" { statement { actions = [ "s3:ListBucket", ] resources = ["${var.s3_r_bucket_arns}"] } statement { actions = [ "s3:GetObject", ] resources = ["${split( ",", "${join("/*,", var.s3_r_bucket_arns)}/*" )}"] } } }}} ---- **fmt: 自動整形 [#lb0faa62] - カレントディレクトリの全ファイルを自動整形する #geshi(bash){{ terraform fmt }} ---- **複数個リソースの参照 [#u395aec4] - terraformのいつのバージョンからか以下のような2つ以上のリソースの場合「.1.」の記述がエラー(not found for variable)になった。 #geshi(text){{ output "web-0001.public_dns" { value = "${aws_instance.web.1.public_dns}" } }} - outputは'*'で複数リソースをJSONで出力可能: #geshi(text){{ output "web.public_dns" { value = "${aws_instance.web.*.public_dns}" } }} - [[element():https://www.terraform.io/docs/configuration/functions/element.html]] 添え字が要素数を超えていた場合、初めからループする。例:AZが3つで、EC2が4台以上ある時、EC2の4台目はAZ1を使って欲しい。 #geshi(text){{ subnet_id = element(data.aws_subnet_ids.main_private.ids[*], count.index) }} - 添え字が要素数を超えた場合、エラーになる #geshi(text){{ aws_instance.web.id[count.index] }} ---- ** workspace: stateを分ける [#cc2de2f5] stateはbackend指定でS3に置けるが、通常default一つしか無い。~ dev, stg, prodのように分けてstateを管理できる。~ awsのregion毎に分けても良い。 -[[State: Workspaces - Terraform by HashiCorp:https://www.terraform.io/docs/state/workspaces.html]] - dev #geshi(bash){{ terraform workspace new dev terraform workspace select dev }} - "${terraform.workspace}"で現在のworkspace名の参照が出来る ---- ** locals: ローカル変数 [#r105258e] -[[Configuring Local Values - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/locals.html]] - NG: variableのdefaultで別変数を参照すると「default may not contain interpolations」エラーになる #geshi(text){{ variable "cidr_whitelist" { type = "list" default = [ { "value" = "${var.cidr_office}" type = "IPV4" }, ] }} - OK: locals は別変数の参照が可能 #geshi(text){{ locals { cidr_whitelist = [ { "value" = "${var.cidr_office}" type = "IPV4" }, ] } }} - localsを複数定義すれば、前参照もできる。 #geshi(text){{{ locals { owner = "taro.yamada" site_id = "example-dev" } locals { common_tags = { owner = local.owner site_id = local.site_id } } aws_instance web { ... tags = merge(local.common_tags, { Name = "${local.prefix}-web-01" role = "web" }) } }}} ---- ** terraform-landscape: planの結果を見やすく [#c027fb68] -[[GitHub - coinbase/terraform-landscape: Improve Terraform's plan output to be easier to read and understand:https://github.com/coinbase/terraform-landscape]] --plan結果の色づけ、JSONを差分表示 -- terraform v0.11 まで対応。v0.12はterraform側のdiffが大きく変わり、非対応 -記事 --[[terraform-landscapeでterraform planのdiffをクッソ見やすく整形する - Qiita:https://qiita.com/minamijoyo/items/6b4544ecbeaec675055d]] ---- ** 配列の要素の末尾に文字列を追加 [#n9cd5ef5] 配列の要素の末尾に文字列を結合したい場合がある。 -元変数: ["arn:aws:s3:::example1","arn:aws:s3:::example2"] -求める出力: ["arn:aws:s3:::example1/*","arn:aws:s3:::example2/*"] -記事 --[[[terraform] [小ネタ] 配列の各要素の末尾に文字列を追加した配列を返す - Qiita:https://qiita.com/sonodar/items/a2cf4557c59eae9fb0d7]] -Terraform v0.11.7 -例:[[Amazon S3: 特定の S3 バケットへの読み取りと書き込みアクセスを許可する:https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_s3_rw-bucket.html]] のポリシーを生成したい場合 #geshi(c){{ data "aws_iam_policy_document" "s3-r" { statement { actions = [ "s3:ListBucket", ] resources = ["${var.s3_r_bucket_arns}"] } statement { actions = [ "s3:GetObject", ] resources = ["${split( ",", "${join("/*,", var.s3_r_bucket_arns)}/*" )}"] } } }} - 例: web-01~NNのPublic IP(IPv4)を、他のセキュリティグループに登録したい。セキュリティグループはCIDR形式(0.0.0.0/0)が必要 -- terraform v0.11.14 #geshi(text){{ data "aws_instances" "web" { instance_tags = { "Name" = "web-*" } instance_state_names = ["running", "stopped"] } # 参照する場合 "${split( ",", "${join("/32,", data.aws_instances.web.public_ips)}/32")}", }} ---- ** Terratest: インフラ自動テストツール [#mda42349] Terraformで起動、チェック実行、破棄までを自動化 -記事 --[[構成ファイルの通りにインフラは立ち上がったのか? インフラ自動テストツール「Terratest」がオープンソースで公開 - Publickey:https://www.publickey1.jp/blog/18/_terratest.html]] ---- ** タイムアウトの延長 [#d0b46da3] リソースの作成/削除等で、timeoutする場合がある。 -[[Timeouts:https://www.terraform.io/docs/configuration/resources.html#timeouts]] -aws_db_instanceの場合 #geshi(php){{ resource "aws_db_instance" "timeout_example" { name = "mydb" # ... timeouts { create = "60m" delete = "2h" } } }} ---- ** alicloud(aliyun alibaba) [#o9e2a179] 中国のalicloud用プロバイダー -[[Provider: alicloud - Terraform by HashiCorp:https://www.terraform.io/docs/providers/alicloud/index.html]] -記事 --[[Alibaba Cloud 日本リージョンがTerraformに対応したので試した - Qiita:https://qiita.com/mosuke5/items/a65683ce6569bffd7ef0]] ---- ** ヒアドキュメントでJSON等を綺麗に書く [#e83aaf4e] - [[Expressions - Configuration Language - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/expressions.html]] > "heredoc" -terraformでは引用が「"」のみで、JSON等を書こうとすると、「\"」のエスケープが必要で見難い -[[JSON Formatter & Validator:https://jsonformatter.curiousconcept.com/]] JSONの検証と整形 - ヒアドキュメント「var = <<EOT ~ EOT」が使える - 「<<-」だと、インデントを他の変数と同じに合わせる - 例: #geshi(php){{ resource "aws_iam_policy" "policy" { name = "test_policy" path = "/" description = "My test policy" policy = <<EOT { "Version": "2012-10-17", "Statement": [ { "Action": [ "ec2:Describe*" ], "Effect": "Allow", "Resource": "*" } ] } EOT } }} - ヒアドキュメント内で他リソース"${aws_s3_bucket.example.arn}"等の展開はできる。ただし、terraform v0.11 planでは値は表示されなかったが、apply後は正常だった。 - iam_policyは [[AWS: aws_iam_policy_document - Terraform by HashiCorp:https://www.terraform.io/docs/providers/aws/d/iam_policy_document.html]] を使うとJSONが出力できるのでお勧め。 ---- ** 変数にmap(連想配列)を使う [#pf63099e] -type = "map" は省略できる -aws.tf #geshi(php){{ # https://aws.amazon.com/marketplace/fulfillment?productId=b7ee8a69-ee97-4a49-9e68-afaee216db2e&ref=cns_srchrow&versionTitle=1708 # "${lookup(var.aws_ami_ids, "ap-northeast-1_centos7_2017-10-12")}" variable "aws_ami_ids" { default = { "us-east-1_centos7_2017-10-12" = "ami-db48ada1" "us-west-2_centos7_2017-10-12" = "ami-e535c59d" "ap-southeast-1_centos7_2017-10-12" = "ami-1fbad07c" "ap-northeast-1_centos7_2017-10-12" = "ami-e4599a82" "eu-central-1_centos7_2017-10-12" = "ami-2540f74a" "eu-west-1_centos7_2017-10-12" = "ami-5f76b626" } } variable "aws_region" { default = "ap-northeast-1" } locals { aws_ami_id_centos7 = "${lookup(var.aws_ami_ids, "${var.aws_region}_centos7_2017-10-12")}" } output "aws_ami_ids" { value = "${var.aws_ami_ids}" } output "aws_ami_id_centos7" { value = "${local.aws_ami_id_centos7}" } }} -実行結果 #geshi(bash){{ terraform apply ... aws_ami_id_centos7 = ami-e4599a82 aws_ami_ids = { ap-northeast-1_centos7_2017-10-12 = ami-e4599a82 ap-southeast-1_centos7_2017-10-12 = ami-1fbad07c eu-central-1_centos7_2017-10-12 = ami-2540f74a eu-west-1_centos7_2017-10-12 = ami-5f76b626 us-east-1_centos7_2017-10-12 = ami-db48ada1 us-west-2_centos7_2017-10-12 = ami-e535c59d }} ---- ** import block: import対象リソースをコードに書けるように [#j7947a2d] - [[Import - Configuration Language | Terraform | HashiCorp Developer:https://developer.hashicorp.com/terraform/language/import]] - terraform v1.5から - importコマンドでtfsateにimportするより手間がかからず、コードの変更としてgit等に記録されるのでわかりやすいかも。 ---- **import: 既存のリソースからtfstateを作成する [#p91885ac] - import blockの方が手軽かも -[[Command: import - Terraform by HashiCorp:https://www.terraform.io/docs/commands/import.html]] -- 既存リソースからtfstateファイルを生成する。 -- importが実装されていないリソースも多い。 -- リソースIDを一つ一つ指定しなければいけないので面倒。 -- v0.9.4: route53をimportしようとするとcrashした。 - インポートするリソースを間違った場合、stateから削除してから、再インポート -- [[Command: state - Terraform by HashiCorp:https://www.terraform.io/docs/commands/state/index.html]] #geshi(bash){{ terraform state list terraform state rm <terraform address> terraform import <terraform address> <resource id> }} -記事 -- [[terraformingとAWS CLIとTerraform importを使って、tfファイルを修正するところまでのメモ | DevelopersIO:https://dev.classmethod.jp/cloud/aws/terraforming-terraform-import/]] -- [[既存のAWS環境を後からTerraformでコード化する | DevelopersIO:https://dev.classmethod.jp/cloud/aws/aws-with-terraform/]] ---- **変数 [#db654cdb] -[[Input Variables - Terraform by HashiCorp:https://www.terraform.io/intro/getting-started/variables.html]] - 機密情報をtfファイルに書かないようにしたい場合。「-var "key=value"」が複数使える。ただし、パスワードをこの方法にしてしまうと、apply時に毎回同じパスワードを入力するハメになるのでお勧めしない。 #geshi(bash){{ cat var.tf -- variable api_key {} -- api_key=*** terraform plan -var "api_key=$api_key" }} - 別ファイルにしたい場合: 「-var-file=~/.secret.tfvars」 #geshi(test){{ api_key = "****" }} - パスワード等、ランダム文字列で良い場合は[[生成>Memo/Terraform#t775cf01]]できる。 - v0.11: listをマージしたい場合、 そのまま書ける #geshi(test){{ security_groups = ["sg-xxxx1", "${var.list_sg}"] }} ---- **バージョン変更によるアップグレード方法 [#e530fc0c] -[[Upgrading to Terraform 0.9 - Terraform by HashiCorp:https://www.terraform.io/upgrade-guides/0-9.html]] -[[Upgrading to Terraform 0.7 - Terraform by HashiCorp:https://www.terraform.io/upgrade-guides/0-7.html]] ---- **lifecycle: 指定リソースの差分を無視する [#y3b1fa14] EBSを追加したり、SGを追加しようとするとEC2を作り直してしまう場合がある。 -[[Configuring Resources - Terraform by HashiCorp:https://www.terraform.io/docs/configuration/resources.html#lifecycle]] --create_before_destroy: 既存リソースが有った場合、削除してから作成する --prevent_destroy: リソース保護。削除する時にエラーになる --ignore_changes: 差分があっても無視する - 後から追加したroot_block_deviceの差分を無視したい。 #geshi(){{{ lifecycle { ignore_changes = ["root_block_device"] } }}} ---- **デバッグ [#g55bbc43] -[[Debugging - Terraform by HashiCorp:https://www.terraform.io/docs/internals/debugging.html]] -デバッグログを出す #geshi(bash){{ TF_LOG=DEBUG terraform plan }} ---- **output: 出力だけを見る [#f57703a8] - [[Command: output - Terraform by HashiCorp:https://www.terraform.io/docs/cli/commands/output.html]] public dns, IP等、後で参照したい情報を出力するのに便利。~ refreshまたはapplyした後でしか表示されない。(tfstateファイルの中身を表示しているのでは)~ #geshi(bash){{ terraform output # json terraform output -json }} - 例: 必要な項目だけをjqでtsv出力 #geshi(bash){{ terraform output -json example_domain_validation_options | jq -r ".[] | [.resource_record_name, .resource_record_type, .resource_record_value] | @tsv" _1234567890abcdef1234567890abcdef.example.com CNAME _1234567890abcdef1234567890abcde.abcdefghij.acm-validations.aws. }} ---- ***outputで複数リソースの値を出力 [#h4f1d320] [[Output Variables:https://www.terraform.io/intro/getting-started/outputs.html]]では1つの値しか出力できないようだ。~ -いつのバージョンからかJSONで出力できるようになっていた #geshi(){{ output "web.private_ip" { value = "${aws_instance.web.*.private_ip}" } Outputs: web.private_ip = [ i-1111, i-2222 ] }} -複数EC2のprivate ipをカンマ区切りで出力 #geshi(){{ output "web.private_ip" { value = "${join(",",aws_instance.web.*.private_ip)}" } }} ---- **backend: S3等にtfstateファイルを置く [#v2184c91] - [[Backend Type: s3 | Terraform | HashiCorp Developer:https://developer.hashicorp.com/terraform/language/backend/s3]] -- terraform 1.10からs3 backend上でのファイルでのロックが可能になった。今までのdynamodbは不要になった。 -- [[Terraform S3 Backend でステートロックのための DynamoDB が不要になる use_lockfile = true - kakakakakku blog:https://kakakakakku.hatenablog.com/entry/2025/01/17/012216]] - 記事 -- [[Backend の S3 や DynamoDB 自体を terraform で管理するセットアップ方法 - Qiita:https://qiita.com/saiya_moebius/items/a8f8aa3683c2347d607c]] - v0.9から backendへ変更: [[Backend Type: s3 - Terraform by HashiCorp:https://www.terraform.io/docs/backends/types/s3.html]] - terraform.tf: -- このファイル内で変数は使えなかった。「configuration cannot contain interpolations」エラー #geshi(){{{ terraform { backend "s3" { profile = "myprofile" # shared_credentials_file = "~/.aws/config" # v0.9.3 で試しても失敗 region = "ap-northeast-1" bucket = "mybucket" key = "web/terraform.tfstate" # path/file で1バケットに複数のtfstateを置ける use_lockfile = false # v1.10以降。CIや複数人使用時にロックしたい場合 } } }}} - 実行: terraform v0.9.3 #geshi(bash){{ aws --profile myprofile configure # ~/.aws/credentials に該当キーが出来ているのが重要。 terraform init terraform plan }} ---- ** -target: 指定したリソースだけplan/apply [#gc122720] - 「-target=resource 」オプションがある。複数指定したい場合は「-target=resource1 -target=resource2」とする #geshi(bash){{ terraform plan -target=aws_instance.web[0] }} -[[Command: plan - Terraform by HashiCorp:https://www.terraform.io/docs/commands/plan.html]] - tfファイルから「-target <resource.name>」形式で抽出 #geshi(bash){{ cat example.tf| perl -ane 'if(/resource\s+"([^\"]+)"\s+"([^\"]+)"/){print "-target $1.$2 \\\n";}' }} ---- **変数で配列が扱えない [#s1c6ae0c] -記事 --[[Terraformで複数台のEC2インスタンスを構築する場合のTIPS | Developers.IO:http://dev.classmethod.jp/cloud/tips-for-creating-multiple-aws_instance-resources/]] - %%変数に配列が使用できない。(v0.6.6で確認)%% v0.7以降で使用可能 - [v0.7以降] #geshi(){{{ # example.tf variable "security_groups" { default = ["sg-xx", "sg-x,sg-xxx"] } resource "aws_instance" "web" { ... security_groups = "${var.security_groups}" } }}} - [[Store arrays in variables Issue #57 hashicorp/terraform GitHub:https://github.com/hashicorp/terraform/issues/57]]で要望は上がっているようだ。 - [v0.7未満] 擬似的に配列を設定する #geshi(){{{ # example.tf variable "security_groups" { default = "sg-xx,sg-x,sg-xxx" } resource "aws_instance" "web" { ... security_groups = "${split(",", var.security_groups)}" } }}} ---- ** terraformer: 既存のインフラのインポート。GoogleCloudPlatform製 [#ec80ccb5] - [[GoogleCloudPlatform/terraformer: CLI tool to generate terraform files from existing infrastructure (reverse Terraform). Infrastructure to Code:https://github.com/GoogleCloudPlatform/terraformer]] - 記事 -- [[既存の環境からterraformのファイルを出力するterraformerを使ってみた | DevelopersIO:https://dev.classmethod.jp/cloud/terraformer-aws-check/]] ---- **Terraforming: 既存のインフラのインポート [#o8c54939] ※''terraform v0.7.0''からimport機能が実装されたため、Terraformingは不要かもしれない。 terraform.tfstate や tf形式のソースを生成する [#f45434d2] - https://github.com/dtan4/terraforming -- Terraformすべてのリソースには対応していない - 記事 --[[Terraforming で既存のインフラを Terraform 管理下におく - Qiita:http://qiita.com/dtan4/items/345c56281ab0e87d6646]] - CentOS6.xの場合。ruby 2.1.0以上なので、rbenvやrvmで新しいrubyを使えるようにする #geshi(bash){{ rvm use 2.1.5 ruby --version ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux] rvmsudo gem install terraforming aws-sdk ln -s ~/.aws/config ~/.aws/credentials # なぜか --profileオプションが動作せず export AWS_ACCESS_KEY_ID=**** export AWS_SECRET_ACCESS_KEY=**** export AWS_REGION=ap-northeast-1 terraforming ec2 > terraforming.ec2.tf terraforming ec2 --tfstate >terraforming.terraform.tfstate }} ---- **複数バージョンの切り替え [#v55bc3c4] - [[tfenv>Memo/Terraform#b13dc1f4]]が複数バージョンの切り替えが楽 - バージョンによって動作しない機能があり、古いバージョンに切り替えたい時がある。 -- 0.7.1: terraformコマンドが1ファイルになった -- 0.6.0: CentOS6.x ReleaseMediaが起動しないのが直った? -- 0.5.3: AWS EC2でCentOS6.x ReleaseMediaが起動しない -- 0.3.7: 1回目は成功するが、2回目は、AWS EC2セキュリティグループが壊れる -記事 --[[tfenvでTerraformのバージョン管理をする - Qiita:https://qiita.com/kamatama_41/items/ba59a070d8389aab7694]] tfenvというツールもある - CentOS6.x 64bitの場合 #geshi(bash){{ TERRAFORM_VER=0.8.5 sudo mkdir -p /opt/terraform.${TERRAFORM_VER} sudo wget -O /opt/terraform.${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip sudo unzip -d /opt/terraform.${TERRAFORM_VER}/ /opt/terraform.${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip # v0.7.x以上の場合 sudo alternatives --install /usr/local/bin/terraform terraform /opt/terraform.${TERRAFORM_VER}/terraform 70 # v0.6.x以下の場合 echo alternatives --install /usr/local/bin/terraform terraform /opt/terraform.${TERRAFORM_VER}/terraform 60 \\ > /tmp/install-terraform.sh find /opt/terraform.${TERRAFORM_VER}/ -name 'terraform-*' -type f -printf ' --slave /usr/local/bin/%f %f %p \\\n' >> /tmp/install-terraform.sh sudo bash /tmp/install-terraform.sh }} -バージョンの切り替え #geshi(bash){{ alternatives --display terraform sudo alternatives --set terraform /opt/terraform.${TERRAFORM_VER}/terraform # 削除 sudo alternatives --set terraform /opt/terraform.${TERRAFORM_VER}/terraform }} ---- **不具合 [#rd936ab4] - https://github.com/hashicorp/terraform/issues -- 1つのpolicyに1つのIAMしか関連付けが出来ない。例えばJPリージョンのIAM:s3-jp-rwにAmazonS3FullAccessを割り当てる。同様にUSリージョンにIAM:s3-us-rw を作ろうとすると、IAM:s3-jp-rwのpolicyがデタッチされる --- または、以下の様にusersを配列にする。ただし、jpとusでディレクトリを分けている場合、相互に修正する必要があり面倒 #geshi(bash){{{ resource "aws_iam_policy_attachment" "s3-rw" { name = "s3-jp-rw" users = ["s3-jp-rw","s3-us-rw"] policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess" } }}} ---[[aws_iam_policy_attachment only one per policy · Issue #4165 · hashicorp/terraform · GitHub:https://github.com/hashicorp/terraform/issues/4165]] ---[[provider/aws : IAM policy attachment/detach bug ? · Issue #6045 · hashicorp/terraform · GitHub:https://github.com/hashicorp/terraform/issues/6045]] - v0.6.16: v0.6.15と同様にawsリソースを作り直す不具合有り - v0.6.15: awsリソースを全部作り直そうとする不具合がある。destroy, apply, refresh, planで確認 - v0.6.14: heroku_app SSL証明書更新でクラッシュした - v0.4.2: awsのroot_block_deviceでvolume_size等を変更しようとすると「logicalType cannot be modified on root device」のエラーが出る。v0.3.7へダウングレードした。 - v0.4.2: aws_instance の *_block_device が毎回違うように表示される - v0.4.2: aws_security_group の 自己参照、他セキュリティグループIDの指定がうまくいかない - v0.4.2: --target=リソース名で、ハイフン(-)に対応していない。 例:--target=aws_route53_record.web-01 はNG、web_01ならOK ---- **インストール [#ia31a63e] - [[tfenv>Memo/Terraform#b13dc1f4]]が複数バージョンの切り替えが楽 -[[Download Terraform - Terraform by HashiCorp:http://www.terraform.io/downloads.html]] - CentOS6.x /usr/local/binにインストールする #geshi(bash){{ wget -O terraform_0.3.7_linux_amd64.zip https://dl.bintray.com/mitchellh/terraform/terraform_0.3.7_linux_amd64.zip sudo unzip terraform_0.3.7_linux_amd64.zip -d /usr/local/bin/ terraform version Terraform v0.3.7 }}