はじめに
今回は、以下の記事で作成した「単一の.tfファイル」のTerraformプロジェクトを、「remote state」を使用して、クロススタック化してみました。
少しでも参考になれば幸いです。
remote stateとは
remote stateとは、別のTerraformプロジェクト(スタック)の状態ファイル(.tfstate)を参照する仕組みのことです。
通常Terraformは「自分のstateしか知らない」ですが、remote stateを使うと別スタックのoutput値を取得できるようになります。
特徴
・取得(参照)できるのはoutputだけ
・読み取り専用で書き換え不可
・backendが一致している必要あり
メリット
・スタック(デプロイ単位)を分割できる
・VPCなどを共通化することで再利用性が高まる
デメリット
・強い依存(結合)になるため、更新/バージョン管理時に注意が必要です
本番環境では、remote stateの使用は最小限にし、モジュールやParameter Store / Secrets Manager等を使用した、外部stateに依存しない構成が推奨されています。
さっそく実装してみた
今回のプロジェクト構成
.
├── envs/
│ └── dev/
│ ├── network/
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── backend.tf
│ │ └── providers.tf
│ │
│ └── compute/
│ ├── main.tf
│ ├── data_remote_state.tf
│ ├── backend.tf
│ └── providers.tf
① networkディレクトリの作成
Terraformプロジェクト用のディレクトリを作成し、配下にenvs/dev/ディレクトリを作成します(実務での環境分離を想定しています)
envs/dev/配下にnetwork/ディレクトリを作成し、配下に以下の
・backend.tf
・provider.tf
・main.tf
・outputs.tf
を作成します。
terraform {
required_version = ">= 1.11.0"
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "dev/network/terraform.tfstate"
profile = "project-a-dev"
region = "ap-northeast-1"
encrypt = true
use_lockfile = true
}
}
provider "aws" {
profile = "project-a-dev"
region = "ap-northeast-1"
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "test-vpc"
}
}
# Internet Gateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "test-igw"
}
}
# Subnet(Public)
resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-a"
}
}
# Route Table
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.main.id
tags = {
Name = "public-rt-a"
}
}
# Route(Internet向け)
resource "aws_route" "public_route" {
route_table_id = aws_route_table.public_rt.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
# SubnetとRouteTableの関連付け
resource "aws_route_table_association" "public_assoc" {
subnet_id = aws_subnet.public_a.id
route_table_id = aws_route_table.public_rt.id
}
output "vpc_id" {
value = aws_vpc.main.id
}
output "subnet_id" {
value = aws_subnet.public_a.id
}
② computeディレクトリの作成
envs/dev/配下にcompute/ディレクトリを作成し、配下に以下の
・backend.tf
・provider.tf
・main.tf
・data_remote_state.tf
を作成します。
terraform {
required_version = ">= 1.11.0"
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "dev/compute/terraform.tfstate"
profile = "project-a-dev"
region = "ap-northeast-1"
encrypt = true
use_lockfile = true
}
}
provider "aws" {
profile = "project-a-dev"
region = "ap-northeast-1"
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
# SG
resource "aws_security_group" "public_sg" {
name = "public-sg"
vpc_id = data.terraform_remote_state.network.outputs.vpc_id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# EC2
resource "aws_instance" "ec2_instance" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
subnet_id = data.terraform_remote_state.network.outputs.subnet_id
vpc_security_group_ids = [aws_security_group.public_sg.id]
user_data = <<-EOF
#!/bin/bash
dnf update -y
dnf install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from EC2!!!!!</h1>" > /var/www/html/index.html
EOF
}
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "dev/network/terraform.tfstate"
profile = "project-a-dev"
region = "ap-northeast-1"
}
}
③ S3作成
以下のコマンドを順に実行し、各スタックの状態ファイル保存用のS3をデプロイします。
(必要に応じて暗号化/バージョニング設定等を行います)
aws s3api create-bucket \
--bucket my-terraform-state-bucket \
--create-bucket-configuration LocationConstraint=ap-northeast-1 \
--profile project-a-dev
④ networkをデプロイ
依存関係を意識し、networkを先にデプロイします。
以下のコマンドを順に実行します。
cd network
# 初期化
terraform init
# コード整形
terraform fmt
# 構文チェック
terraform validate
# 差分チェック
terraform plan
# 適用(デプロイ)
terraform apply
⑤ computeをデプロイ
同様の手順でcomputeをデプロイすれば完了です。
削除手順
削除は「依存関係の逆順」で行っていきます。
① computeの削除
以下のコマンドを順に実行し、まずはcomputeのリソースを削除します。
cd compute
terraform destroy
② networkの削除
次に、networkのリソースを削除します。
cd network
terraform destroy
③ S3の削除
最後に、S3バケットも削除します。
terraform管理外で作成したリソースのため、手動で削除します。
# バケット自体は削除されず、配下の全オブジェクトを一括削除(事前確認)
aws s3 rm s3://your-bucket-name --recursive --dryrun --profile project-a-dev
# 実行(バージョニングが有効になっている場合過去バージョンは残る)
aws s3 rm s3://your-bucket-name --recursive --profile project-a-dev
# 中身が空のバケット自体を削除(バージョニングが有効になっている場合エラー)
aws s3api delete-bucket --bucket your-bucket-name --profile project-a-dev
今回は以上になります!