Terraformのimportブロックを使ってRoute53のレコードをインポートする
はじめに
AWSコンソールから手動でRoute53のレコードを作成した後に、Terraformコード管理にしたくなりましたので、 import
ブロックを使ったやり方を調査、検証してみました。
import
ブロックとは
Terraformにはv1.5.0から import
ブロックというものが実装されておりまして、Terraform以外で作成されたリソースをTerraform管理化に移行するための機能です。それより以前のバージョンではterraform import
コマンドを使ってStateを更新する必要がありましたが、 import
ブロックを使えば terraform import
コマンドの実行は不要になります。
また、v1.6.0からは import
ブロックの中で静的な値以外に、変数や他の値を参照する式が使えるようになったようで、より使いやすくなりました。
事前準備
検証のための事前準備としてAWSコンソールから直接、以下のように hoge.com
というホストゾーンと、TXTレコードを1つ作成しておきました。
作成したTXTレコード
key | value |
---|---|
レコード名 | _dmarc.hoge.com |
タイプ | TXT |
値 | "v=DMARC1;p=none" |
※ホストゾーン自体はTerraform管理になっていない前提で、ゾーンIDは Zxxxxxxxxxxxxxx
として進めます。今回インポートしたい対象はTXTレコードのみとします。
コードを書く
インポートしたいTXTレコードができたので、次はコードを書きます。Terraformのベストプラクティスにならって、以下のようなディレクトリ構造でファイルを配置しました。(dev環境という想定です。)
.
├── environments
│ └── dev
│ ├── import.tf //importブロックを記述
│ ├── main.tf //mail_senderモジュールを使用
│ ├── terraform.tf //terraform設定を記述
│ └── variables.tf //ルートモジュールに必要な入力値
└── modules
└── mail_sender //mail_senderモジュール
├── main.tf //Route53レコードのリソースを記述
└── variables.tf //mail_senderモジュールに必要な入力値
environments/dev
がルートモジュールであり、そこで mail_sender
モジュールを呼び出す構成です。今回のTXTレコードはメール送信者としての認証のための設定という想定であるため、このようなモジュール名にしています。
ファイルの中身は以下のように記述しました。
今回のポイントではないファイルたち
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40.0"
}
}
required_version = ">= 1.7.4"
}
variable "hostzone_name" {
type = string
default = "hoge.com"
}
variable "hostzone_id" {
type = string
default = "Zxxxxxxxxxxxxxx"
}
provider "aws" {
region = "ap-northeast-1"
}
module "mail_sender" {
source = "../../modules/mail_sender"
hostzone_name = var.hostzone_name
hostzone_id = var.hostzone_id
}
variable "hostzone_name" { type = string }
variable "hostzone_id" { type = string }
resource "aws_route53_record" "dmarc" {
zone_id = var.hostzone_id
name = "_dmarc.${var.hostzone_name}"
type = "TXT"
ttl = 300
records = ["v=DMARC1;p=none"]
}
↑↑↑
インポート先となる module.mail_sender.aws_route53_record.dmarc
を記述しています。この例では、インポートした内容と同じ設定になるように、あらかじめ値を指定しています。
import {
to = module.mail_sender.aws_route53_record.dmarc
id = "${var.hostzone_id}__dmarc.${var.hostzone_name}_TXT"
}
↑↑↑
import
ブロックを記述できる場所はルートモジュールのみだそうで、子モジュールに書くとエラーになります。そのため import
ブロックの to
属性で、インポート先のモジュールを指定する形にしました。
terraform plan
を確認する
コードが書き終わったら terraform plan
を実行してみます。
インポート後の差分がないパターン
以下は terraform plan
の結果を抜粋したものになります。
$ terraform plan
...(省略)
Terraform will perform the following actions:
# module.mail_sender.aws_route53_record.dmarc will be imported
resource "aws_route53_record" "dmarc" {
fqdn = "_dmarc.hoge.com"
id = "Zxxxxxxxxxxxxxx__dmarc.hoge.com_TXT"
multivalue_answer_routing_policy = false
name = "_dmarc.hoge.com"
records = [
"v=DMARC1;p=none",
]
ttl = 300
type = "TXT"
zone_id = "Zxxxxxxxxxxxxxx"
}
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
このパターンではインポートしたリソースと、記述したmodule.mail_sender.aws_route53_record.dmarc
が同じ内容であったため 1 to import
だけになりました。
インポート後の差分があるパターン
module.mail_sender.aws_route53_record.dmarc
の設定を以下のように変更してみます。
さきほど p=none
と記述していた値の一部を p=reject
に変更しています。
resource "aws_route53_record" "dmarc" {
zone_id = var.hostzone_id
name = "_dmarc.${var.hostzone_name}"
type = "TXT"
ttl = 300
records = ["v=DMARC1;p=reject"]
}
以下は terraform plan
の結果を抜粋したものになります。
$ terraform plan
...(省略)
Terraform will perform the following actions:
# module.mail_sender.aws_route53_record.dmarc will be updated in-place
# (imported from "Zxxxxxxxxxxxxxx__dmarc.hoge.com_TXT")
~ resource "aws_route53_record" "dmarc" {
fqdn = "_dmarc.hoge.com"
id = "Zxxxxxxxxxxxxxx__dmarc.hoge.com_TXT"
multivalue_answer_routing_policy = false
name = "_dmarc.hoge.com"
~ records = [
- "v=DMARC1;p=none",
+ "v=DMARC1;p=reject",
]
ttl = 300
type = "TXT"
zone_id = "Zxxxxxxxxxxxxxx"
}
Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.
このパターンでは、インポートもして、その後の差分も表示してくれています。
1 to import
と 1 to change
という結果になりました。
terraform plan
の結果を元にコードを修正
今回はあえて差分を出してみましたが、本来であれば差分がでなくなるまでコードを修正していく流れになると思います。 terraform plan
の結果を見れば、コードをどのように修正すればいいかわかり、差分がでなくなれば、 terraform apply
できる合図になりますね。
terraform apply
コードが完成したら、 terraform apply
を実行すれば完了です。
Terraform Cloudでの実行
今回私はTerraform Cloudを使って terraform apply
を実行しましたので結果はスクショを貼りますね。またCLIによる実行結果の見え方の違いとして terraform plan
のスクショも貼っておきます。(ゾーンIDはマスクしています)
Stateの確認
以下は terraform apply
後のStateの抜粋です。
{
"version": 4,
"terraform_version": "1.7.4",
...(省略)
"resources": [
{
"module": "module.mail_sender",
"mode": "managed",
"type": "aws_route53_record",
"name": "dmarc",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 2,
"attributes": {
"fqdn": "_dmarc.hoge.com",
"name": "_dmarc.hoge.com",
"records": ["v=DMARC1;p=reject"],
"type": "TXT",
"ttl": 300,
...(省略)
}
}
]
}
],
"check_results": null
}
ちゃんと mode
が managed
になっており、Terraformで管理されていることが確認できました。
import
ブロックの削除
インポート後の インポートしてTerraform管理になったあとは import
ブロックは削除することができます。
environments/dev/import.tf
ファイルごと削除してTerraform Cloudで terraform plan
を実行した結果が以下になります。 No changes
となり削除しても何も影響がないことが確認できました。
おわりに
一度試してみると、Terraformの便利さを改めて実感しますね。最近では、AWSのCDKもインポートができるようになったようですが、以前はできなかったためやり辛さを感じていました。
インポートが容易になると、「とりあえずポチポチして作って、あとでインポートすればいっか。」という選択が取れるようになるので、今後も活用していきたいと思います。
Discussion