Terraformで複数環境をデプロイするチュートリアル(ローカルモジュール編)
Terraform入門時の最初の関門として、環境分離があると思います。
公式ドキュメントに記載はありますが初めての場合、理解に時間がかかるかもしれません。
手を動かして理解したい方向に向けて、ハンズオンできるチュートリアルを用意してみました。
前提
HashiCorp公式のTerraformチュートリアルのコードをベースに、環境分離の対応を行います。
以下のチュートリアルを終えてから実施することをおすすめします。
AWS | Terraform | HashiCorp Developer
チュートリアルで作成したリソースも削除済みであることを前提とします。削除していない場合は、以下を参考に削除してください。
Destroy infrastructure | Terraform | HashiCorp Developer
ハンズオン
最終的には以下のディレクトリ構成になります。
.
├── environments
│ ├── prod
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── terraform.tf
│ └── stg
│ ├── main.tf
│ ├── outputs.tf
│ └── terraform.tf
└── modules
├── compute
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── network
├── main.tf
└── outputs.tf
以降はこの構成になるプロセスを説明します。
ハンズオン用ファイルの用意
HashiCorp公式のTerraformチュートリアルを終えると以下のファイルが作成されていると思います。
.
├── main.tf
├── outputs.tf
├── terraform.tf
└── variables.tf
手元にない場合は、以下のファイルを用意します。
HCP Terraform部分のチュートリアルまでやっている場合は、terraform.tf
を以下の設定に修正してください。(cloudブロックとworkspace設定の削除)
Collaborate using HCP Terraform | Terraform | HashiCorp Developer
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
provider "aws" {
region = "us-west-2"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.19.0"
name = "example-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24"]
enable_dns_hostnames = true
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
vpc_security_group_ids = [module.vpc.default_security_group_id]
subnet_id = module.vpc.private_subnets[0]
tags = {
Name = var.instance_name
}
}
variable "instance_name" {
description = "Value of the EC2 instance's Name tag."
type = string
default = "learn-terraform"
}
variable "instance_type" {
description = "The EC2 instance's type."
type = string
default = "t2.micro"
}
output "instance_hostname" {
description = "Private DNS name of the EC2 instance."
value = aws_instance.app_server.private_dns
}
モジュールと環境ディレクトリの作成
まずはモジュールディレクトリを作成します。
今回はnetworkとcomputeモジュールを作成します。
networkにはVPC、computeにはEC2の定義が入ります。
mkdir modules
mkdir modules/network
mkdir modules/compute
環境毎のディレクトリを作成します。
今回はSTGとPRODの2環境を想定しています。
環境が増える際は、このディレクトリも増やすイメージです。
mkdir environments
mkdir environments/stg
mkdir environments/prod
環境ディレクトリには、既存のtfファイルをコピーしておきます。
ルートディレクトリにあるtfファイルは不要になるため、削除します。
cp *.tf envrionments/prod
cp *.tf envrionments/stg
rm *.tf
この時点で以下のディレクトリ構成になります。
├── environments
│ ├── prod
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── terraform.tf
│ │ └── variables.tf
│ └── stg
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tf
│ └── variables.tf
└── modules
├── compute
└── network
network モジュールの作成
network モジュールを作成します。
既存のmain.tf
のVPC部分のリソース定義を、そのままmodules/network/main.tf
に記載します。
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.19.0"
name = "example-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24"]
enable_dns_hostnames = true
}
環境ディレクトリのmain.tfをnetworkモジュールを利用するように修正します。
provider "aws" {
region = "us-west-2"
}
+ module "network" {
+ source = "../../modules/network"
+ }
- module "vpc" {
- source = "terraform-aws-modules/vpc/aws"
- version = "5.19.0"
-
- name = "example-vpc"
- cidr = "10.0.0.0/16"
-
- azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
- private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
- public_subnets = ["10.0.101.0/24"]
-
- enable_dns_hostnames = true
- }
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
vpc_security_group_ids = [module.vpc.default_security_group_id]
subnet_id = module.vpc.private_subnets[0]
tags = {
Name = var.instance_name
}
}
リソースaws_instance
のvpc_security_group_ids
とsubnet_id
はVPCモジュールの値を参照しています。
しかし、vpcモジュールはnetworkモジュール内に移動しました。このままでは参照ができません。
networkモジュール外からvpcモジュールの値を参照できるようにする必要があります。
networkモジュールに以下のファイルを追加します。
output "vpc" {
description = "VPC module containing all VPC related resources"
value = module.vpc
}
compute モジュールの作成
computeモジュールを作成します。
aws_instance
リソースでvariablesを使っているため、variables.tfを用意します。
既存のvariables.tfはcomputeモジュールで使う値のみ定義されているので、そのままコピーします。
cp environments/prod/variables.tf modules/compute
今回は環境ディレクトリ側では、variables.tfを使わないため削除します。
rm environments/prod/variables.tf
rm environments/stg/variables.tf
networkモジュールからdefault_security_group_id
とprivate_subnet_id
を受け取るために、variablesを追加します。
variable "instance_name" {
description = "Value of the EC2 instance's Name tag."
type = string
default = "learn-terraform"
}
variable "instance_type" {
description = "The EC2 instance's type."
type = string
default = "t2.micro"
}
+ variable "default_security_group_id" {
+ description = "The ID of the default security group."
+ type = string
+ }
+ variable "private_subnet_id" {
+ description = "The ID of the private subnet to launch the instance in."
+ type = string
+ }
既存のmain.tfのEC2関連のリソース定義をcomputeモジュールに記載します。
モジュール内でaws_instance
を定義するにあたり、module.vpcから受け取っていた部分をVariables経由の受け渡しに変更します。
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
+ vpc_security_group_ids = var.default_security_group_id
+ subnet_id = var.private_subnet_id
- vpc_security_group_ids = [module.vpc.default_security_group_id]
- subnet_id = module.vpc.private_subnets[0]
tags = {
Name = var.instance_name
}
}
variables.tf
と同様にoutputs.tf
もmodules配下へコピーします。
cp environments/prod/outputs.tf modules/compute
環境ディレクトリで直接EC2リソースを定義していた部分を、computeモジュールの呼び出しに置き換えます。
環境差異は環境ディレクトリで定義します。以下ではモジュール呼び出し時に環境ごとの引数を渡します。
provider "aws" {
region = "us-west-2"
}
module "network" {
source = "../../modules/network"
}
+ module "compute" {
+ source = "../../modules/compute"
+
+ instance_name = "<prod | stg>-server"
+ instance_type = "t2.micro"
+ default_security_group_id = module.network.vpc.default_security_group_id
+ private_subnet_id = module.network.vpc.private_subnets[0]
+ }
- data "aws_ami" "ubuntu" {
- most_recent = true
-
- filter {
- name = "name"
- values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
- }
-
- owners = ["099720109477"] # Canonical
- }
-
- resource "aws_instance" "app_server" {
- ami = data.aws_ami.ubuntu.id
- instance_type = var.instance_type
-
- vpc_security_group_ids = [module.vpc.default_security_group_id]
- subnet_id = module.vpc.private_subnets[0]
-
- tags = {
- Name = var.instance_name
- }
- }
moduleを経由して取得するために、環境ディレクトリのoutputs.tfを修正します。
output "instance_hostname" {
description = "Private DNS name of the EC2 instance."
+ value = module.compute.instance_hostname
- value = aws_instance.app_server.private_dns
}
動作確認: Terraformの実行
Terraformの実行は環境に応じたenvironmentsディレクトリにて可能です。
cd environments/<prod | stg>/
terraform init
terraform plan
terraform apply
ちなみに、-chdir
オプションを使えばディレクトリを移動せずに実行も可能です。
terraform -chdir=./environments/stg plan
terraform -chdir=./environments/prod plan
おわりに
ローカルモジュールを使ったTerraform環境分離についてでした。
中々ステップ数は多くなってしまいましたが、理解の助けになれば幸いです。
Terraformの環境差異の表現方法は他にも色々あります。
他のパターンにも興味がある方は以下の記事シリーズがおすすめです。
#HashiTalks Japanで「シングルテナント構成のSaaSのIaCにTerraform Workspacesを導入してみた」という発表をしました | DevelopersIO