コンテンツにスキップ

コマンド@Terraform

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. terraformコマンド

global option

▼ -chdir

コマンドを実行する作業ディレクトリを指定する。


apply

▼ applyとは

インフラリソースをプロビジョニングする。

▼ -destroy

指定したバックエンドで管理するリソースを削除する。

削除後に、パラメーターとして使用したtfvarsファイル自体を削除する必要がある。

# 削除するまでに以下の手順が必要である。

# 初期化
$ terraform init -reconfigure -backend-config=backend.tfvars

# 現状のtfstateファイルと実インフラの間に、差分がないことを確認する。
$ terraform plan -var-file=terraform.tfvars

No changes. Your infrastructure matches the configuration.


# 実行計画
$ terraform plan -destroy -var-file=foo.tfvars

# 削除
$ terraform apply -destroy -var-file=foo.tfvars

▼ -parallelism

並列処理数を設定できる。

デフォルト値は10である。

クラウドプロバイダーのレートリミットが小さい場合は、並列処理数を5ほどに小さくし、コマンドのAPIのコールがレートリミットを超過しないようにする。

$ terraform apply \
    -var-file=foo.tfvars \
    -parallelism=30

オプションで毎回設定するのが大変なため、環境変数で設定しても良い。

$ export TF_CLI_ARGS_plan="--parallelism=50"
$ export TF_CLI_ARGS_apply="--parallelism=50"

▼ -refresh-only

すでに管理対象になっている実インフラが、Terraformの管理外から変更された場合、実インフラの状態はそのままに、tfstateファイルにその状態を書き込む。

もし、Terraform管理外の実インフラがない場合は、No changes.になる。

具体的は、terraform planコマンドで出力されるNote: Objects have changed outside of Terraformの内容を指す。

ただし、そもそもTerraformで管理されていない実インフラ (create処理判定されるもの) を処理することはできず、代わりにterraform importコマンドの実行が必要になる。

$ terraform apply -refresh-only

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
$ terraform plan -refresh-only

# もし、Terraform管理外の実インフラがない場合は、No changes. になる。
No changes. Your infrastructure still matches the configuration.
Changes to Outputs:
  ...

$ terraform apply -refresh-only

Apply complete! Resources: 0 added, 0 changed, 0 destroyed. # 実インフラは変更しない。

▼ -target

特定のresourceブロックを使用して、terraform applyコマンドを実行する。

リリース用のブランチに、今回はリリースしたくない差分が含まれてしまっているような場合、特定の差分のみをプロビジョニングできる。

$ terraform apply \
    -var-file=foo.tfvars \
    -target='<resourceタイプ>.<resourceブロック名>'

moduleブロックを採用している場合、指定の方法が異なる。

$ terraform apply \
    -var-file=foo.tfvars \
    -target='module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>'

*例*

$ terraform apply \
    -var-file=foo.tfvars \
    -target='aws_instance.bastion'

▼ -var-file

クラウドプロバイダー上にクラウドインフラを作成する。

$ terraform apply -var-file foo.tfvars
# ディレクトリを指定することも可能
$ terraform -chdir=<ルートモジュールのディレクトリへの相対パス> apply \
    -var-file=<ルートモジュールのディレクトリへの相対パス>/foo.tfvars

成功すると、以下のメッセージが表示される。

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

.tfplanファイル

事前に、terraform planコマンドによって作成された実行プランファイルを元に、terraform applyコマンドを実行する。

実行プランを渡す場合は、環境変数をオプションに設定する必要はない。

$ terraform apply foo.tfplan


init

▼ initとは

terraformコマンドを実行しているローカルマシンの.terraformディレクトリを初期化 (terraform.lock.hclファイルの作成、ローカル/リモートモジュールやプロバイダーのインストール、バックエンドの切り替えなど) を実行する。

tfstateファイルを書き換えることはしないため、基本的には安全である。

もしプロバイダーをアップグレードした場合は、新バージョンのインストールするために、本コマンドを実行する必要がある。

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of pagerduty/pagerduty from the dependency lock file
# AWSプロバイダーのバージョン
- Installing hashicorp/aws v4.3.0...
- Installed hashicorp/aws v4.3.0 (signed by HashiCorp)
# 使用しているその他のプロバイダーのバージョン
- Installing foo/bar v2.3.0...
- Installed foo/bar v2.3.0 (signed by a HashiCorp partner, key ID *****)

▼ -backend=false

指定したバックエンドの初期化をスキップする。

一度でもバックエンドを初期化している場合は、改めて初期化することは不要なため、このオプションを使用する。

$ terraform init -backend=false
# ディレクトリを指定することも可能
$ terraform -chdir=<ルートモジュールのディレクトリへの相対パス> init -backend=false

▼ -backend=true, -backend-config

指定したバックエンドにあるtfstateファイルを使用して、ローカルマシンの.terraformディレクトリを初期化する。

また、terraform planコマンドやterraform applyコマンドの向き先を別のバックエンドに切り替える。

バックエンドの代わりに、terraformブロック内のbackendオプションで指定しても良い。

ただし、terraform settingブロック内では通常変数を使用できないため、こちらのオプションが推奨である。

$ terraform init \
    -backend=true \
    -reconfigure \
    `# バケット名` \
    -backend-config="bucket=prd-foo-tfstate-bucket" \
    `# tfstateファイル名` \
    -backend-config="key=terraform.tfstate" \
    `# 認証情報ファイルのプロファイル名` \
    -backend-config="profile=bar" \
    -backend-config="encrypt=true"

▼ -reconfigure

初期化のためのterraform initコマンドの時、今現在で設定しているバックエンドにあるtfstateファイルをそのまま使用する。

--migrate-stateオプションとは異なり、元のバックエンドが異なる場合、元のバックエンドのtfstateファイルはそのまま保持される。

$ terraform init -reconfigure -backend-config=./foo/backend.tfvars

また、開発時に一時的にlocalをバックエンドとして使用する場合にも役立つ。

▼ --migrate-state

初期化のためのterraform initコマンドの時、この時、元のバックエンドにあるtfstateファイルをコピーし、指定したバックエンドに移行する。

元のバックエンドのtfstateファイルを削除するか否かを選択できる。

$ terraform init --migrate-state -backend-config=./foo/backend.tfvars

▼ -upgrade

現在のバージョンを基に、自前/公式リモートモジュール、プラグイン、のアップグレード/ダウングレードを行う。

合わせて、.terraform.lock.hclファイルを更新する。

リモートモジュールやプラグインのバージョンを固定していない場合、upgradeオプションによって、最新のバージョンを毎回インストールすることになる。

$ terraform init -upgrade

▼ 問題が起こる場合

terraform initコマンドで以下のようなエラーが起こる場合がある。

 Error: Failed to query available provider packages
│
│ Could not retrieve the list of available versions for provider hashicorp/aws: the previously-selected version <バージョン> is no longer available
╵

その場合、以下のいずれかで解決できることがある。

# プロバイダーの削除
$ rm -r $HOME/.terraform.d/plugins/registry.terraform.io/hashicorp/aws/

# 再インストール
$ terraform init --reconfigure
# プロバイダーの置き換え
$ PROVIDER_VER="<バージョン>"
$ PROVIDER_NAME="aws"
$ ARCH=$(if [ `uname -m` = 'arm64' ]; then echo "darwin_arm64"; else echo "darwin_amd64"; fi)

$ mkdir -p $HOME/.terraform.d/plugins/registry.terraform.io/hashicorp/${PROVIDER_NAME}/${PROVIDER_VER}/${ARCH}/ \
    && wget "https://releases.hashicorp.com/terraform-provider-${PROVIDER_NAME}/${PROVIDER_VER}/terraform-provider-${PROVIDER_NAME}_${PROVIDER_VER}_${ARCH}.zip" \
    && unzip "terraform-provider-${PROVIDER_NAME}_${PROVIDER_VER}_${ARCH}.zip" -d $HOME/.terraform.d/plugins/registry.terraform.io/hashicorp/${PROVIDER_NAME}/${PROVIDER_VER}/${ARCH}/ \
    && rm -f "terraform-provider-${PROVIDER_NAME}_${PROVIDER_VER}_${ARCH}.zip"


fmt

▼ fmtとは

.tfファイルのコードを整形する。

▼ -check

インデントを揃えるべき箇所が存在するか否かを判定する。

もし存在する場合『1』、存在しない場合は『0』を返却する。

$ terraform fmt -check

▼ -diff

インデントを揃えるべき箇所が存在する場合、これを取得する。

$ terraform fmt -diff

▼ -recursive

設定ファイルのインデントを揃える。

処理を行ったファイルが表示される。

# -recursive: サブディレクトリを含む全ファイルをフォーマット
$ terraform fmt -recursive

main.tf


get

terraformコマンドを実行しているローカルマシンの.terraformディレクトリに、ローカル/リモートモジュールをインストールする。

ただし、terraform initコマンドに同じ機能が含まれている。

$ terraform get


graph

▼ graphとは

tfstateファイルに基づいて、リソース間の依存関係をグラフ化する。

これにより、どのresourceブロックが他のどのresourceブロックを使用しているかがわかる。

Graphvizのダウンロードが必要である。

$ brew install graphviz

$ terraform graph | dot -Tpng > graph.png

▼ -draw-cycles

グラフの中で循環参照の矢印を色付きで表示する。

$ terraform graph -draw-cycles | dot -Tpng > graph.png

▼ 図形の見方

図形 種類
楕円 ルートモジュール
菱形 providerブロック
四角 resourceブロック、dataブロック
ノート variableブロック、outputブロック、localブロック

▼ 他のツール

terraform graphコマンドを使用する以外に、リソース間の依存関係をグラフ化する。

  • Terraform graph beautifie
  • Rover
  • Terraform Visual
  • Inframap
  • Pluralith


import

▼ importとは

実インフラの状態を読み込み、tfstateファイルに反映する。

▼ -var-file

.tfvarsファイルを指定して、terraform importコマンドを実行する。

$ terraform import \
    -var-file=foo.tfvars \
    <resourceタイプ>.<resourceブロック名> <実体リソースのARN、ID、名前など>


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.


output

tfstateファイルのoutputブロックを表示する。

$ terraform output -json

{
  "vpc_id": {
    "sensitive": "false",
    "type": "string",
    "value": "vpc-004c2d1ba7394b3d6"
  }
}


plan

▼ planとは

実行計画を取得する。

▼ -destroy

指定したバックエンドで管理するリソースを削除する場合の実行計画を取得する。

$ terraform plan -destroy -var-file=foo.tfvars

Terraform will perform the following actions:

...

Plan: 0 to add, 0 to change, 10 to destroy.

▼ -var-file

クラウドに対してリクエストを行い、現在のインフラリソースの状態をtfstateファイルには反映せずに、設定ファイルの記述との差分を検証する。

スクリプト実行時に、環境変数が定義されたファイルを実行すると、variableブロックで宣言した変数に、値が格納される。

$ terraform plan -var-file=foo.tfvars
# ディレクトリを指定することも可能
# 第一引数で環境変数ファイルの相対パス、第二引数でをルートモジュールの相対パス
$ terraform -chdir=<ルートモジュールのディレクトリへの相対パス> plan \
    -var-file=<ルートモジュールのディレクトリへの相対パス>/foo.tfvars

差分がなければ、以下の通りになる。

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.

▼ -target

特定のresourceブロックを使用して、terraform planコマンドを実行する。terraform planコマンドの最初のRefreshingStateフェーズを実行するブロックも絞り込めるため、特定のブロックRefreshingStateフェーズでバグがある場合の回避策にも使用できる。-targetオプションで指定するアドレスは、terraform planコマンド自身の出力結果や、terraform state listコマンドで確認できる。

$ terraform plan \
    -var-file=foo.tfvars \
    -target='<resourceタイプ>.<resourceブロック名>'

moduleブロックを使用している場合、指定の方法が異なる。

$ terraform plan \
    -var-file=foo.tfvars \
    -target='module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>'

指定方法は、全てのブロックを対象としたterraform planコマンドが参考になる。

grepコマンドを使用してresourceタイプ名やmoduleブロック名で抽出すると、指定方法がわかる。

# resourceブロックの指定方法を調べる。
$ terraform plan | grep <resourceタイプ>

# foo.bar will be created
# foo.baz will be changed

$ terraform plan \
    -var-file=foo.tfvars \
    -target='foo.bar' \
    -target='foo.baz'
# moduleブロックの指定方法を調べる。
$ terraform plan | grep <moduleブロック名>

# module.qux.quux will be changed
# module.qux.corge will be changed
# module.grault will be destroyed

$ terraform plan \
    -var-file=foo.tfvars \
    -target='module.qux.quux' \
    -target='module.qux.corge' \
    -target='module.grault'

▼ -refresh

このオプションをつければ、terraform refreshコマンドを同時に実行してくれる。

ただし、デフォルトでtrueなため、不要である。

$ terraform plan \
    -var-file=foo.tfvars \
    -refresh=true

▼ -parallelism

並列処理数を設定できる。

デフォルト値は10である。

クラウドプロバイダーのレートリミットが小さい場合は、並列処理数を小さくし、コマンドのAPIのコールがレートリミットを超過しないようにする。

$ terraform plan \
    -var-file=foo.tfvars \
    -parallelism=30

▼ -out

実行プランファイルを作成する。

terraform applyコマンドのために使用できる。

$ terraform plan \
    -var-file=foo.tfvars \
    `# 実行プランファイル名` \
    -out=foo.tfplan


planのプラクティス

▼ 出力内容の読み方

リソースの作成 (+) 、更新 (~) 、削除 (-) 、再作成 (-/+) で表す。

+ create
~ update in-place
- destroy
-/+ destroy and then create replacement

前半部分と後半部分に区別されている。

前半部分は、Terraform管理外の方法 (画面上、他ツール) による実インフラの変更について、その変更前後を検出する。

また、クラウドプロバイダーの新機能に伴う新しいAPIの追加も検出される。

検出のため、applyによって変更される実インフラを表しているわけではない。

そして後半部分は、Terraformのコードの変更によって、実インフラがどのように変更されるか、を表している。

結果の最後に表示される対象のresourceブロックの数を確認しても、前半部分のresourceブロックは含まれていないことがわかる。

Note: Objects have changed outside of Terraform

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

  # Terraform管理外の方法 (画面上、他ツール) による実インフラの変更について、その変更前後を検出。

Unless you have made equivalent changes to your configuration, or ignored the
relevant attributes using ignore_changes, the following plan may include
actions to undo or respond to these changes.

─────────────────────────────────────────────────────────────────────────────

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # Terraformのコードの変更によって、実インフラがどのように変更されるか。

Plan: 0 to add, 1 to change, 0 to destroy.

▼ 差分認識される/されない変更

値を変更した場合に差分として認識されるものを示した。

ただし、差分として認識されるもののmovedブロックを使用すれば、差分を回避できる。

変更内容 される/されない
resourceブロック名の変更 される
moduleブロック名の変更 される
ファイルやディレクトリを指定するパスの変更 されない
resourceブロックにハードコーディングされた値を環境変数に変更 (.tfvarsファイルに移行) されない
variablesブロック名の変更 されない


provider

▼ providerとは

terraform.lock.hclファイルを作成する。

*例*

CPUアーキテクチャ (例:Intel、AMD、ARM) を設定しつつ、terraform.lock.hclファイルを作成する。

$ terraform providers lock \
    -platform=darwin_amd64 \
    -platform=darwin_arm64 \
    -platform=linux_amd64 \
    -platform=linux_arm64 \
    -platform=windows_amd64

事前に、terraform.lock.hclファイルを削除する必要がある。

$ rm .terraform.lock.hcl


refresh (非推奨)

▼ -var-file

クラウドに対してリクエストを行い、現在のインフラリソースの状態をtfstateファイルに反映する。非推奨であり、代わりに、terraform apply -refresh-onlyコマンドを使用する。

$ terraform refresh -var-file=foo.tfvars


state

▼ stateとは

tfstateファイルを操作する。

▼ list

tfstateファイルで定義されているresourceブロック (tfstateファイル上ではmanagedモード) の一覧を取得する。terraform applyコマンドで-targetオプションを使用する前にアドレスを確認したい場合や、terraform applyコマンドの実行に失敗した時にtfstateファイルと実インフラにどのような差分があるかを確認する場合に使用する。

$ terraform state list

以下の通り、moduleブロックも含めて、resourceブロックが表示される。

aws_instance.www-1a
aws_instance.www-1c
aws_key_pair.key_pair
module.alb_module.aws_alb.alb
module.ami_module.data.aws_ami.amazon_linux_2
module.route53_module.aws_route53_record.r53_record
module.route53_module.aws_route53_zone.r53_zone
module.security_group_module.aws_security_group.security_group_alb
module.security_group_module.aws_security_group.security_group_ecs
module.security_group_module.aws_security_group.security_group_instance
module.vpc_module.aws_internet_gateway.internet_gateway
module.vpc_module.aws_route_table.route_table_public
module.vpc_module.aws_route_table_association.route_table_association_public_1a
module.vpc_module.aws_route_table_association.route_table_association_public_1c
module.vpc_module.aws_subnet.subnet_public_1a
module.vpc_module.aws_subnet.subnet_public_1c
module.vpc_module.aws_vpc.vpc

▼ pull

リモートにあるtfstateファイルをローカルマシンにダウンロードする。

$ terraform state pull > <tfstateファイル名>

▼ rm

terraform importコマンドでtfstateファイルに反映した設定値を削除する。

count引数やfor_each引数を使用している場合は、シングルクオーテーションで囲う必要がある。

# 関数を使用せずに定義されている場合
$ terraform state rm --dry-run aws_instance.bastion
$ terraform state rm aws_instance.bastion

Removed aws_instance.bastion
Successfully removed 1 resource instance(s).
# moduleブロックを使用して定義されている場合
$ terraform state rm --dry-run module.ec2.aws_instance.bastion
$ terraform state rm module.ec2.aws_instance.bastion

Removed module.ec2.aws_instance.bastion
Successfully removed 1 resource instance(s).
# for_each関数で定義されている場合
$ terraform state rm --dry-run 'aws_instance.bastion["<キー名1>"]'
$ terraform state rm 'aws_instance.bastion["<キー名1>"]'

Removed aws_instance.bastion["<キー名1>"]
Successfully removed 1 resource instance(s).


# その他のキー名も削除が必要になる。
$ terraform state rm --dry-run 'aws_instance.bastion["<キー名2>"]'
$ terraform state rm 'aws_instance.bastion["<キー名2>"]'
# count関数で定義されている場合
$ terraform state rm --dry-run 'aws_instance.bastion[0]'
$ terraform state rm 'aws_instance.bastion[0]'

Removed aws_instance.bastion[0]
Successfully removed 1 resource instance(s).


# その他のインデックス番号も削除が必要になる。
$ terraform state rm --dry-run 'aws_instance.bastion[1]'
$ terraform state rm 'aws_instance.bastion[1]'
# moduleブロックを使用して、for_each関数で定義されている場合
$ terraform state rm --dry-run 'module.ec2.aws_instance.bastion["<キー名1>"]'
$ terraform state rm 'module.ec2.aws_instance.bastion["<キー名1>"]'

Removed module.ec2.aws_instance.bastion["<キー名1>"]
Successfully removed 1 resource instance(s).


# その他のキー名も削除が必要になる。
$ terraform state rm --dry-run 'module.ec2.aws_instance.bastion["<キー名2>"]'
$ terraform state rm 'module.ec2.aws_instance.bastion["<キー名2>"]'

▼ show list

tfstateファイルを表示する。

$ terraform state

{
  "version": 4,
  "terraform_version": "1.0.0",
  "serial": 3,
  "lineage": "*****-*****-*****-*****-*****",
  "outputs": { # outputブロックのapplyで追加される。
    "foo_ids": {
      "value": "*****",
      "type": "string"
    }
  },
  "resources": [
    {
      "mode": "data", # dataブロックのapplyで追加される。
      "type": "aws_caller_identity", # resourceタイプ
      "name": "current", # リソース名
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [ # 設定値
        {
          "schema_version": 0,
          "attributes": {
            "account_id": "<AWSアカウントID>",
            "arn": "*****",
            "id": "*****",
            "user_id": "*****"
            ...
          },
          "sensitive_attributes": []
        }
      ]
    },
    {
      "module": "module.ec2", # moduleブロックの場合に追加される。
      "mode": "managed", # importや、resourceブロックのapplyで追加される。
      "type": "aws_instance", # resourceタイプ
      "name": "bastion", # リソース名
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [ # 設定値
        {
          "schema_version": 0,
          "attributes": {
            "arn": "*****",
            "name": "prd-foo-instance"
            "tags": {
              "Env": "prd",
              "ManagedBy": "terraform"
              "Repository": "https://github.com/*****"
            },
            "description": "*****",
            ...
          }
        }
      ]
    }
  ]
}

特定のresourceブロックのみを表示することもできる。

$ terraform state show 'aws_instance.bastion'


taint

▼ taintとは

バックエンドにあるtfstateファイルにて、指定されたresourceブロックのtaintedフラグを立てる。

▼ -var-file <resourceブロック>

例えば、applyしたが、途中でエラーが発生してしまい、実インフラに中途半端に作成されてしまうことがある。

ここで、taintedを立てておくと、実インフラのresourceブロックを削除したと想定したterraform planコマンドを実行できる。

$ terraform taint \
    -var-file=foo.tfvars \
    module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>

この後のterraform planコマンドのログからも、-/+で削除が行われる想定で、差分を比較していることがわかる。

$ terraform plan -var-file=foo.tfvars

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

-/+ <resourceタイプ>.<resourceブロック名> (tainted) (new resource required)
      id: '1492336661259070634' => <computed> (forces new resource)


Plan: 1 to add, 0 to change, 1 to destroy.


validate

▼ validateとは

設定ファイルの検証を行う。

$ terraform validate

Success! The configuration is valid.
# ディレクトリを指定することも可能
$ terraform -chdir=<ルートモジュールのディレクトリへの相対パス> validate


02. 実インフラの全ての設定値をtfstateファイルに取り込む

なぜimportコマンドが必要なのか

実インフラの全ての設定値をtfstateファイルに取り込む場合、これの設定値をresourceブロックの設定値としてtfstateファイルに書き込み、Terraformの管理下におく必要がある (tfstateファイル上では、resourceブロックはmanagedモードという表記になる) 。

この時、terraform importコマンドを実行するか、コンソール画面から一度削除した上でterraform applyコマンドを実行する方法がある (前者が推奨) 。

執筆時点 (2022/07/19) で、複数のインフラリソースを網羅的に確認する方法は公式になく、インフラリソースを1個ずつ指定して、tfstateファイルに書き込んでいく必要がある。


手順

▼ はじめに

(1)

バックエンドがリモートの場合、ローカルマシンにtfstateファイルをダウンロードする。

# バックエンドがS3バケットの場合
$ aws s3 cp s3://<S3バケット名>/<tfstateファイルへのパス> <ローカルマシンのパス>

# バックエンドがGCSの場合
$ gsutil cp gs://<GCS名>/<tfstateファイルへのパス> <ローカルマシンのパス>
(2)

ダウンロードしたtfstateファイルをlocalバックエンドで指定する。

terraform {

  # ローカルマシンで管理するように設定
  backend "local" {
    path = "terraform.tfstate"
  }
}

▼ 初期化

(3)

localバックエンドで初期化する。

$ terraform init -reconfigure

▼ 実インフラの設定値をtfstateファイルに取り込む

(4)

resourceタイプとresourceブロック名を指定し、tfstateファイルに実インフラの状態を書き込む。

パラメーターの『<resourceタイプ>.<resourceブロック名>』は、terraform planコマンドの結果が参考になる。

また『ARN、ID、名前など』は、resourceタイプによって異なるため、リファレンスの『Import』の項目を確認すること。

何らかの理由でterraform importコマンドを実行し直したい場合は、terraform state rmコマンドでresourceブロックを削除し、改めて書き込む。

# 関数を使用せずに定義されている場合
$ terraform import \
    -var-file=foo.tfvars \
    '<resourceタイプ>.<resourceブロック名>' <実体リソースのARN、ID、名前など>


# もし、中途半端な同じリソースがtfstateファイルにある場合は、事前に削除する。
$ terraform state rm --dry-run '<resourceタイプ>.<resourceブロック名>'
$ terraform state rm '<resourceタイプ>.<resourceブロック名>'
# moduleブロックを使用して定義されている場合
$ terraform import \
    -var-file=foo.tfvars \
    'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>' <実体リソースのARN、ID、名前など>


# もし、中途半端な同じリソースがtfstateファイルにある場合は、事前に削除する。
$ terraform state rm --dry-run 'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>'
$ terraform state rm 'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>'
# for_each関数で定義されている場合
$ terraform import \
    -var-file=foo.tfvars \
    '<resourceタイプ>.<resourceブロック名>["<キー名1>"]' <実体リソースのARN、ID、名前など>


# その他のキー名もimportが必要になる
$ terraform import \
    -var-file=foo.tfvars \
    '<resourceタイプ>.<resourceブロック名>["<キー名2>"]' <実体リソースのARN、ID、名前など>


# もし、中途半端な同じリソースがtfstateファイルにある場合は、事前に削除する。
$ terraform state rm --dry-run '<resourceタイプ>.<resourceブロック名>["<キー名1>"]'
$ terraform state rm --dry-run '<resourceタイプ>.<resourceブロック名>["<キー名2>"]'
$ terraform state rm '<resourceタイプ>.<resourceブロック名>["<キー名1>"]'
$ terraform state rm '<resourceタイプ>.<resourceブロック名>["<キー名2>"]'
# count関数で定義されている場合
$ terraform import \
    -var-file=foo.tfvars \
    '<resourceタイプ>.<resourceブロック名>[0]' <実体リソースのARN、ID、名前など>


# その他のインデックス番号もimportが必要になる
$ terraform import \
    -var-file=foo.tfvars \
    '<resourceタイプ>.<resourceブロック名>[1]' <実体リソースのARN、ID、名前など>


# もし、中途半端な同じリソースがtfstateファイルにある場合は、事前に削除する。
$ terraform state rm --dry-run '<resourceタイプ>.<resourceブロック名>[0]'
$ terraform state rm --dry-run '<resourceタイプ>.<resourceブロック名>[1]'
$ terraform state rm '<resourceタイプ>.<resourceブロック名>[0]'
$ terraform state rm '<resourceタイプ>.<resourceブロック名>[1]'
# moduleブロックを使用して、for_each関数で定義されている場合
$ terraform import \
    -var-file=foo.tfvars \
    'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>["<キー名1>"]' <実体リソースのARN、ID、名前など>


# その他のキー名もimportが必要になる
$ terraform import \
    -var-file=foo.tfvars \
    'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>["<キー名2>"]' <実体リソースのARN、ID、名前など>


# もし、中途半端な同じリソースがtfstateファイルにある場合は、事前に削除する。
$ terraform state rm --dry-run 'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>["<キー名1>"]'
$ terraform state rm --dry-run 'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>["<キー名2>"]'
$ terraform state rm 'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>["<キー名1>"]'
$ terraform state rm 'module.<moduleブロック名>.<resourceタイプ>.<resourceブロック名>["<キー名2>"]'

.tfファイルに実インフラの設定値を取り込む

(5)

tfstateファイルから.tfファイルを表示する。

.tfファイルにこれを定義する。

$ terraform state show '<resourceタイプ>.<resourceブロック名>'`

# <resourceタイプ>.<resourceブロック名>:
resource "<resourceタイプ>" "<resourceブロック名>" {
  ...
}
(6)

terraform importコマンドを実行する。

この時、tfstateファイルの差分表記と反対に (例:+の場合は削除、-は追加、は逆向き変更) になるように、tfファイルを修正する。

(7)

tfstateファイルと実インフラの差分が無くなったら完了である。

$ terraform plan -var-file=foo.tfvars

No changes. Infrastructure is up-to-date.

▼ さいごに

(8)

ローカルマシンのtfstateファイルをリモートバックエンドにアップロードし、上書きする。

# バックエンドがS3バケットの場合
$ aws s3 cp <ローカルマシンのパス> s3://<S3バケット名>/<tfstateファイルへのパス>

# バックエンドがGCSの場合
$ gsutil cp <ローカルマシンのパス> gs://<GCS名>/<tfstateファイルへのパス>
(8)

ローカルマシンのtfstateファイルを削除する。

$ rm terraform.tfstate
$ rm terraform.tfstate.backup


Tips

▼ importできないresourceタイプ

resourceブロック間の紐付けに特化したようなresourceブロックは、terraform importコマンドに対応していないものが多い (AWSであれば、aws_acm_certificate_validationaws_lb_target_group_attachmentなど) 。

その場合、tfstateファイルと実インフラの差分を解消できない。

ただし、こういった非対応のresourceブロックは、クラウドプロバイダーにはインフラリソースが存在しないTerraform特有のresourceブロックであることが多い。

そのため、実際にterraform applyコマンドを実行してみても、実インフラに影響が起こらない可能性がある。

▼ importを行わなかった場合のエラー

もしterraform importコマンドを行わないと、すでにクラウド上にインフラリソースが存在しているためにインフラリソースを作成できない、というエラーになってしまう。

(エラー例1)

Error: InvalidParameterException: Creation of service was not idempotent.

(エラー例2)

Error: error creating ECR repository: RepositoryAlreadyExistsException: The repository with name 'f' already exists in the registry with id '*****'


03. 実インフラの一部の設定値をtfstateファイルに取り込む

実インフラから実インフラの一部の設定値をtfstateファイルに取り込む場合、以下の方法が便利である。

(1)

先にコンソール画面に設定値を変更する。

(2)

terraform apply -refresh-onlyコマンドまたはterraform applyコマンドを実行する。

(3)

実インフラは変更されず、tfstateファイルに状態が書き込まれる。