Terraform import ブロックの実活用
こんにちは、tkgです。
この記事はNHN テコラス Advent Calendar 2023の16日目の記事です。
今更ではありますが、Terraform 1.5で実装された importブロックについて触ってみましたので備忘録的にまとめたいと思います。
公式リファレンスには AWSのEC2インスタンスが例として記述されていますが、実際の環境をimportブロックを利用してどうコード化していくかという視点で記載していきます。
元々どうやって既存リソースのコード化(import)をしていたか
terraform import
コマンド自体はかなり前から用意されていましたので、私はこちらを利用してStateとコード、実際のリソースとの差分を埋めるような形にしていました。
各リソースをtfファイルに記述の上でコマンドを実行、terraform plan
で差分を埋めるような作業で、コード自体はTerraformerやFormer2などサードパーティ製品で生成または手作業で準備するなどする必要がありました。
例 EC2の場合 1. 別途生成したリソース設定を記述 または 下記のようなコードをtfファイルに書く resource "aws_instance" "this" {} 2. terraform importコマンドを実行 terraform import aws_instance.this i-xxxxxxxxxxx 3. terraform planを実行、差分の記述をする 生成したコードの場合差分を埋める、直接記述した場合記述を一旦消してからplanを実行、差分出力から必要な項目を記述する
サードパーティ製品の出力ではコードのプロバイダバージョンの差分が存在することがあったり、terraform plan
の結果から差分を元に記述するなど、リソースからコードを起こすのはまあまあ大変だった印象です。
importブロックの動作について
公式リファレンス通りにはなりますが、基本的に下記のような構文でimportを行っていきます。
import { to = aws_instance.example id = "i-xxxxxxxxxxxx" }
こちらを記述した上で、対象となるリソースを記述していきます。
公式リファレンスに変更される可能性が示唆されているものにはなりますが、リソース自体を生成するオプションが用意されていますので、そちらを実行することで実環境からのコード生成が可能です。
terraform plan -generate-config-out=hogehoge.tf
importブロックとresourceが存在している状態で terraform apply
を実施することでstateに反映され、コード管理が実現できるという仕組みです。
実際にインポートしてみる
それでは実際の環境をコード化してみたいと思います。
今回コード化を行う環境は下記構成図の赤枠で囲ったものです。
準備するもの
対象のAWSアカウントに対してTerraformが実行できるプロバイダ設定はあらかじめ行っておく必要があります。
今回は下記記事でご紹介していた際のprovider設定とほぼ同一のものを準備しました。(宣伝)
Terraform利用時のアクセスキー管理から脱却!assume_roleを活用して複数環境の管理を楽にする方法
locals { aws_id = [展開先のAWSID] region = "ap-northeast-1" } terraform { backend "s3" { bucket = "[バケット名]" dynamodb_table = "[DynamoDBのテーブル名]" key = "[stateを置きたいディレクトリ]/terraform.tfstate" region = "ap-northeast-1" } required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } variable "role" { type = string default = "default" description = "select iam role" } provider "aws" { region = "${local.region}" assume_role { role_arn = "arn:aws:iam::${local.aws_id}:role/Techorus-terraform-${var.role}-role" session_name = "terraform" external_id = "[iamで指定しているexternal_id]" } }
上記コードのみの状態でterraform init
および terraform plan
(No changes) が実行できることを確認します。
実際にimportを実施する
準備ができたら、実際にimportブロックを作成していきます。今回は import.tfとしてファイルを作成、記述しました。
idに指定するパラメーターは各リソースによって異なるので、取り込みたい対象のリソースのリファレンスの下部 importについての記述を参考に記載します。
今回は EC2、RDS、ALBをコード化していきます。
import { to = aws_instance.web_1 id = "i-xxxxxxxxxxxxxxx" } import { to = aws_db_instance.rds_1 id = "tkg-test-rds" } import { to = aws_lb.front_alb id = "arn:aws:elasticloadbalancing:ap-northeast-1:[展開先のAWSID]:loadbalancer/app/tkg-test-alb/ee4fab7b80076f59" } import { to = aws_lb_target_group.alb_target id = "arn:aws:elasticloadbalancing:ap-northeast-1:[展開先のAWSID]:targetgroup/tkg-test-tg/80768af5767da182" } import { to = aws_lb_listener.alb_listener id = "arn:aws:elasticloadbalancing:ap-northeast-1:[展開先のAWSID]:listener/app/tkg-test-alb/ee4fab7b80076f59/e00543683ea454e1" }
上記import.tfを作成して、 terraform plan -generate-config-out=generated.tf
を実施します。
すると生成機能は実験段階である旨と今後変更される可能性がある警告とエラーが出力されました。
Plan: 2 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. ╵ ╷ │ Error: expected default_action.0.order to be in the range (1 - 50000), got 0 │ │ with aws_lb_listener.alb_listener, │ on generated.tf line 10: │ (source code not available) │ ╵ ╷ │ Error: Conflicting configuration arguments │ │ with aws_instance.web_1, │ on generated.tf line 14: │ (source code not available) │ │ "ipv6_address_count": conflicts with ipv6_addresses ╵ ╷ │ Error: Conflicting configuration arguments │ │ with aws_instance.web_1, │ on generated.tf line 15: │ (source code not available) │ │ "ipv6_addresses": conflicts with ipv6_address_count ╵ ╷ │ Error: Missing required argument │ │ with aws_lb_target_group.alb_target, │ on generated.tf line 37: │ (source code not available) │ │ The argument "target_failover.0.on_deregistration" is required, but no definition was found. ╵ ╷ │ Error: Missing required argument │ │ with aws_lb_target_group.alb_target, │ on generated.tf line 38: │ (source code not available) │ │ The argument "target_failover.0.on_unhealthy" is required, but no definition was found. ╵ ╷ │ Error: Missing required argument │ │ with aws_lb_target_group.alb_target, │ on generated.tf line 41: │ (source code not available) │ │ The argument "target_health_state.0.enable_unhealthy_connection_termination" is required, but no definition was found. ╵
エラーが表示されておりますが、 generated.tf 自体は生成されているようです。
内容的から排他パラメーターのコンフリクト等が発生しているようなので、エラー箇所の修正と生成されたコードから “null” や “{}” など初期値であるものが指定されているコードを削除して下記のようなコードで再度 terraform plan を実行してみます。
# __generated__ by Terraform # Please review these resources and move them into your main configuration files. # __generated__ by Terraform resource "aws_lb_listener" "alb_listener" { alpn_policy = null certificate_arn = "arn:aws:acm:ap-northeast-1:[展開先のAWSID]:certificate/9041b977-626a-47a6-ac8c-450fa9913a8d" load_balancer_arn = "arn:aws:elasticloadbalancing:ap-northeast-1:[展開先のAWSID]:loadbalancer/app/tkg-test-alb/ee4fab7b80076f59" port = 443 protocol = "HTTPS" ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" default_action { target_group_arn = "arn:aws:elasticloadbalancing:ap-northeast-1:[展開先のAWSID]:targetgroup/tkg-test-tg/80768af5767da182" type = "forward" } mutual_authentication { ignore_client_certificate_expiry = false mode = "off" } } # __generated__ by Terraform resource "aws_lb_target_group" "alb_target" { connection_termination = null deregistration_delay = "300" ip_address_type = "ipv4" load_balancing_algorithm_type = "round_robin" load_balancing_cross_zone_enabled = "use_load_balancer_configuration" name = "tkg-test-tg" port = 80 protocol = "HTTP" protocol_version = "HTTP2" slow_start = 0 target_type = "instance" vpc_id = "vpc-08c54ee99a8e9e6e4" health_check { enabled = true healthy_threshold = 5 interval = 30 matcher = "200" path = "/" port = "traffic-port" protocol = "HTTP" timeout = 5 unhealthy_threshold = 2 } stickiness { cookie_duration = 86400 enabled = false type = "lb_cookie" } } # __generated__ by Terraform from "arn:aws:elasticloadbalancing:ap-northeast-1:[展開先のAWSID]:loadbalancer/app/tkg-test-alb/ee4fab7b80076f59" resource "aws_lb" "front_alb" { desync_mitigation_mode = "defensive" drop_invalid_header_fields = false enable_cross_zone_load_balancing = true enable_deletion_protection = true enable_http2 = true enable_tls_version_and_cipher_suite_headers = false enable_waf_fail_open = false enable_xff_client_port = false idle_timeout = 60 internal = false ip_address_type = "ipv4" load_balancer_type = "application" name = "tkg-test-alb" preserve_host_header = false security_groups = ["sg-0bc2af0d193d9a169"] subnets = ["subnet-01ab151723768f96c", "subnet-03c722ee50f9b4137"] xff_header_processing_mode = "append" access_logs { bucket = "tkg-test-dev-alb-web-accesslog" enabled = true } subnet_mapping { subnet_id = "subnet-01ab151723768f96c" } subnet_mapping { subnet_id = "subnet-03c722ee50f9b4137" } } # __generated__ by Terraform from "tkg-test-rds" resource "aws_db_instance" "rds_1" { allocated_storage = 20 auto_minor_version_upgrade = true availability_zone = "ap-northeast-1a" backup_retention_period = 0 backup_target = "region" backup_window = "03:00-06:00" ca_cert_identifier = "rds-ca-2019" copy_tags_to_snapshot = false customer_owned_ip_enabled = false db_name = "techorus" db_subnet_group_name = "tkg-test-vpc" delete_automated_backups = true deletion_protection = false engine = "mysql" engine_version = "8.0.34" iam_database_authentication_enabled = false identifier = "tkg-test-rds" instance_class = "db.t4g.micro" iops = 3000 kms_key_id = "arn:aws:kms:ap-northeast-1:[展開先のAWSID]:key/a85fac58-665d-479d-9e29-43723646bdd5" license_model = "general-public-license" maintenance_window = "mon:00:00-mon:03:00" max_allocated_storage = 100 monitoring_interval = 0 multi_az = false network_type = "IPV4" option_group_name = "default:mysql-8-0" parameter_group_name = "default.mysql8.0" performance_insights_enabled = false performance_insights_retention_period = 0 port = 3306 publicly_accessible = false skip_final_snapshot = true storage_encrypted = true storage_throughput = 125 storage_type = "gp3" username = "techorus_test" vpc_security_group_ids = ["sg-02e2685fe9d622880"] } # __generated__ by Terraform resource "aws_instance" "web_1" { ami = "ami-012261b9035f8f938" associate_public_ip_address = true availability_zone = "ap-northeast-1a" disable_api_stop = false disable_api_termination = false ebs_optimized = false get_password_data = false hibernation = false iam_instance_profile = "tkg-test_cloudwatch-ssm" instance_initiated_shutdown_behavior = "stop" instance_type = "t3.nano" monitoring = false placement_partition_number = 0 private_ip = "10.0.100.79" source_dest_check = true subnet_id = "subnet-02a7819a2e4f546d1" tags = { Name = "tkg-test-ec2-al2-1" backup = "true" } tags_all = { Name = "tkg-test-ec2-al2-1" backup = "true" } tenancy = "default" vpc_security_group_ids = ["sg-0b9bc9b1f09387a70", "sg-0e5c49aeb001258da"] capacity_reservation_specification { capacity_reservation_preference = "open" } cpu_options { amd_sev_snp = null core_count = 1 threads_per_core = 2 } credit_specification { cpu_credits = "standard" } enclave_options { enabled = false } maintenance_options { auto_recovery = "default" } metadata_options { http_endpoint = "enabled" http_protocol_ipv6 = "disabled" http_put_response_hop_limit = 1 http_tokens = "optional" instance_metadata_tags = "disabled" } private_dns_name_options { enable_resource_name_dns_a_record = false enable_resource_name_dns_aaaa_record = false hostname_type = "ip-name" } root_block_device { delete_on_termination = true encrypted = true iops = 3000 kms_key_id = "arn:aws:kms:ap-northeast-1:[展開先のAWSID]:key/6b70a3f3-a980-46b5-b5f1-674142a2b2e6" tags = { Name = "tkg-test-ec2-al2-1" } throughput = 125 volume_size = 50 volume_type = "gp3" } }
上記コードでterraform plan
を実行したところ、下記出力の通りエラー無くimportが行われるようになりました。
こちらで terraform apply
を実施するとリソースがtfstateに反映されimportが完了となります。
利用したimportブロックはリソース重複のエラーが以後のplan/applyで発生してしまうため、コメントアウトまたは削除を行います。
$ terraform plan ~中略~ Plan: 5 to import, 0 to add, 0 to change, 0 to destroy. $ terraform apply ~中略~ Apply complete! Resources: 5 imported, 0 added, 0 changed, 0 destroyed.
まとめ
コード生成時に出力された警告のとおり、まだ完成された仕組みではないようでしたが、サードパーティ製品を利用せずにコードの生成が可能な点がかなり魅力的な機能だなと思いました。
APIで取得できる値が概ねすべて出力されるようで、リソースによっては手動でのカスタマイズが必要になってくる状況のようです。
また、import不可なリソースも存在しますので、そこは別途準備する必要はあります。(今回の場合、aws_lb_target_group_attachment
は別途足してあげる必要があります)
ですがその上記の様な作業だけで一旦のコード化が可能で、AWSでいうリホストように、ここからカスタマイズ、管理しやすい形になおして行く足がかりになるかなと思います。
ただし、各リソースブロックがどういう動作をしているものか、AWS APIの機能や動作仕様との対比のような Terraformや各Providerへの理解はかなり必要である機能ではあるかなと思います。
テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!
Follow @twitter2016年入社のインフラエンジニアです。 写真が趣味。防湿庫からはレンズが生え、押入れには機材が生えます。 2D/3DCG方面に触れていた時期もありました。
Recommends
こちらもおすすめ
-
【ハンズオン】Amazon EKSをTerraformで構築してみる
2022.10.25
-
AWS CodePipelineでTerraformパイプラインを実装する
2024.3.28
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28
AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16