terraform_remote_stateをまとめてみた

terraform_remote_stateの運用に困っている人に役立つtipsです。
2022.05.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。

今回は、Terraformで使用されるterraform_remote_stateをまとめてみようと思います。

terraform_remote_stateとは

terraform_remote_stateとは、一言でいうと、「別フォルダのtfstateファイルのoutputを参照するdataブロック」です。

フォルダ間の値のやり取りで使う便利な機能で私も愛用しています。

詳しい使い方は、以下ブログで紹介されているので合わせてご覧いただければと思います。

ハブを作ろう!

terraform_remote_stateを使うにあたり、気をつけるポイントとして「どのフォルダがどのtfstateを参照しているか」という問題があります。

とっても便利ですが、作りすぎると参照先を迷走する時がたまにあります。

例えば、以下のようなフォルダ構成があるとします。

また、terraform applyenvironment/production/albなどのフォルダ単位で行うとします。

.
├── environment
│   ├── production
│   │   ├── README.md
│   │   ├── alb
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars
│   │   ├── ec2
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars
│   │   ├── security_group
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars
│   │   ├── vpc
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars

このフォルダ構成の場合、以下のように各フォルダごとにterraform_remote_stateを定義する必要があります。

environment/production/alb

environment/production/alb/data.tf

# 配置するVPC、サブネットのIDを取得したい
data "terraform_remote_state" "vpc" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/vpc/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

# ALBのSecurity Group IDを取得したい
data "terraform_remote_state" "security_group" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/security_group/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

# ターゲットグループに登録するインスタンスIDを取得したい
data "terraform_remote_state" "ec2" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/ec2/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

environment/production/ec2

environment/production/ec2/data.tf

# 配置するVPC、サブネットのIDを取得したい
data "terraform_remote_state" "vpc" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/vpc/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

# EC2のSecurity Group IDを取得したい
data "terraform_remote_state" "security_group" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/security_group/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

environment/production/security_group

environment/production/security_group/data.tf

# 配置するVPC、サブネットのIDを取得したい
data "terraform_remote_state" "vpc" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/vpc/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

ご覧の通り、各ファイルごとに必要なterraform_remote_stateブロックが異なっていることわかります。まとめると以下の通りです。

  • alb:「ec2」、「security_group」、「vpc」のtfstate
  • ec2:「security_group」、「vpc」のtfstate
  • security_group:「vpc」のtfstate
  • vpc:必要なし

個人的には許容範囲ですが、構成が大きくなるほどより参照が複雑になります。

そこで、terraform_remote_stateを一箇所にまとめ、参照先の一元化を行います。

やってみた

フォルダ構成の変更

はじめに、以下のようにterraform_remote_state用にフォルダを作成します。

.
├── environment
│   ├── production
│   │   ├── README.md
│   │   ├── alb
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars
│   │   ├── ec2
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars
│   │   ├── security_group
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars
│   │   ├── vpc
│   │   │   ├── data.tf
│   │   │   ├── main.tf
│   │   │   ├── output.tf
│   │   │   ├── variables.tf
│   │   │   └── terraform.tfvars
│   │   ├── remote_state
│   │   │   ├── data.tf
│   │   │   ├── output.tf

terraform_remote_stateの収集

次に、environment/production/remote_state/data.tfで、すべてのterraform_remote_stateを収集します。

environment/production/remote_state/data.tf

# environment/production/alb/
data "terraform_remote_state" "alb" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/alb/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

# environment/production/ec2/
data "terraform_remote_state" "ec2" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/ec2/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

# environment/production/security_group/
data "terraform_remote_state" "security_group" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/security_group/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

# environment/production/vpc/
data "terraform_remote_state" "vpc" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/vpc/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

outputで再出力

最後に、environment/production/remote_state/outputs.tfで収集したterraform_remote_stateをまとめて再出力します。

environment/production/remote_state/outputs.tf

output "production" {
  value = {
    # vpc
    vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id
    subnet_1a = data.terraform_remote_state.vpc.outputs.subnet_1a
    subnet_1c = data.terraform_remote_state.vpc.outputs.subnet_1c

    # security_group
    sg_id_ec2 = data.terraform_remote_state.security_group.outputs.security_group_id_ec2
    sg_id_alb = data.terraform_remote_state.security_group.outputs.security_group_id_alb

    # ec2
    web_a = data.terraform_remote_state.ec2.outputs.ec2_id_a
    web_c = data.terraform_remote_state.ec2.outputs.ec2_id_c

    # alb
    alb_dns = data.terraform_remote_state.alb.outputs.alb_dns
  }
}

もたらす効果

他のディレクトリは、environment/production/remote_state/のみを参照することで他フォルダのterraform_remote_stateの参照が完結します。

各ディレクトリごとに定義していた、terraform_remote_stateに対するコード量の減少と、各ディレクトリの値を一元管理できるメリットがあります。

environment/production/remote_state以外/data.tf

data "terraform_remote_state" "env" {
  backend = "s3"

  config = {
    bucket = "terraform-state-XXXXXXXXXXXX"
    key    = "production/remote_state/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

まとめると、各種フォルダに必要なtfstateが以下のように変化します。

「どこに何があるの?」から「とりあえず、remote_stateをみる」に変化します。

As Is

  • alb:「ec2」、「security_group」、「vpc」のtfstate
  • ec2:「security_group」、「vpc」のtfstate
  • security_group:「vpc」のtfstate
  • vpc:必要なし

To Be

  • alb:「remote_state」のtfstate
  • ec2:「remote_state」のtfstate
  • security_group:「remote_state」のtfstate
  • vpc:「remote_state」のtfstate

※vpcに関してはつけてもつけなくても良いです。個人的には、個別管理は煩雑さを招くため定義する派です。

参照

参考までに参照方法は以下の通りです。

Mapで出力しているため参照名が長いですが、文字列型で出力すれば少し短くできるかと思います。

environment/production/ec2/main.tf

resource "aws_instance" "web_1a" {
  ami = var.ami_id
  instance_type = "t2.micro"

  subnet_id = data.terraform_remote_state.env.outputs.production.subnet_1a
  security_groups = [
    data.terraform_remote_state.env.outputs.production.security_group_id_ec2
  ]
}

参考記事

公式

DevelopersIO

おわりに

以上、「terraform_remote_stateをまとめてみた」でした。

terraform_remote_stateはとても便利ですが、管理も大変なので今回みたいな手法を使うと楽になれるかと思います。

この記事がどなたかの参考になれば幸いです。

以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!