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
こちらもおすすめ
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28

AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16

