【小ネタ】Terraform標準機能を組み合わせて、applyコマンドを使用せずにtfstate・tfファイルに既存リソースの定義をインポートしてみた

importブロックは、既存のリソースの定義をtfstate・tfファイルに自動生成してくれる非常に便利な機能です。 しかし、tfstateファイルへインポートする際にterraform applyコマンドを使用する為、稼働中のリソースに影響を与えてしまう可能性があります。そこで今回は、importコマンドとimportブロックを組み合わせて、applyコマンドを使用せずに既存リソースの定義をtfstateファイルおよびtfファイルに生成する方法を紹介します。
2024.01.30

※本記事ではTerraform v1.7.0を使用しています。

importコマンドとimportブロックについて

importコマンド

importコマンドは、指定した既存リソース及びその設定情報の定義をtfstateファイルに生成するコマンドです。
以下の様に使用します。

terraform import [resourceブロックのtype].[resourceブロックのname] [リソースID]

importブロック

importブロックはTerraform v1.5.0でリリースされた機能です。
importコマンドではリソースの定義はtfstateファイル上にしか生成されず、tfファイルは手書きで編集する必要がありますが、importブロックを使用するとtfファイル上にもリソース定義を自動生成することが可能です。
以下のように定義します。

import {
  id = [リソースID]
  to = [resourceブロックのtype].[resourceブロックのname]
}

上記を定義した後、「terraform apply -generate-config-out=[任意の名前].tf」コマンドを実行することで「[任意の名前].tf」ファイルが生成され、その中にresourceブロックが生成されます。

また、事前にresourceブロックを定義しておくとTerraformはresourceブロックの自動生成を行わず、既存リソースと事前に定義したresourceブロックの紐づけを行います。

importコマンドとimportブロックの使用方法については過去のブログに記載しているので、より詳細な内容を知りたい方はそちらを読んでみてください。

importブロック使用時のリスク

本記事の概要でも触れていますが、importブロックは非常に便利ですが、terraform applyコマンド実行時に既存リソースに影響を与えてしまうリスクがあります。
例えばインポート先のresource定義を事前に作成しておくパターンでは、以下のようなケースが考えられます。

  1. importブロック、resourceブロックを定義
  2. terraform planを実行し、resourceブロックを修正するも設定するAMIのIDを間違える
  3. terraform applyを実行
  4. AMIが異なるため、削除 → 再作成という挙動となる

apply実行前に出力される実行内容を確認すれば防ぐことは可能ですが、個人的には万が一があってはならない本番環境等ではterraform applyコマンドは避けたいところです。

terraform applyを使用せずに、tfstate・tfファイルに既存リソースをインポートする方法

ここから、terraform applyを使用せずに、tfstateファイルとtfファイル両方に既存リソースの定義をインポートする方法を紹介します。

① インポート対象リソースの作成

「manabe-test-instance」というNameタグを付けたEC2インスタンスを作成します。
後の手順で使用する為、リソースID、AMI、インスタンスタイプを控えておきます。

② importブロックによるtfファイルの生成

importブロックを定義します。

import {
  id = "i-0b83e2e9e7d9d13bf"
  to = aws_instance.sample1
}

「terraform plan -generate-config-out=[任意の名前].tf」コマンドを実行します。

$ terraform plan -generate-config-out=import.tf
aws_instance.sample1: Preparing import... [id=i-0b83e2e9e7d9d13bf]
aws_instance.sample1: Refreshing state... [id=i-0b83e2e9e7d9d13bf]

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Warning: Config generation is experimental
│
│ Generating configuration during import is currently experimental, and the generated configuration format may change in future versions.
╵
╷
│ Error: Conflicting configuration arguments
│
│   with aws_instance.sample1,
│   on import.tf line 14:
│   (source code not available)
│
│ "ipv6_address_count": conflicts with ipv6_addresses
╵
╷
│ Error: Conflicting configuration arguments
│
│   with aws_instance.sample1,
│   on import.tf line 15:
│   (source code not available)
│
│ "ipv6_addresses": conflicts with ipv6_address_count

import.tfファイルが生成され、中に「manabe-test-instance」のリソース定義が出力されていますが、共存できないパラメータが生成されてしまっているためplanコマンドの実行結果としてエラーが出力されています。

今回は、「ipv6_addresses」と「ipv6_address_count」が競合していたため、「ipv6_address_count」をコメントアウトし、オプションを付けずにterraform planコマンドを実行します。

$ terraform plan
aws_instance.sample1: Preparing import... [id=i-0b83e2e9e7d9d13bf]
aws_instance.sample1: Refreshing state... [id=i-0b83e2e9e7d9d13bf]
~~ 省略 ~~
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.

エラーが消えました。

この時点で、importブロックを削除しておきます。

③ importコマンドによるtfstateファイルへの取り込み

以下のようにimportコマンドを実行します。

$ terraform import aws_instance.sample1 i-0b83e2e9e7d9d13bf
aws_instance.sample1: Importing from ID "i-0b83e2e9e7d9d13bf"...
aws_instance.sample1: Import prepared!
  Prepared aws_instance for import
aws_instance.sample1: Refreshing state... [id=i-0b83e2e9e7d9d13bf]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

tfstateファイルの中身を確認します。

無事にリソース定義が生成されていました。

最後に

importブロックを使用することで複数リソースを一度に取り込むことができますが、本手順ではimportコマンドも使用しているうえimportコマンドは1リソースずつしか取り込めない為手順が複雑化してしまっています。

本来は十分に気を付けたうえでimportブロックのみで既存リソースを取り込むことが望ましいと思いますが、どうしても不安がありリスクをなくしたい場合はこの手順を使ってみてください。