Terraformで複数環境をデプロイするチュートリアル(ローカルモジュール編)

Terraformで複数環境をデプロイするチュートリアル(ローカルモジュール編)

2025.08.29

Terraform入門時の最初の関門として、環境分離があると思います。

公式ドキュメントに記載はありますが初めての場合、理解に時間がかかるかもしれません。

https://developer.hashicorp.com/terraform/language/style#multiple-environments

手を動かして理解したい方向に向けて、ハンズオンできるチュートリアルを用意してみました。

前提

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.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.92"
    }
  }

  required_version = ">= 1.2"
}
main.tf
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
  }
}
variables.tf
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"
}

outputs.tf
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に記載します。

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モジュールを利用するように修正します。

environments/<prod | stg>/main.tf
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_instancevpc_security_group_idssubnet_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_idprivate_subnet_idを受け取るために、variablesを追加します。

modules/compute/variables.tf
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経由の受け渡しに変更します。

modules/compute/main.tf
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モジュールの呼び出しに置き換えます。

環境差異は環境ディレクトリで定義します。以下ではモジュール呼び出し時に環境ごとの引数を渡します。

environments/<prod | stg>/main.tf
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を修正します。

environments/<prod | stg>/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

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.