Terraformで困っている部分
2/14最新時点の苦戦ポイント
- 環境差分によって生じるリソース数の違いを共通モジュールでインポートする方法
環境差分によるインポートリソース数が異なる場合について
VPCやIGWなど異なる環境でも数が変わらないものは必要な分だけリソースを宣言して宣言したリソースに対してimportをすれば問題はない。
環境差異によるパラメータは環境ディレクトリ側で変数化すればOK
resource "aws_vpc" "terraform-vpc" {
cidr_block = var.cidr_block
tags = {
Name = "${var.Tag_Name}-vpc"
Terraform = "True"
}
}
問題はリソースの数が異なる場合だ。
例
- 本番環境
- Subnet:6
- NAT:3
- 検証環境
- Subnet:4
- NAT:1
リソース数が異なるので上記のようにリソースを全部書きだそうとすると検証環境側に余計なリソースを新規作成してしまう問題点がある。
この問題を解決する方法としてfor_each
を使った対象リソースのループ処理で解決できるのはないかとリーダーからもアドバイスを頂き検証してみた。
ディレクトリ構成
現在のディレクトリ構成を整理する。
$ tree
.
├── env
│ ├── prd
│ │ ├── main.tf
│ │ └── vpc.tf
| | ~~~~~~~~~~~~~~~~~~~~~ # リソース単位でtfファイルが存在
│ └── sandbox
│ ├── main.tf
│ └── vpc.tf
| ~~~~~~~~~~~~~~~~~~~~~ # リソース単位でtfファイルが存在
|└── modules
| └── vpc
| ├── main.tf
| ├── outputs.tf
| └── variables.tf
| ~~~~~~~~~~~~~~~~~~~~~ # リソース単位でディレクトリが存在
modules/vpc/main.tf
resource "aws_vpc" "terraform-vpc" {
cidr_block = var.cidr_block
tags = {
Name = "${var.Tag_Name}-vpc"
Terraform = "True"
}
}
resource "aws_internet_gateway" "terraform-igw" {
vpc_id = aws_vpc.terraform-vpc.id
tags = {
Name = "${var.Tag_Name}-igw"
Terraform = "True"
}
}
resource "aws_subnet" "terraform-subnet" {
for_each = toset(var.subnet_list)
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet[0]
tags = {
Name = "${var.Tag_Name}-${var.subnet_type[0]}-subnet-a"
Terraform = "True"
}
}
modules/vpc/variables.tf
variable "Tag_Name" {
type = string
description = "Tag"
}
variable "cidr_block" {
type = string
description = "vpcのサブネットです"
}
variable "subnet_list" {
type = list(string)
description = "サブネット一覧"
}
variable "cidr_block-subnet" {
type = list(string)
description = "各サブネットのCIDR範囲"
}
variable "subnet_type" {
type = list(string)
description = "Nameタグ付与に使うパブリックかプライベートサブネットかの使い分け"
}
ポイントになるのがresource aws_subnet.terraform-subnet
だ。
for_each = toset(var.subset_list)'と記載しており、本番、検証でそれぞれ異なるサブネットIDの一覧を格納する配列変数をしている。
env/<環境名>/<リソース名.tf>`にパラメータ変数を入れていて例えば検証環境のVPCリソース関連のパラメータ変数をこのようにしている。
env/sandbox/vpc.tf
module "terraform-vpc" {
source = "../../modules/vpc"
Tag_Name = "dev"
cidr_block = "172.26.0.0/16"
cidr_block-subnet = ["172.26.10.0/24","172.26.11.0/24","172.26.20.0/24","172.26.21.0/24"]
subnet_type = ["public", "private"]
subnet_list = ["subnet-092b81ef7effd9b7b","subnet-0a0ade8ce186e3fde","subnet-059a56beb4eb17290","subnet-07d46fa561aa72db7"]
}
ここでsandbox側のVPCディレクトリのterraform state list
は以下の通りである。
$ terraform state list
module.terraform-vpc.aws_internet_gateway.terraform-igw
module.terraform-vpc.aws_subnet.terraform-subnet["subnet-059a56beb4eb17290"]
module.terraform-vpc.aws_subnet.terraform-subnet["subnet-07d46fa561aa72db7"]
module.terraform-vpc.aws_subnet.terraform-subnet["subnet-092b81ef7effd9b7b"]
module.terraform-vpc.aws_subnet.terraform-subnet["subnet-0a0ade8ce186e3fde"]
module.terraform-vpc.aws_vpc.terraform-vpc
この状態でterraform plan
を実行するとこうなる
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# module.terraform-vpc.aws_subnet.terraform-subnet["subnet-059a56beb4eb17290"] must be replaced
-/+ resource "aws_subnet" "terraform-subnet" {
~ arn = "arn:aws:ec2:ap-northeast-1:XXXXXXXXXXXXXX:subnet/subnet-059a56beb4eb17290" -> (known after apply)
~ availability_zone = "ap-northeast-1a" -> (known after apply)
~ availability_zone_id = "apne1-az4" -> (known after apply)
~ cidr_block = "172.26.20.0/24" -> "172.26.10.0/24" # forces replacement
~ id = "subnet-059a56beb4eb17290" -> (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
- map_customer_owned_ip_on_launch = false -> null
~ owner_id = "XXXXXXXXXXX" -> (known after apply)
~ tags = {
~ "Name" = "terraform-dev-private-subnet-a" -> "terraform-dev-public-subnet-a"
# (1 unchanged element hidden)
}
~ tags_all = {
~ "Name" = "terraform-dev-private-subnet-a" -> "terraform-dev-public-subnet-a"
# (1 unchanged element hidden)
}
# (3 unchanged attributes hidden)
- timeouts {}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Plan: 3 to add, 0 to change, 3 to destroy.
既存サブネットを3個消して新規に3個サブネットを作成しようとしていることがわかる。
細かく見るとcidr_block部分がすべて同じCIDRになっている。
moduleディレクトリ直下のmain.tf
でCIDRを配列で決め打ちしていることが原因だからである。
resource "aws_subnet" "terraform-subnet" {
for_each = toset(var.subnet_list)
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet[0] #### 配列変数で指定していることが原因
tags = {
Name = "${var.Tag_Name}-${var.subnet_type[0]}-subnet-a"
Terraform = "True"
}
}
実現したいこととしてはcidr_blockの変数部分をループで回せるようにしたい。
(env/sandbox/vpc.tf
部分の変数subnet_list
の配列部分)
こちらもfor_eachで回せればと思ったが、for_eachを再度使うことができず、他の記事で紹介されているcount
もfor_eachと組み合わせ使うことはできなかった。
│ Error: Invalid combination of "count" and "for_each"
│
│ on ../../modules/vpc/sg.tf line 19, in resource "aws_subnet" "terraform-subnet":
│ 19: for_each = toset(var.subnet_list)
│
│ The "count" and "for_each" meta-arguments are mutually-exclusive, only one should be used to be explicit about the number of resources to be created.
結論
for_each
をすでに使っている状態で他の変数の配列をループ処理させる方法が今のところわからず考え中
解決
サブネットリソースをパブリックとプライベートに分けて、AZの値を変数化することでやりたかったことが実現できた。
上のmain.tf
では1つのaws_subnet
リソースでパブリック、プライベートそれぞれのサブネットリソースをインポートしようとしたが、これを2つに分けた。
modules/vpc/main.tf
resource "aws_subnet" "terraform-public-subnet" {
for_each = var.public-AZ
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = each.value
availability_zone = "ap-northeast-1${each.key}"
tags = {
Name = "terraform-${var.Tag_Name}-public-subnet-${each.key}"
Terraform = "True"
}
}
resource "aws_subnet" "terraform-private-subnet" {
for_each = var.private-AZ
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = each.value
availability_zone = "ap-northeast-1${each.key}"
tags = {
Name = "terraform-${var.Tag_Name}-private-subnet-${each.key}"
Terraform = "True"
}
}
modules/vpc/variables.tf
variable "public-AZ" {
type = map(string)
description = "パブリックサブネットのAZ識別子とCIDRを紐付けたMAP変数"
}
variable "private-AZ" {
type = map(string)
description = "プライベートサブネットのAZ識別子とCIDRを紐付けたMAP変数"
}
これでenv側の変数はこのように値を入れていきます。
env/sandbox/vpc.tf
module "terraform-vpc" {
source = "../../modules/vpc"
Tag_Name = "dev"
cidr_block = "172.26.0.0/16"
public-AZ = { a = "172.26.10.0/24", c = "172.26.11.0/24" } #AZの識別子と対応するサブネットCIDRを選択
private-AZ = { a = "172.26.20.0/24", c = "172.26.21.0/24" }
}
このようにしてインポートする際に配列の要素を指定します。
terraform import 'module.terraform-vpc.aws_subnet.terraform-public-subnet["a"]' subnet-XXXXXXXXX
$ terraform state list
module.terraform-vpc.aws_subnet.terraform-private-subnet["a"]
module.terraform-vpc.aws_subnet.terraform-private-subnet["c"]
module.terraform-vpc.aws_subnet.terraform-public-subnet["a"]
module.wadatsumi-vpc.aws_subnet.wadatsumi-public-subnet["c"]
$ terraform state show 'module.terraform-vpc.aws_subnet.terraform-public-subnet["a"]'
# module.terraform-vpc.aws_subnet.terraform-public-subnet["a"]:
resource "aws_subnet" "terraform-public-subnet" {
availability_zone = "ap-northeast-1a"
availability_zone_id = "apne1-az4"
cidr_block = "172.26.10.0/24"
}
# 一部リソース省略しています
cidr_blockのeach_value
は変数でPublic-AZ
の値で、availability_zoneに含まれるeach_key
がキーになります。
インポート時にキーであるa
を指定してインポートしましたが、このときにcidr_block部分は変数で対応した値が入るようにしました。
このようにマップで変数設定することで環境ごとに作成するリソースの数が異なっていたとしても同じmodule
を使い回すことができるようになりました。
本番環境例
$ terraform state list
module.terraform-vpc.aws_subnet.terraform-private-subnet["a"]
module.terraform-vpc.aws_subnet.terraform-private-subnet["c"]
module.terraform-vpc.aws_subnet.terraform-private-subnet["d"]
module.terraform-vpc.aws_subnet.terraform-public-subnet["a"]
module.terraform-vpc.aws_subnet.terraform-public-subnet["c"]
module.terraform-vpc.aws_subnet.terraform-public-subnet["d"]
ここまで行けば後はチームで協力して各種リソースをインポートすれば当初やりたかった既存リソースのIaC化は実現できそうです。
スクラップで雑多に書き連ねた内容は記事としてまた改めて投稿しようと思います。
概要
会社の既存AWSリソースをIaCするという施策に取り組んでいるが、詰まっていてどうしてもわからない部分があって広く多くの人の目に触れて意見を頂きたいと考えて書き記してみました。
実現イメージ図
Terraform IaC構成
弊社では環境ごとにAWSマルチアカウント運用を実施しており、本番環境はアカウントA、検証環境はアカウントBといった感じで分けています。
検証環境の既存リソースをterraformer
でインポートした後にハードコーディングされた固定値部分を変数に直し、env/<環境名>/
ディレクトリでterraform apply
を実行してIaCしようと考えました。
既存リソースのコード化に関しては、以前書いた記事もご覧ください。
検証用にまずはVPCのみをインポートしてみました。
この時VPCディレクトリ内でterraformを実行したところ問題なく既存リソースをIaC化することはできました。
簡易ディレクトリ構成
$ tree
.
├── env
│ ├── production
│ └── sandbox
│ ├── main.tf
│ └── variables.tf
└── vpc
├── main.tf
├── outputs.tf
├── provider.tf
├── terraform.tfstate.backup
├── versions.tf
└── vpc.tf
4 directories, 8 files
vpc.tf
resource "aws_vpc" "terraform-vpc" {
assign_generated_ipv6_cidr_block = "false"
cidr_block = "10.0.0.0/16"
enable_classiclink = "false"
enable_classiclink_dns_support = "false"
enable_dns_hostnames = "true"
enable_dns_support = "true"
instance_tenancy = "default"
tags = {
Name = "Yuta-VPC2"
Terraform = "True"
}
tags_all = {
Name = "Yuta-VPC"
Terraform = "True"
}
}
既存リソース
$ ls
main.tf outputs.tf provider.tf terraform.tfstate.backup versions.tf vpc.tf
$ terraform plan
aws_vpc.terraform-vpc: Refreshing state... [id=vpc-0618354c8dd897278]
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.
この次にenv/sandbox
ディレクトリ内でvpcのディレクトリをdataリソースを使って参照し、既存リソースを参照できるか確認してみました。
main.tf
provider "aws" {
region = "ap-northeast-1"
}
terraform {
backend "s3" {
bucket = "XXXXXXXXXXXXX"
key = "env/sandbox/terraform.tfstate"
region = "ap-northeast-1"
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.70.0"
}
}
}
variables.tf
data "terraform_remote_state" "yuta-vpc" {
backend = "s3"
config = {
bucket = "XXXXXXXXXXXXX"
key = "vpc/terraform.tfstate"
region = "ap-northeast-1"
}
}
module "vpc" {
source = "../../vpc"
}
$ ls
main.tf variables.tf
$ terraform plan
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:
# module.vpc.aws_vpc.terraform-vpc will be created
+ resource "aws_vpc" "terraform-vpc" {
+ arn = (known after apply)
+ assign_generated_ipv6_cidr_block = false
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = false
+ enable_classiclink_dns_support = false
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "Yuta-VPC2"
+ "Terraform" = "True"
}
+ tags_all = {
+ "Name" = "Yuta-VPC2"
+ "Terraform" = "True"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
╷
│ Warning: Backend configuration ignored
│
│ on ../../vpc/main.tf line 2, in terraform:
│ 2: backend "s3" {
│
│ Any selected backend applies to the entire configuration, so Terraform expects provider configurations only in the root module.
│
│ This is a warning rather than an error because it's sometimes convenient to temporarily call a root module as a child module for testing purposes, but this backend
│ configuration block will have no effect.
╵
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
env/sandbox
ディレクトリでterraform plan
を実行すると既存リソースではなく新規リソースとして作成しようとしています。
resourceがmoduleに変更されていますので、vpcディレクトリのstate情報をterraform state mv
で変更してもう一度実行してみました。
## vpcディレクトリ
$ terraform state list
aws_vpc.terraform-vpc
$ terraform state mv aws_vpc.terraform-vpc module.vpc.aws_vpc.terraform-vpc
Move "aws_vpc.terraform-vpc" to "module.vpc.aws_vpc.terraform-vpc"
Successfully moved 1 object(s).
## env/sandboxディレクトリ
$ terraform plan
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:
# module.vpc.aws_vpc.terraform-vpc will be created
+ resource "aws_vpc" "terraform-vpc" {
+ arn = (known after apply)
+ assign_generated_ipv6_cidr_block = false
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = false
+ enable_classiclink_dns_support = false
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "Yuta-VPC2"
+ "Terraform" = "True"
}
+ tags_all = {
+ "Name" = "Yuta-VPC2"
+ "Terraform" = "True"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
╷
│ Warning: Backend configuration ignored
│
│ on ../../vpc/main.tf line 2, in terraform:
│ 2: backend "s3" {
│
│ Any selected backend applies to the entire configuration, so Terraform expects provider configurations only in the root module.
│
│ This is a warning rather than an error because it's sometimes convenient to temporarily call a root module as a child module for testing purposes, but this backend
│ configuration block will have no effect.
╵
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
tfstate情報を変更したにも関わらず、新規リソースとして見に行ってしまいます。
env/sandboxのtfstateも変更しようとしましたが、env/sandbox
ディレクトリにはまだtfstateファイルは作成されておらず、こちらは関係ないように思えます。
## env/sandboxディレクトリ
$ terraform state list
No state file was found!
State management commands require a state file. Run this command
in a directory where Terraform has been run or use the -state flag
to point the command to a specific state location.
モジュールの中にtfstateやprovider、backendの設定があるのがちょっと奇妙な感じがしていて、それはモジュールの外側にあるべきではないでしょうか。
そのVPCをモジュールとして使い回すならなおさら。
VPCをの定義ではなくてVPC自体を使いまわしたいのであれば、VPCを環境とは別のところで定義しておいて、dataブロックでvpc idなり何なりを参照するか、いっそそれを固定値として渡すかのほうが見通しが良いように思えます。
今回でいうと環境ごとにVPCを定義したいんだと思いますが。
terraform state mvをしても意味がないのは、それはvpcモジュール(として定義しているディレクトリ)の中のtfstateをいじくっているだけであって、env/sandbox
配下で参照しているtfstate(未作成なので何も参照していないようですが)はその情報を持っていないので新規作成になるんだと思います。
vpc
配下からproviderとbackendの定義を外して、その定義をenv/sandbox
配下のtfファイルに持ってきてあげてよしなにやればうまく行きそうな気がします。
…分かりづらかったらすみません。
ありがとうございます。
少し簡略化した図ですが、構成のイメージとしてリソース自体は各リソースディレクトリ内で定義して、変数の固定値を環境ディレクトリ内のvariables.tf
に埋め込みでvpc.tf
は共通化しようと考えています。
簡易版
VPC自体を使いまわしたいのであれば、VPCを環境とは別のところで定義しておいて、dataブロックでvpc idなり何なりを参照するか、いっそそれを固定値として渡すかのほうが見通しが良いように思えます。
仰るとおりVPC自体はリソースディレクトリ内で定義し、その情報をdataで環境ディレクトリで呼び出して既存リソースとして管理したいと考えていました。(ここはdata.tf
として別だしも考えています)
既存リソースとしてimportしたリソースはリソースディレクトリのtfstateファイル(vpc/terraform.tfstate
)で宣言されているから、環境ディレクトリのtfstateファイル(env/sandbox/tfstate)に書き込もうとすると別のリソース扱いになるのではないかと考えています。
解決するための方法として、環境ディレクトリのtfstateにリソースディレクトリのtfstate情報を移せば実現できるのではないかと考えていまして、、、
これが合っているのかわからないですが、もう少し調べてみます。
何をしようとしているのかようやく理解しました。
結論を言うと、その構成でstagingディレクトリからmoduleでvpcディレクトリを呼び出していることが間違いです。
その書き方だとvpcディレクトリのコードをvariables.tfで読み取り、cidr_vpcとenvを変数として渡してVPCを構築しようとしていることになるのでplanをすると新規作成になります。
moduleはあくまでコードを使い回すものです。
あと、おそらくdata sourceの使い方を誤解されています。
あくまで自分ならこうする、というものですが
vpcディレクトリのほうで既にVPCを作成したのであれば、そっちのtfstateは独立させておきます。
stagingディレクトリでvpcディレクトリのtfstateを読み込もうとはせず、vpcディレクトリで作成したvpc idを変数として直接渡すか、data sourceを使ってvpc idを検索してその値を必要なところに渡してやります。
vpcとvpcs(IDを取得したいなら面倒ですが後者)のdata resourceの使い方は先にはりました
また、記載のTerraformではdata sourceでterraform_remote_stateを取得していますが、取得したterraform_remote_stateの値をどこにも使用していないので無意味な記載になってしまっています。
自分もterraform_remote_stateはあまり使わないので、使い方の説明はこの記事に任せます。
※古めの記事なので、0.11以前の書き方で書かれています。
また、data sourceについてはこの辺を見てみてください。
分かりづらい部分がありましたら、言っていただければ頑張って補足します…。
ご丁寧に説明いただきありがとうございます。
まだ私自身Terraformの理解が完全に追いついていないのですが、
stagingディレクトリからmoduleでvpcディレクトリを呼び出していることが間違いです。
こちらは環境ディレクトリ内からterraform apply
を実行するとvpcのリソースを読み込むため環境ディレクトリ内で新規リソースが作成から新規作成されると理解しました。
環境ディレクトリ内でterraform apply
を実行する場合、module
とresourece
は使わず、data
リソースを配置してdata.tf
ファイル内でVPCディレクトリで作成したVPC-IDを受け取るようにして、variables.tf
で変数を指定し渡すということでしょうか?
data "aws_vpcs" "terraform-vpc" {}
data "aws_vpc" "terraform-vpc" {
count = length(data.aws_vpcs.terraform-vpc.ids)
id = tolist(data.aws_vpcs.terraform-vpc.ids)[count.index]
}
output "terraform-vpc"{
value = data.aws_vpcs.terraform-vpc
}
dataリソースの理解が間違っているように思えるので昔Kindleで手に入れたTerraform本で土日復習してみます。
こちらは環境ディレクトリ内からterraform applyを実行するとvpcのリソースを読み込むため環境ディレクトリ内で新規リソースが作成から新規作成されると理解しました。
意図しているところが合ってるかわかりませんが、読み込んでるのはコードだけであってリソースとはちょっと違うと思います。リソースの情報を持ってるのはtfstateなので。
data "aws_vpcs" "terraform-vpc" {}
これだけだとVPCを何も絞り込みできていませんので全てのVPCを引っ張ってきてしまいます。
それだと困るならtagsやfilterで適切に条件を設定して目的のVPCの情報を取ってこれるようにしてください。
これはcliで言うところのdescribe-vpcsにあたると考えるとイメージしやすいかと。
ドキュメントのサンプルでは全てのVPCの情報を取得して全てにflow_logの設定をしていますが、ここでやりたいことはそうではないと思います。
"aws_vpc"
(≠aws_vpcs
)のほうは、VPCのIDを渡してVPCの詳細情報を取得するのに使います。
このようにdata sourceを使うことによってAWS上の既存リソースの情報を取得することができます。
使うのにVPCのIDが必要なため、当然のことながらVPC IDを検索するのには使えません。
variables.tfで変数を指定し
これは別にvariables.tfでなくても、同ディレクトリ内の同階層のtfファイルならどこでも良いです。
あと、同ディレクトリ同階層の中で使うならoutputは必要ないです。
outputは戻り値のようなもので、Module内で設定すればModule外にその値を受け渡せますし、普通にModuleの外に書くとterraform apply
実行時にその値を表示します。
そのあたりも実践Terraformに恐らく記載していると思うのでよく読んでみてください。
試したこと
こちらの記事を参考にリソースディレクトリ内で作成されたtfstate情報を環境ディレクトリ配下のtfstateに移せばどうなるだろうと検証してみた。
## VPCリソースディレクトリ
$ terraform state list
aws_vpc.terraform-vpc
$ terraform state mv -state-out=from.tfstate aws_vpc.terraform-vpc aws_vpc.terraform-vpc
Move "aws_vpc.terraform-vpc" to "aws_vpc.terraform-vpc"
Successfully moved 1 object(s).
## 環境ディレクトリ
$ terraform state pull > to.tfstate
$ ls
main.tf to.tfstate variables.tf
$ terraform state mv -state=../../vpc/from.tfstate -state-out=to.tfstate aws_vpc.terraform-vpc aws_vpc.terraform-vpc
Move "aws_vpc.terraform-vpc" to "aws_vpc.terraform-vpc"
Successfully moved 1 object(s).
$ terraform state push to.tfstate
$ terraform state list
aws_vpc.terraform-vpc
$ terraform plan
aws_vpc.terraform-vpc: Refreshing state... [id=vpc-XXXXXXXXXXXXXXXXXXX]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
~~~~~~~~~
結果は変わらず、新規作成しようとしている。
リソースがモジュール化されているので移動させたtfstate情報をモジュール化したいのだが、どうやればいいのか行き詰まっています。
試したこと
頂いたアドバイスと読んだ本をもとに環境ディレクトリでVPCディレクトリのリソースIDを取得することができた。
data "aws_vpcs" "sandbox" {
tags = {
Terraform = "True"
}
}
data "aws_vpc" "sandbox" {
count = length(data.aws_vpcs.sandbox.ids)
id = tolist(data.aws_vpcs.sandbox.ids)[count.index]
}
# 確認出力
output "sandbox" {
value = data.aws_vpcs.sandbox.ids
}
$ terraform apply
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.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
sandbox = toset([
"vpc-06e88241aaea0ee4e",
])
ただここからどうやって取得したVPC IDを他のところに渡して更新作業を行なえばいいかで手詰まり中
本ではWorkSpacesの本番事例は少ないと記述されていたが、出版されてから数年経過しておりいくつか他社事例もあったのでWorkSpacesによる事例のほうがいいのかな?
記載されている例でもaws_vpcsで取得した情報をaws_vpcに渡していますし、なによりoutputのvalueに対してidsを渡していますよね。
それを他のresourceやmoduleに対しても渡してやればいいだけです。
ただ、aws_vpcsで取得するidsはlistであることに注意してください。性質上、複数の値を返すのでlistを返すようです。
listから個別の要素を取得するにはelement関数を使う他、
例えば
data.aws_vpcs.sandbox.ids[0]
みたいな書き方で、何番目の要素を取得するか指定できたはずです。(これは最初の要素を指定した例)
参考
Terraformのドキュメントには型の情報が一切書いてないので、Terraformのドキュメントだけ読んでても分かりづらいのは確かです…。
ここまではvpcディレクトリ下で作成したVPCは独立させて他からは変更を加えない前提で話していましたが、もしやりたいことがenv/sandbox
配下に既に作成したVPCの管理を移したい、ということなのであればやることは変わってきます。
私の中のイメージですと、最初の構成図でも示したディレクトリ構成図にして
- 検証環境からリソースをインポート
-
/env/{環境}
ディレクトリからそれぞれ変数だけを変更 - 変更事項があったら
/env/{環境}/
ディレクトリ配下でterraform apply
を実行しようと考えていました。
ディレクトリ構成図
ただ色々と検証してみて考え始めたのですが、既存リソースをインポートしている時点で別のAWSアカウントである本番環境リソースを同じリソースで管理するのはできないのではないかなと思い始めました。
後述でterraformerのインポートもうまくいかず悩んでいますが、インポート時点でtfstateの情報は検証環境のリソースを管理しているので当初私がやりたかったことは難しいのではないかと考えています。
そうなると最終的なディレクトリ構成図はこうなるのではないかと思います。
修正ディレクトリ構成図
ただこれは環境ごとにリソースファイルを作ることになり、土日に読んだ実践本でもIaCのアンチパターンであることは理解しています。
恥ずかしながら色々と頭の中で混乱していて明日辺りに一度リーダーに相談してみようと思います。
やりたいことはわかりました。
自分も前者のイメージで良いと思いますが、アプローチが間違ってると思います。
やっぱりModuleの使い方を誤っています。
前述のとおり、Moduleで使い回せるのは記載したコードのみであって、tfstateをModuleとして使い回すことはできません。
Terraformerに引っ張られすぎているのが混乱の原因の一つだと思います。
TerraformerではtfstateとTerraformのコードの双方を出力してくれますが、Terraformerで出力したtfstateは結局捨てたほうが早い気がしますね。
Terraformerのことは忘れて一旦ディレクトリ構成だけ見ると、Module用としてALB、EC2、ECSのディレクトリを作っておいてenv/{環境}
ディレクトリの中からModuleとして../../ALB
、../../EC2
、../../ECS
を参照するような構成を想定してるかと思います。
必要なのは「env/{環境}
ディレクトリ配下でterraformを実行した時に生成されるtfstate上で、既存リソースを管理すること」になります。
図中には「リソースデータはterraformerもしくはterraform importで取得」と書いてありますが、Moduleとして利用する各ディレクトリに必要なのはテンプレートとして使いまわしたいTerraformのコードだけであって、既存リソースの情報は含めません。
図だと、providers.tfは全て不要ですし、それは最終的にterraform applyを実行したいところであるenv/{環境}
にあるべきものです。
面倒ですがterraformerで作成されたtfstateは一旦捨てて、env/{環境}
下でModuleを含めて想定構成を記載し、その上でenv/{環境}
でterraform importでModule含めたリソースにtfstateを一致させる、とする必要がありそうです。
terraformerの作成したtfstateをそのまま使いたいなら後者の構成にならざるを得ないと思いますが、ご認識の通りそれはterraformとして効率的な記述とは言えません。
今後この環境をどうしていきたいのでしょうか?
今後もパラメータ変化をさせる可能性が高い、もしくは同じような環境を作る可能性が高いのであれば頑張ってModuleを使った記述にしてterraform importでtfstateをコードに一致させる作業をしてもよいと思います。
ただ、それは結構な工数がかかるのでそこまでするべきかは要検討です。もちろん理想的な構成を整備すべき気持ちはわかるのですが、他にもやるべきことは恐らくあるでしょうし。
デプロイ周りがきっちり整備されていたり、外部環境から直接IPで参照されていない、など条件が揃っていればいっそのこと新しく環境を作り直してしまうというのも一つの手段です。
既に手で作りこんだ環境をTerraformに落とすのは正直かなり面倒な作業なのです…。
度々アドバイスをいただき本当にありがとうございます。
terraformerで一括インポートは便利でしたが、terraformの理解不足で使い方を間違えている気がしていると感じています。
別の方からのアドバイスも頂いてterraform import
でインポートして既存リソースを管理したほうがいい気がしてきました。
今後この環境をどうしていきたいのでしょうか?
今後もパラメータ変化をさせる可能性が高い、もしくは同じような環境を作る可能性が高いのであれば頑張ってModuleを使った記述にしてterraform importでtfstateをコードに一致させる作業をしてもよいと思います。
仰るとおり今IaC目指すリソースをベースに新しいプロダクトを作る未来も見据えてまして、かつ社内のIaCノウハウを溜めるという目的も兼ねてIaCを推進しています。
規模自体はかなり小さくいくつかのリソースをIaCすることを目指しているので一旦頂いたアドバイスをもとに terraform import
からもう一度挑戦してみようと思います。
WorkSpaces検証
参考記事↓
Workspacesを使って環境別に切り替えられないか調べてみた。
init.tf
を作成し、terraformerを実行できる準備は済ませておく
インポート前にterraform {
required_version = ">= 1.0"
}
provider "aws" {
region = "ap-northeast-1"
}
terraform {
required_providers {
aws = {
version = "~> 3.70.0"
}
}
}
$ terraform workspace new sandbox
$ terraformer import aws -r vpc --filter="Name=tags.Terraform;Value=True"
$ ls -R
.:
generated init.tf terraform.tfstate.d
./generated:
aws
./generated/aws:
vpc
./generated/aws/vpc:
outputs.tf terraform.tfstate terraform.tfstate.backup vpc.tf
provider.tf terraform.tfstate.1644225261.backup versions.tf
./terraform.tfstate.d:
sandbox
./terraform.tfstate.d/sandbox:
この後はSandbox側のVPCリソースのTerraformバージョンを引き上げたりしたり何なりして調整
次に本番環境のworkspacesを作成
$ terraform workspace new prd
$ terraform workspace list
default
prd
* sandbox
また別アカウントの本番環境のVPCリソースをterraformerでインポートするためassume_role
リソースを追加する。
terraform {
required_version = ">= 1.0"
}
provider "aws" {
region = "ap-northeast-1"
assume_role {
role_arn = "arn:aws:iam::XXXXXXXXXXXXXXXXX:role/Terraform-Prd-Switch"
}
}
terraform {
required_providers {
aws = {
version = "~> 3.70.0"
}
}
}
次にterraformer実行時にプロファイルを変更して実行したが、本番リソースを見ずに検証環境のリソースをインポートしようとしてエラーになってしまった。
$ terraformer import aws -r vpc --filter="Name=tags.Terraform;Value=True" --profile=Prd --regions=ap-northeast-1
2022/02/07 09:54:46 aws importing region ap-northeast-1
2022/02/07 09:54:49 aws importing... vpc
2022/02/07 09:54:49 aws done importing vpc
2022/02/07 09:54:49 Number of resources for service vpc: 7
2022/02/07 09:54:49 Refreshing state... aws_vpc.tfer--vpc-06f6317461965de6c
2022/02/07 09:54:49 Refreshing state... aws_vpc.tfer--vpc-082a3ddd59c79bec0
2022/02/07 09:54:49 Refreshing state... aws_vpc.tfer--vpc-0e810cd22c713c246
2022/02/07 09:54:49 Refreshing state... aws_vpc.tfer--vpc-0fcaae8cac1aa1610
2022/02/07 09:54:49 Refreshing state... aws_vpc.tfer--vpc-06e88241aaea0ee4e
2022/02/07 09:54:49 Refreshing state... aws_vpc.tfer--vpc-0b34330eea1cee3c6
2022/02/07 09:54:49 Refreshing state... aws_vpc.tfer--vpc-d2b8a0b5
2022/02/07 09:54:49 ERROR: Read resource response is null for resource aws_vpc.tfer--vpc-0fcaae8cac1aa1610
2022/02/07 09:54:49 ERROR: Read resource response is null for resource aws_vpc.tfer--vpc-06e88241aaea0ee4e
2022/02/07 09:54:49 ERROR: Read resource response is null for resource aws_vpc.tfer--vpc-082a3ddd59c79bec0
2022/02/07 09:54:49 ERROR: Read resource response is null for resource aws_vpc.tfer--vpc-d2b8a0b5
2022/02/07 09:54:49 ERROR: Read resource response is null for resource aws_vpc.tfer--vpc-0b34330eea1cee3c6
2022/02/07 09:54:49 ERROR: Read resource response is null for resource aws_vpc.tfer--vpc-0e810cd22c713c246
2022/02/07 09:54:49 ERROR: Read resource response is null for resource aws_vpc.tfer--vpc-06f6317461965de6c
2022/02/07 09:54:49 ERROR: Unable to refresh resource tfer--vpc-06f6317461965de6c
2022/02/07 09:54:49 ERROR: Unable to refresh resource tfer--vpc-082a3ddd59c79bec0
2022/02/07 09:54:49 ERROR: Unable to refresh resource tfer--vpc-0e810cd22c713c246
2022/02/07 09:54:49 ERROR: Unable to refresh resource tfer--vpc-0fcaae8cac1aa1610
2022/02/07 09:54:49 ERROR: Unable to refresh resource tfer--vpc-06e88241aaea0ee4e
2022/02/07 09:54:49 ERROR: Unable to refresh resource tfer--vpc-0b34330eea1cee3c6
2022/02/07 09:54:49 ERROR: Unable to refresh resource tfer--vpc-d2b8a0b5
2022/02/07 09:54:49 Filtered number of resources for service vpc: 0
2022/02/07 09:54:49 aws Connecting....
2022/02/07 09:54:49 aws save vpc
2022/02/07 09:54:49 aws save tfstate for vpc
terraformerのGitHubを確認してもプロファイルで複数環境のリソースをインポートできる記述があるのにできない事象でつまづいている
ここまで現在わからない部分
- 環境ディレクトリから変数を渡して環境別の使い分け
- 別アカウントのリソースインポート方法
一つのTerraformディレクトリで複数環境の使いまわし方法が全然うまくいかない状況。
Workspaces
今回の運用では使わない方向で進める
理由
- 社内に詳しい人がいない
- 本番運用の事例がディレクトリ別リソース管理と比べて少ない
- workspaceのselect忘れで環境を間違えるリスクがある
今の所はリソースごとにディレクトリを切って環境ディレクトリで変数をもたせるようにする。
riddle(in twitter) です。
自分ならこうしますというのを書いておきます。
まず目指す最終構成はこれです。
modules
に定義を書き、environments
から参照します。
.
├── environments
│ ├── production
│ │ ├── alb.tf
│ │ ├── ec2.tf
│ │ ├── ecs.tf
│ │ ├── main.tf
│ │ └── vpc.tf
│ └── staging
│ └── 省略.tf
└── modules
├── alb
│ └── 省略.tf
├── ec2
│ └── 省略.tf
├── ecs
│ └── 省略.tf
└── vpc
├── main.tf
├── outputs.tf
└── variables.tf
modules/vpc
main.tf
resource "aws_vpc" "terraform-vpc" {
assign_generated_ipv6_cidr_block = "false"
cidr_block = var.cidr_block
enable_classiclink = "false"
enable_classiclink_dns_support = "false"
enable_dns_hostnames = "true"
enable_dns_support = "true"
instance_tenancy = "default"
}
variables.tf
variables "cidr_block" {
type = string
description = "vpc のサブネットです"
}
environments/production
main.tf
provider "aws" {
region = "ap-northeast-1"
}
terraform {
backend "s3" {
bucket = "XXXXXXXXXXXXX"
key = "env/sandbox/terraform.tfstate"
region = "ap-northeast-1"
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.70.0"
}
}
}
vpc.tf
module "vpc" {
source = "../../modules/vpc"
cidr_block = "192.168.0.0/24"
}
ここまでの持っていき方
module の作成
まず複数の環境に別々の vpc があるとのことなので、これを作るための module を作成する必要があります。module とはいってもただの terraform resource の集合体です。
まずは適当な作業用のディレクトリを作り、そこに移動します。
その上で staging/vpc.tf
、production/vpc.tf
を作ります。
以下の手順を参考に staging/vpc.tf
、production/vpc.tf
に各環境の設定をインポートします。
続いて、この2つのファイルを比較しながら共通部分と独自部分を自分で整理します。
例えば以下の場合はcidr_block
が違いますよね。
staging
resource "aws_vpc" "terraform-vpc" {
assign_generated_ipv6_cidr_block = "false"
cidr_block = "192.168.0.0/24"
enable_classiclink = "false"
enable_classiclink_dns_support = "false"
enable_dns_hostnames = "true"
enable_dns_support = "true"
instance_tenancy = "default"
}
production
resource "aws_vpc" "terraform-vpc" {
assign_generated_ipv6_cidr_block = "false"
cidr_block = "192.168.1.0/24"
enable_classiclink = "false"
enable_classiclink_dns_support = "false"
enable_dns_hostnames = "true"
enable_dns_support = "true"
instance_tenancy = "default"
}
なので modules/vpc/main.tf では以下を定義し
resource "aws_vpc" "terraform-vpc" {
assign_generated_ipv6_cidr_block = "false"
cidr_block = var.cidr_block
enable_classiclink = "false"
enable_classiclink_dns_support = "false"
enable_dns_hostnames = "true"
enable_dns_support = "true"
instance_tenancy = "default"
}
variables.tf でこれを定義します。
variables "cidr_block" {
type = string
description = "vpc のサブネットです"
}
各環境から呼び出す
environments/staging/vpc.tf
module "vpc" {
source = "../../modules/vpc"
cidr_block = "192.168.0.0/24"
}
environments/production/vpc.tf
module "vpc" {
source = "../../modules/vpc"
cidr_block = "192.168.0.0/24"
}
を作成します。
リソースを import する
そしたら最後にenvironments/staging
とenvironments/production
でそれぞれ terraform import をして、各環境に存在しているリソースをimportして state を作成します。
※この時、先にenvironments/staging/main.tf を作成し terraform init などを実行しておいてください
こちらにもコメントいただきありがとうございます。
terraformerは理解不足もあり、terraform import
を使って既存リソースをインポートして環境ディレクトリからterraform実行できるように挑戦してみます。
アドバイスいただき改めてありがとうございます。
教わった構成で検証を行っているのですが、一つ確認したいことがあります。
現在のディレクトリ構成
terraform$ tree
.
├── env
│ ├── prd
│ │ ├── main.tf
│ │ └── vpc.tf
│ └── sandbox
│ ├── main.tf
│ └── vpc.tf
└── modules
└── vpc
├── main.tf
└── variables.tf
5 directories, 6 files
適当な作業ディレクトリで既存リソースのインポート完了後、moduleの作成を実施しました。
modules/vpc
resource "aws_vpc" "terraform-vpc" {
cidr_block = var.cidr_block
tags = {
Name = var.Tag_Name
}
}
variable "cidr_block" {
type = string
description = "vpcのサブネットです"
}
variable "Tag_Name" {
type = string
description = "Tag名"
}
その後環境ディレクトリ配下に移動し、必要リソースを作成しました。
provider "aws" {
region = "ap-northeast-1"
}
terraform {
backend "s3" {
bucket = "XXXXXXXXXXXXXXXXXXXXXXXXXXX"
key = "env/sandbox/terraform.tfstate"
region = "ap-northeast-1"
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.70.0"
}
}
}
module "terraform-vpc" {
source = "../../modules/vpc"
cidr_block = "172.26.0.0/16"
Tag_Name = "dev"
}
この状態で、env/環境ディレクトリ
でterraform initを実行しterraform importを実行するとエラーになります。
$ terraform import aws_vpc.terraform-vpc vpc-06e88241aaea0ee4e
Error: resource address "aws_vpc.terraform-vpc" does not exist in the configuration.
Before importing this resource, please create its configuration in the root module. For example:
resource "aws_vpc" "terraform-vpc" {
# (resource arguments)
}
resourceがないことから生じるエラーですが、試しに環境ディレクトリ内のリソースファイル(ここではvpc.tf
)に空のリソースを追記してterraform importを実施しましたが、これだとモジュールリソースを見てくれないように感じます。
module "vpc" {
source = "../../modules/vpc"
cidr_block = "172.26.0.0/16"
Tag_Name = "waas-dev"
}
resource "aws_vpc" "terraform-vpc" {}
$ terraform plan
aws_vpc.terraform-vpc: Refreshing state... [id=vpc-06e88241aaea0ee4e]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create #新規作成する
~ update in-place
Terraform will perform the following actions:
~~~~~~~~~~~~~~~~~~~~
importするときはresourece
を実行ディレクトリで作成すると上記のようになるのですが、importするときはモジュールディレクトリで実行すればよろしいでしょうか?
terraform import ADDR ID
のように ADDR に terraform でのリソースの位置、 ID に AWS のリソースアドレスを取ります。
environments/prd
で terraform import aws_vpc.terraform-vpc vpc-06e88241aaea0ee4e
を実行した場合、vpc.tf には以下が記載されてないといけません。
resource "aws_vpc" "terraform-vpc" {
cidr_block = "てきとう"
tags = {
Name = "てきとう"
}
}
しかしこれでは module が使えていないですね。
さて、今回のケースは vpc.tf
に module の呼び出しが記載されています。
module "my-vpc" { # ←わかりやすさのために名前を変えます
source = "../../modules/vpc"
cidr_block = "172.26.0.0/16"
Tag_Name = "dev"
}
そして module 側はこのようになっています。
resource "aws_vpc" "terraform-vpc" {
cidr_block = var.cidr_block
tags = {
Name = var.Tag_Name
}
}
そのためこのときの terraform のリソースアドレス(リソースの位置) は module.my-vpc.aws_vpc.terraform-vpc
となります。
よって今回は environments/prd
で terraform import module.my-vpc.aws_vpc.terraform-vpc vpc-06e88241aaea0ee4e
を実行すればよいことなります。
ありがとうございます!
確かめてみたら出来ました。
これで既存リソースをモジュール化してみてIaC挑戦してみようと思います。
本当にありがとうございました!!
進捗状況 2/10時点
多くのアドバイスを頂き、モジュール化することで環境ディレクトリからモジュールAWSリソースを変数で環境別に使い回すことができることが確認できた。
terraformerだと異なるアカウントのリソースをassume-role
でインポートができず、困っていたがterraform import
ならassume-role
でできた。
terraform/env/prd/main.tf
provider "aws" {
region = "ap-northeast-1"
assume_role {
role_arn = "arn:aws:iam::XXXXXXXXXXXXX:role/Terraform-Prd-Switch"
}
}
今考えている懸念点
- 既存リソースのインポート作業は
terraform import
の場合1個ずつしかインポートできず、本番/検証で2回インポート作業が発生する(上で頂いたご指摘どおり工数が多くなる) - モジュールディレクトリの数を1リソース単位で作成した場合、NAT、IGW、ルートテーブル、セキュリティグループと細かくなり視認性が悪くなる
懸念点への(自分で考えた)対応策
- 工数が多くなるが、チームにIaCのノウハウを広める目的もあるので私が検証環境で実施したことを他チームメイトに共有して実施してもらえたらチーム内のノウハウが溜まるのでそのまままずは進めてみる
- モジュールディレクトリ内のリソースディレクトリは関連リソースを一つに固めて整理する
- 例:VPCディレクトリならNAT、ルートテーブル、IGWをセット
terraform/modules/vpc/main.tf
resource "aws_vpc" "terraform-vpc" {
cidr_block = var.cidr_block
tags = {
Name = var.Tag_Name_vpc
Terraform = "True"
}
}
resource "aws_route_table" "terraform-rt" {
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = var.gateway
}
tags = {
Name = var.Tag_Name_rt
Terraform = "True"
}
}
resource "aws_internet_gateway" "terraform-igw" {
tags = {
Name = var.Tag_Name_igw
Terraform = "True"
}
}
こういう感じで進めればやりたいことはできると思う。
幸い(?)対象リソースはまだそこまで複雑な仕組みでないのでディレクトリ構成とモジュール化までできれば後は協力して進めばいけると思う。
これで挑戦してみよう!
新たなつまずきポイント
サブネットやNATなど本番と検証環境で数が異なる場合のインポート方法に苦戦中
本番
- サブネット数:6
- NAT数:3
検証
- サブネット数:4
- NAT数:1
モジュールディレクトリでサブネットのリソースを書こうとする場合当初考えていた方法
terraform/modules/vpc/main.tf
resource "aws_vpc" "terraform-vpc" {
cidr_block = var.cidr_block
tags = {
Name = var.Tag_Name_vpc
Terraform = "True"
}
}
~~~~~~~~中略~~~~~~~~
resource "aws_subnet" "terraform-subnet-1" {
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet-1
tags = {
Name = var.Tag_Name_subnet-1
Terraform = "True"
}
}
resource "aws_subnet" "terraform-subnet-2" {
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet-2
tags = {
Name = var.Tag_Name_subnet-2
Terraform = "True"
}
}
resource "aws_subnet" "terraform-subnet-3" {
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet-3
tags = {
Name = var.Tag_Name_subnet-3
Terraform = "True"
}
}
resource "aws_subnet" "terraform-subnet-4" {
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet-4
tags = {
Name = var.Tag_Name_subnet-4
Terraform = "True"
}
}
# 検証環境では不要
resource "aws_subnet" "terraform-subnet-5" {
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet-5
tags = {
Name = var.Tag_Name_subnet-5
Terraform = "True"
}
}
# 検証環境では不要
resource "aws_subnet" "terraform-subnet-6" {
vpc_id = aws_vpc.terraform-vpc.id
cidr_block = var.cidr_block-subnet-6
tags = {
Name = var.Tag_Name_subnet-6
Terraform = "True"
}
}
サブネットリソースをモジュール化する場合、本番環境に合わせるため6個作ることになるが検証環境では逆に不要なリソースになる。
for_eachを使ってループ処理させるというアドバイスもいただいたが、terraform import <リソース位置> <既存リソースID>
と指定する関係上、1個1個指定しないといけないからループ構文がかけない問題がある。
参考記事
抱えている問題として2点挙げられる。
- 同じリソースを何回も書く手間
- その環境にしかない場合のモジュールの使いまわし方法
新規リソースならfor_each
やcount
などを使って繰り返し処理、条件分岐で生成タイミングを環境タグで指定できそうだが、既存リソースを1個ずつインポートする問題はどうしても避けられないようと思うので、1の問題は人力でやるしかなさそう。
ただそうなると2のように検証環境では作成不要なサブネット5,6のように作っていけないリソースの制御判定をどうすればよいか🤔
for_each
とconcat
を組み合わせて動的にリソースを作成する記事をリーダーから共有されたのでこれで検証してみる。