そうだ...Terraform importしてみよう...
記事の内容
本記事では、昨今のマルチクラウド化に伴って、より複雑化しているクラウド環境の救世主(Terraform)が提供しているTerraform import機能について紹介したいと思います。
既存のインフラ環境をInfrastructure as Code (IaC) したい方への一助になれば幸いです。
Terraform import とは?
Terraform importとは、既存のインフラ環境に作成されているリソースをTerraformで管理するために利用する機能となります。現在、Terraform importには2つの手法が存在しています。
importコマンド と import宣言の違い
importコマンドを利用する場合、terraform import
コマンドを実行することでstateファイルにリソースの状態をインポートすることができます。ただし、実際にTerraformで管理するためには、stateファイルの内容を適宜読み解いてHCLを手動で記載する必要があります。
import宣言を利用する場合、HCLを手動で記載してterraform plan
コマンドを実行することでリソースの状態をインポートすることができます。この時、terraform plan
コマンドの-generate-config-out
オプションを利用することで、HCLを自動で記載することができます。
要は、importコマンドでインポートすると意外と手間が掛かるなぁ...。
その点import宣言ってすげぇよな、最後までチョコたっぷりだもん。
っということです。
検証
事前準備
IAMユーザをインポート対象のリソースとして作成しました。
IAMユーザの情報は以下の通りとなります。
$ aws iam get-user --user-name test-user
# {
# "User": {
# "Path": "/",
# "UserName": "test-user",
# "UserId": "{UserId}",
# "Arn": "arn:aws:iam::{AccountId}:user/test-user",
# "CreateDate": "2024-01-22T15:13:22+00:00",
# "Tags": [
# {
# "Key": "Environment",
# "Value": "Dev"
# }
# ]
# }
# }
importコマンドの検証
importコマンドを検証する際のフォルダ構成は、以下の通りとなります。
import.tf
provider.tf
importコマンドを利用する場合、インポートしたリソースの状態を関連付けるためのresourceブロックを事前に定義しておく必要がありますので、以下の通りimport.tf
にresourceブロックを定義しました。
resource "aws_iam_user" "test" {
name = "test-user"
}
terraform init
terraform init
コマンド実行後のフォルダ構成は、以下の通りとなります。
.terraform/
.terraform.lock.hcl
import.tf
provider.tf
terraform import
terraform import
コマンドを実行して、stateファイルが作成されていることを確認しました。
terraform import
コマンド実行後のフォルダ構成は、以下の通りとなります。
.terraform/
.terraform.lock.hcl
import.tf
provider.tf
terraform.tfstate
コマンドを実行する際は、resourceブロックの<リソースの種類>.<リソースブロックの名前> および リソース名の情報を引数として渡してあげる必要があります。具体的なコマンドについては、公式ドキュメント(AWS Provider)に記載されているので確認してみてください。
本検証では以下の通りコマンドを実行しています。
$ terraform import aws_iam_user.test test-user
# aws_iam_user.test: Importing from ID "test-user"...
# aws_iam_user.test: Import prepared!
# Prepared aws_iam_user for import
# aws_iam_user.test: Refreshing state... [id=test-user]
#
# 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.
stateファイルの内容とimport.tf
に定義されているresourceブロックの内容を比較したところ、IAMユーザに紐づいているTAG情報部分に差分が発生していることがわかりました。この状態でterraform plan
コマンドを実行すると差分が発生してしまうため、resourceブロックにTAG情報を追記してあげる必要があります。本検証では、以下の通りresourceブロックにTAG情報を追記しました。
resource "aws_iam_user" "test" {
name = "test-user"
+ tags = {
+ Environment = "Dev"
+ }
}
terraform plan
全ての準備が整ったため、terraform plan
コマンドを実行しました。
実行結果から、特に差分が発生することなくリソースがインポートできていることが確認できました👏
$ terraform plan
# aws_iam_user.test: Refreshing state... [id=test-user]
#
# No changes. Your infrastructure matches the configuration.
#
# Terraform has compared your real infrastructure against your configuration and found no differences,
# so no changes are needed.
import宣言の検証
import宣言を検証する際のフォルダ構成は、以下の通りとなります。
import.tf
provider.tf
import宣言を利用する場合、importブロックを事前に定義しておく必要がありますので、以下の通りimport.tf
にimportブロックを定義しました。
import {
to = aws_iam_user.test
id = "test-user"
}
terraform init
terraform init
コマンド実行後のフォルダ構成は、以下の通りとなります。
.terraform/
.terraform.lock.hcl
import.tf
provider.tf
terraform plan
terraform plan
コマンドを実行して、generated.tf
が作成されていることを確認しました。
terraform plan
コマンド実行後のフォルダ構成は、以下の通りとなります。
.terraform/
.terraform.lock.hcl
generated.tf
import.tf
provider.tf
本検証ではresourceブロックを記載していないためコマンドを実行する際は、-generate-config-out
オプションをつけて実行する必要があります。詳細については公式ドキュメント(ImportのGenerating configuration)に記載されているので確認してみてください。
本検証では以下の通りコマンドを実行しています。
$ terraform plan -generate-config-out=generated.tf
# aws_iam_user.test: Preparing import... [id=test-user]
# aws_iam_user.test: Refreshing state... [id=test-user]
#
# Terraform will perform the following actions:
#
# # aws_iam_user.test will be imported
# # (config will be generated)
# resource "aws_iam_user" "test" {
# arn = "arn:aws:iam::{AccountId}:user/test-user"
# id = "test-user"
# name = "test-user"
# path = "/"
# tags = {
# "Environment" = "Dev"
# }
# unique_id = "UniqueId"
# }
#
# Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
# ╷
# │ Warning: Config generation is experimental
# │
# │ Generating configuration during import is currently experimental,
# │ and the generated configuration format may change in future versions.
# ╵
terraform apply
terraform apply
コマンドを実行して、stateファイルが作成されていることを確認しました。
terraform apply
コマンド実行後のフォルダ構成は、以下の通りとなります。
.terraform/
.terraform.lock.hcl
generated.tf
import.tf
provider.tf
terraform.tfstate
実行結果から、特に差分が発生することなくリソースがインポートできていることが確認できました👏
$ terraform apply
# aws_iam_user.test: Preparing import... [id=test-user]
# aws_iam_user.test: Refreshing state... [id=test-user]
#
# Terraform will perform the following actions:
#
# # aws_iam_user.test will be imported
# resource "aws_iam_user" "test" {
# arn = "arn:aws:iam::{AccountId}:user/test-user"
# id = "test-user"
# name = "test-user"
# path = "/"
# tags = {
# "Environment" = "Dev"
# }
# unique_id = "{UniqueId}"
# }
#
# Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
#
# Do you want to perform these actions?
# Terraform will perform the actions described above.
# Only 'yes' will be accepted to approve.
#
# Enter a value: yes
#
# aws_iam_user.test: Importing... [id=test-user]
# aws_iam_user.test: Import complete [id=test-user]
#
# Apply complete! Resources: 1 imported, 0 added, 0 changed, 0 destroyed.
まとめ
本検証では、importコマンドを利用する場合 と import宣言を利用する場合の手順の違いについて確認しました。importコマンドを利用する場合は、stateファイルの確認やresourceブロックを手動で追記する手順が発生するのでimport宣言よりも少し面倒だなと感じました。ただし、for_each構文でリソースをインポートしたい場合などはimportコマンドを利用する方がわかりやすいように感じます。
import宣言の場合は、resourceブロックを記載しなくてもterraform plan
を実行する際に-generate-config-out
オプションを利用することで自動的にresourceブロックを定義してくれるため便利だと思う反面、当該オプションはまだ実験段階であるということを念頭に置いておく必要があると思います。
取捨選択ってことで、、、話のオチになりますかね(ならない)
Discussion