Terraform 1.5 で実現する config-driven なインポート作業
Posted on
Terraform 1.5 で追加された import
ブロックと -generate-config-out
フラグを組み合わせると、既存のパイプラインの中で安全にインポート作業が行えるようになります。 Hashicorp ではこのことを "config-driven import" と呼んでいます。
これまでは terraform import
コマンドでリソースを 1 つずつインポートし、それを定義する HCL も自分で書く必要がありました。コマンドを実行すると即座に tfstate に反映されるので、HCL がマージされるまでの間にほかの人が誤ってリソースを削除してしまう危険性がありました。そのため、チームで共同作業するリポジトリでは気軽にインポートできませんでした。
Terraform 1.5 はこれらの問題をスマートに解決してくれます。
既存のパイプラインでインポートするステップ
チームで共同作業する場合、すでに何らかのパイプラインが存在すると思います。その中で config-driven なインポート作業を行うには、次のようなステップになると思います。
- インポートしたいリソースを
import
ブロックに定義する - ローカルで
-generate-config-out
フラグを付けてterraform plan
を実行する - 生成された HCL をチェックして、不要な引数を削除したり、ベタ書きの値を直す
- ローカルで
terraform plan
を実行して差分がないことを確認する - HCL をコミットして Pull request を作成し、チームメンバーにレビューしてもらう
- レビューが通ったらマージして、パイプラインから
terraform apply
を実行してインポートする - (オプション)不要になった
import
ブロックを削除する
自分の理解を深めるために、Terraform Cloud で実際にインポートしてみました。
インポートの検証に使うリソースを作成する
今回は AWS CLI で IAM user を作成して、それを上記のステップでインポートしてみます。
まず IAM policy を作成します。
$ cat test-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::my-bucket/*"
]
}
]
}
$ aws iam create-policy --policy-name test-policy --policy-document file://test-policy.json
次に IAM user を作って先ほどの policy をアタッチします。
$ aws iam create-user --user-name test-user
$ aws iam attach-user-policy --policy-arn arn:aws:iam::xxxxxxxxxxxx:policy/test-policy --user-name test-user
-generate-config-out フラグを付けて HCL を生成する
既存のリポジトリの中に import.tf
を用意します。 id
で指定する値は terraform import
で指定するものと同じです。コマンドでインポートするのと違い、複数のリソースをまとめてインポートできるのは楽ですね。
import {
id = "test-user"
to = aws_iam_user.test_user
}
import {
id = "arn:aws:iam::xxxxxxxxxxxx:policy/test-policy"
to = aws_iam_policy.test_policy
}
import {
id = "test-user/arn:aws:iam::xxxxxxxxxxxx:policy/test-policy"
to = aws_iam_user_policy_attachment.test_policy
}
-generate-config-out
フラグを付けて terraform plan
を実行します。
$ terraform version
Terraform v1.5.0
on linux_arm64
$ terraform init
$ terraform plan -generate-config-out=test_user.tf
(snip)
Plan: 3 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 has generated configuration and written it to test_user.tf. Please review the configuration and edit it as necessary before adding it to version control.
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
test_user.tf
を確認すると次のような HCL が生成されていました。
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform from "test-user/arn:aws:iam::xxxxxxxxxxxx:policy/test-policy"
resource "aws_iam_user_policy_attachment" "test_policy" {
policy_arn = "arn:aws:iam::xxxxxxxxxxxx:policy/test-policy"
user = "test-user"
}
# __generated__ by Terraform from "test-user"
resource "aws_iam_user" "test_user" {
force_destroy = null
name = "test-user"
path = "/"
permissions_boundary = null
tags = {}
tags_all = {}
}
# __generated__ by Terraform from "arn:aws:iam::xxxxxxxxxxxx:policy/test-policy"
resource "aws_iam_policy" "test_policy" {
description = null
name = "test-policy"
name_prefix = null
path = "/"
policy = "{\"Statement\":[{\"Action\":[\"s3:Get*\",\"s3:List*\"],\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:s3:::my-bucket/*\"]}],\"Version\":\"2012-10-17\"}"
tags = {}
tags_all = {}
}
このままでも間違いはありませんが、不要な引数やベタ書きされた値、ワンライナーで書かれた policy などを直します。
resource "aws_iam_user" "test_user" {
name = "test-user"
path = "/"
}
data "aws_iam_policy_document" "test_policy" {
statement {
actions = [
"s3:Get*",
"s3:List*",
]
resources = ["arn:aws:s3:::my-bucket/*"]
}
}
resource "aws_iam_policy" "test_policy" {
name = "test-policy"
path = "/"
policy = data.aws_iam_policy_document.test_policy.json
}
resource "aws_iam_user_policy_attachment" "test_policy" {
policy_arn = aws_iam_policy.test_policy.arn
user = aws_iam_user.test_user.name
}
再度 terraform plan
を実行して差分がないことを確認します。
パイプラインからインポートを実行する
HCL をコミットし Pull request を作成します。 Terraform Cloud の場合はインポート対象のリソースが分かりやすく表示されます。
あとは通常のレビュープロセスを経てマージし、パイプラインから terraform apply
を実行すればインポート作業は完了です。 import
ブロックはもう不要なので削除できます。
おわりに
リソースのインポートはさまざまな OSS が存在していますが、個人的にはどれも決定打に欠けるものでした。そんな中 Terraform 1.5 が示したソリューションは、ユーザーのペインをよく理解しているなと感じました。
これで手動で作られた AWS リソースを効率よくインポートしていけそうです!