terraform管理外のAWSリソースを簡単にimportしたい
目的
- 手動AWSリソースをTerraform管理下に置く場合の方法を整理したい。
- 可能な限り無駄な作業は減らしたい。(importコマンドを何回も打つような修行をしたくない、、)
事前調査
(軽くググってみた程度。最新情報だとまた異なる可能性あり。)
- 案1:terraformerを利用
- メリット
- AWSリソースを一括でHCLファイル化できそう。
- デメリット
- 対応するtfenvバージョンが古い可能性あり。
- terraform管理下におきたくないリソース(デフォルトVPC、IAMロール等)も管理下になる可能性あり。
- terraformer利用する際の学習コスト・制約が多そう(未利用なので実態はわからない)
- 参考
- メリット
- 案2:terraform importブロックを利用
- メリット
- tfファイル内にimportブロックを記述するのみでterraform import、かつHCLファイルを作成してくれそう
- terraform planのようにimport結果を事前に確認することができそう
- デメリット
- resource単位でimportブロックを記述しないといけないため、作業量が多そう
- importブロックで作成されるHCLファイルが既存コードと異なる可能性がありそう(ここは確認したい)
- 参考
- メリット
案2:terraform importブロックを利用
importブロックについて確認してみる。
もし活用できそうであれば作業量を削減できる仕組みがないか考えてみる。(chatGPTやgithub copilot使えば冗長作業を削減できそう)
terraform importブロックの検証
事前準備
- プライベートサブネットを1つ作成
ファイル構成
.
├── import.tf
├── main.tf
├── network.tf
検証
・import.tfにimportブロックを記載
import {
to = aws_subnet.private_1a
id = "subnet-xxxxx" #サブネットIDは実態に合わせる
}
・実行コマンド
terraform plan -generate-config-out=generated.tf
・実行結果
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform
resource "aws_subnet" "private_1a" {
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1d"
availability_zone_id = "apne1-az2"
cidr_block = "30.0.3.0/24"
customer_owned_ipv4_pool = null
enable_dns64 = false
# enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
ipv6_cidr_block = null
ipv6_native = false
map_customer_owned_ip_on_launch = false
map_public_ip_on_launch = false
outpost_arn = null
private_dns_hostname_type_on_launch = "ip-name"
tags = {}
tags_all = {}
vpc_id = "vpc-xxxxx"
}
・terraform plan実行
以下エラーが発生。自動生成した属性名で重複がある旨。
$ terraform plan
│ Error: Conflicting configuration arguments
│
│ with aws_subnet.private_1a,
│ on generated.tf line 7, in resource "aws_subnet" "private_1a":
│ 7: availability_zone = "ap-northeast-1d"
│
│ "availability_zone": conflicts with availability_zone_id
╵
╷
│ Error: Conflicting configuration arguments
│
│ with aws_subnet.private_1a,
│ on generated.tf line 8, in resource "aws_subnet" "private_1a":
│ 8: availability_zone_id = "apne1-az2"
│
│ "availability_zone_id": conflicts with availability_zone
╵
╷
│ Error: enable_lni_at_device_index must not be zero, got 0
│
│ with aws_subnet.private_1a,
│ on generated.tf line 12, in resource "aws_subnet" "private_1a":
│ 12: enable_lni_at_device_index = 0
│
╵
╷
│ Error: Missing required argument
│
│ with aws_subnet.private_1a,
│ on generated.tf line 17, in resource "aws_subnet" "private_1a":
│ 17: map_customer_owned_ip_on_launch = false
│
│ "map_customer_owned_ip_on_launch": all of `customer_owned_ipv4_pool,map_customer_owned_ip_on_launch,outpost_arn` must be
│ specified
以下のようにgenerated.tfの一部属性をコメントアウトするよう修正
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform
resource "aws_subnet" "private_1a" {
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1d"
# availability_zone_id = "apne1-az2"
cidr_block = "30.0.3.0/24"
customer_owned_ipv4_pool = null
enable_dns64 = false
# enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
ipv6_cidr_block = null
ipv6_native = false
# map_customer_owned_ip_on_launch = false
map_public_ip_on_launch = false
outpost_arn = null
private_dns_hostname_type_on_launch = "ip-name"
tags = {}
tags_all = {}
vpc_id = "vpc-xxxxx"
}
・terraform plan
Terraform will perform the following actions:
# aws_subnet.private_1a will be imported
resource "aws_subnet" "private_1a" {
arn = "xxxxx"
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1d"
availability_zone_id = "apne1-az2"
cidr_block = "30.0.3.0/24"
enable_dns64 = false
enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
id = "subnet-xxxxx"
ipv6_native = false
map_customer_owned_ip_on_launch = false
map_public_ip_on_launch = false
owner_id = "xxxxx"
private_dns_hostname_type_on_launch = "ip-name"
tags = {}
tags_all = {}
vpc_id = "vpc-xxxxx"
}
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
所感
- メリット
- importブロックを一度作成すればHCLファイルを自動生成してくれる点は便利
- 任意の属性も含めて全てHCLファイルに出力してくれるので意図しない設定になっていないか確認できる
- デメリット
- importブロック作成自体が手間
- importブロックを作成する際にAWSリソースのIDを指定しないといけないため、大量生成には不向き
- importブロックで指定するIDはAWSリソース毎に異なるので、毎回terraformドキュメントを参照する必要がある
- 出力するHCLファイル形式が期待する形式と異なる
- 全ての属性をHCLファイルに出力するため、他ファイルと記述粒度が異なる
- vpc_id等、他リソースを参照する箇所は手直しが必要
- importブロック作成自体が手間
用途に応じて使い分けするのが良さそう
- import対象が少ない場合
- 開発者側でterraformコードを記述し、importコマンドを逐一実行
- import対象が多い場合
- importブロックを量産し、HCLファイルをgenerate
- generateされたHCLファイルを修正(依存関係の整理、変数をvariable化)する
→ただ、HCLファイルを整形する手間を考えた際、両者でそこまで生産性は変わらないはず。
であれば既存のコーディング規約にそって新規にコーディングし、terraform importコマンドを実行した方が想定通りの結果となるし、デグレードのリスクも少ない気がする
そもそもterraform importが必要なユースケースについて整理したい
-
IaCリソース(terraform管理)
- ユースケース
- 既存コードをリファクタリングしたい場合(冗長なコードをfor_eachを用いて短縮する)
- 既存resourceをterraform管理外にした上で、terraform importする必要がある
- 手順
- リファクタリング該当箇所の抽出
- リファクタリング該当箇所を一時的にterraform管理外にする
- terraformコードをリファクタリング
- terraform importする
- ユースケース
-
手動リソース(terraform管理外)
- ユースケース
- 手動→IaC管理したい
- 手順
- 手動リソース箇所の抽出
- (手動リソースのうち)IaC化したい箇所の抽出
- terraformコードを記述
- terraform importする
- ユースケース
それぞれのパターンにおいて工数がかかる箇所を整理
-
IaCリソース(terraform管理)
- 手順
- リファクタリング該当箇所の抽出
- ★リファクタリング該当箇所を一時的にterraform管理外にする
- →terraform state rmコマンドの生成を効率化したい
- terraformコードをリファクタリング
- ★terraform importする
- →terraform importコマンドの生成を効率化したい
- 手順
-
手動リソース(terraform管理外)
- 手順
- ★手動リソース箇所の抽出
- →AWSアカウントのリソースを効率的に抽出したい
- (手動リソースのうち)IaC化したい箇所の抽出
- terraformコードを記述
- ★terraform importする
- →terraform importコマンドの生成を効率化したい
- ★手動リソース箇所の抽出
- 手順
各パターンにおいて対応策
-
terraform state rmコマンドの生成を効率化したい
- 案1
- 対象ファイルを読み込み自動でterraform state rmコマンドを生成するスクリプトを作成
- 案1
→リファクタリング箇所が膨大でないことを前提とし、上記方針で検討進めてみる
-
AWSアカウントのリソースを効率的に抽出したい
- 案1
- AWS CLI、SDKを用いたリソース抽出用のスクリプトを作成し、対象リソースを出力する
- →対象サービスが膨大であるためスクリプトを組む工数がかかりそう
- →スクリプト作成 vs 対象リソースを直接参照の工数で比較して妥当な方を採用
- 案2
- EC2、RDS等のリソース数が多い箇所に絞り、 AWS CLI、SDKを用いたリソース抽出用のスクリプトを作成
- →案1の妥協案。おそらくほとんどのAWS環境でリソース抽出する際はこの形式になる気がする。
- 案3
- AWSマネージドサービス使えないか?
- →Resource Manager?等のマネージドサービスで対象抽出できないか?
→案3で調査を進めてみて、無理なら案2で妥協する。
- 案1
terraform importコマンドの生成を効率化したい
色々と案を考えたが、効率化の弊害になっているのはAWSリソースIDを取得する箇所
terraform importコマンドを自動生成するのは可能ではあるが、AWSリソースIDを取得〜コマンド実行までが手間かかる
先人の知恵あり。ちょっと試してみる。
(メモ)
- 案1
- 対象ファイルを読み込み自動でterraform importコマンドを生成するスクリプトを作成
- →コマンド生成後にAWSのリソースIDを指定する必要がある。
- →実運用する場合、AWSリソースをまとめた管理台帳を作成し、生成したterraform importコマンドを貼り付ける運用をするしかない
対話形式でterraform importがスムーズにできた。人類が求めていた機能な気がする。