![[Terraform リファクタリング]リソース内で定義していた設定を別リソースに移す](https://devio2023-media.developers.io/wp-content/uploads/2019/05/terraform-eyecatch.png)
[Terraform リファクタリング]リソース内で定義していた設定を別リソースに移す
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Terraformを使っていると、実リソースへの影響なくリソース内で定義した設定を別リソースに移したくなることが有ると思います。
例: リソースaws_instance内でebs_block_deviceを定義していたが、EBSの定義はリソースaws_ebs_volumeに移したい等
以下の手順で実現可能です。
- リファクタリングする
- 新規に記述を追加したリソースをインポートする
本ブログでは、サンプルコードを交えつつ上記の作業をやってみます。
やってみた
Provider Updateの対応手順ですが、公式ドキュメントには以下の手順がありました。
リソースaws_s3_bucket内で定義したaclをリソースaws_s3_bucket_aclにリファクタリングするという内容です。
S3をリファクタリングしたい場合は、上記を参考にしてください。
全く同じでは面白くないので、今回はaws_instance内のebs_block_deviceをリソースaws_ebs_volumeに移す作業を例にやっていきます。
修正前のコード
EC2インスタンスを定義しています。
resource "aws_instance" "this" {
ami = data.aws_ami.this.id
instance_type = "t4g.nano"
key_name = "sato-test"
root_block_device {
volume_type = "gp3"
volume_size = "10"
}
ebs_block_device {
device_name = "/dev/sdf"
volume_type = "gp3"
volume_size = "20"
}
tags = {
Name = "refactor-test"
}
}
ハイライト箇所を置き換えます。
コードを修正する
ebs_block_deviceの記述を削除して、リソースaws_ebs_volumeとaws_volume_attachmentを追加しました。
resource "aws_instance" "this" {
ami = data.aws_ami.this.id
availability_zone = "ap-northeast-1a"
instance_type = "t4g.nano"
key_name = "sato-test"
root_block_device {
volume_type = "gp3"
volume_size = "10"
}
- ebs_block_device {
- device_name = "/dev/sdf"
- volume_type = "gp3"
- volume_size = "20"
- }
tags = {
Name = "refactor-test"
}
}
+resource "aws_ebs_volume" "this" {
+ availability_zone = "ap-northeast-1a"
+ size = "20"
+ type = "gp3"
+}
+resource "aws_volume_attachment" "this" {
+ device_name = "/dev/sdf"
+ volume_id = aws_ebs_volume.this.id
+ instance_id = aws_instance.this.id
+}
planを実行してみます。
$ terraform plan
data.aws_ami.this: Reading...
data.aws_ami.this: Read complete after 0s [id=ami-055b7614d7fc0f105]
aws_instance.this: Refreshing state... [id=i-05aaeb83023a23766]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_ebs_volume.this will be created
+ resource "aws_ebs_volume" "this" {
+ arn = (known after apply)
+ availability_zone = "ap-northeast-1a"
+ encrypted = (known after apply)
+ final_snapshot = false
+ id = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ size = 20
+ snapshot_id = (known after apply)
+ tags_all = (known after apply)
+ throughput = (known after apply)
+ type = "gp3"
}
# aws_volume_attachment.this will be created
+ resource "aws_volume_attachment" "this" {
+ device_name = "/dev/sdf"
+ id = (known after apply)
+ instance_id = "i-05aaeb83023a23766"
+ volume_id = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
追加したリソースaws_ebs_volumeとaws_volume_attachmentが差分として表示されます。
Stateファイルを見てみると、resource aws_instance内でEBSボリュームの定義があります。
{
"mode": "managed",
"type": "aws_instance",
"name": "this",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
# 省略
"ebs_block_device": [
{
"delete_on_termination": true,
"device_name": "/dev/sdf",
"encrypted": false,
"iops": 3000,
"kms_key_id": "",
"snapshot_id": "",
"tags": {},
"tags_all": {},
"throughput": 125,
"volume_id": "vol-XXXXXX",
"volume_size": 20,
"volume_type": "gp3"
}
],
今回の変更内容は、resourceaws_ebs_volumeとaws_volume_attachmentを追加します。
Terraformは追加したリソースが既存のリソースであることを知らないため、差分が表示されます。
この状態でapplyすると、EBSボリュームを新規に追加してアタッチしようします。
しかし、/dev/sdfには既存のEBSボリュームがアタッチされているので、エラーになります。
エラーや実リソースへの影響なくリファクタリングするには、追加した記述が既存リソースのものであることを、Terraformに教える必要があります。
Terraformはtfstateで状態を管理するため、tfstateに記述を追加します。
importを行うことで、既存リソースをtfstateに取り込むことができます。
新規に記述を追加したリソースをインポートする
インポート対象は、先程のPlanで差分が出たaws_ebs_volumeとaws_volume_attachmentです。
インポートブロック又は、インポートコマンドでインポートを行います。(どちらか1つでOK)
インポートコマンド
$ terraform import aws_ebs_volume.this vol-XXXXXX # ボリュームID $ terraform import aws_volume_attachment.this /dev/sdf:vol-XXXXXX:i-YYYYYYY # デバイス名:ボリュームID:インスタンスID
インポートブロック
import {
to = aws_ebs_volume.this
id = "vol-XXXXXX" # ボリュームID
}
import {
to = aws_volume_attachment.this
id = "/dev/sdf:vol-XXXXXX:i-YYYYYYY" # デバイス名:ボリュームID:インスタンスID
}
- aws_ebs_volume | Resources | hashicorp/aws | Terraform | Terraform Registry
- aws_volume_attachment | Resources | hashicorp/aws | Terraform | Terraform Registry
tfstateを確認すると、aws_ebs_volumeとaws_volume_attachmentが追加されたことがわかります。
{
"mode": "managed",
"type": "aws_ebs_volume",
"name": "this",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
# 省略
{
"mode": "managed",
"type": "aws_volume_attachment",
"name": "this",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
# 省略
}
Planで差分がでないことを確認
再度Planを実行します。
% terraform plan aws_ebs_volume.this: Refreshing state... [id=vol-XXXXXX] data.aws_ami.this: Reading... data.aws_ami.this: Read complete after 0s [id=ami-055b7614d7fc0f105] aws_instance.this: Refreshing state... [id=i-YYYYYYY] aws_volume_attachment.this: Refreshing state... [id=vai-XXXXXX] 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.
No changes.になっており、差分がでていません。実リソースへの影響なく、リファクタリングができました。
おわりに
リソース内で定義していた設定を別リソースに移す作業についてでした。
破壊的な変更を含むAWS Providerのバージョンアップや、Terraformの記述を統一したいときなどに使える方法です。
リファクタリングの参考になれば幸いです。
以上、AWS事業本部の佐藤(@chari7311)でした。







