現代のクラウド環境では、インフラストラクチャの管理が複雑化しています。手動でのサーバー構築やネットワーク設定は、時間がかかるだけでなく、人的ミスによる障害リスクも抱えています。そこで注目されているのがInfrastructure as Code(IaC)という概念です。
本記事では、IaCの代表的なツールであるTerraformについて、基本概念から実践的な使い方まで詳しく解説します。2025年の最新情報も含めて、効率的なインフラ管理を実現する方法をご紹介していきます。

Infrastructure as Codeが解決する課題
手動管理の限界
従来のインフラ管理では、以下のような課題がありました:
- 再現性の低さ:同じ環境を複数作る際、設定のばらつきが発生
- ドキュメント管理の困難:設定変更の履歴が追跡しにくい
- スケーラビリティの問題:大規模環境での手動管理は現実的でない
- 人的ミス:設定ミスによる障害リスク
- 属人化:特定の担当者に依存する運用体制
Infrastructure as Codeの利点
IaCを導入することで、これらの課題を解決できます:
- コードによる管理:インフラの設定をコードとして管理
- バージョン管理:Gitなどを使用した変更履歴の追跡
- 自動化:CI/CDパイプラインとの連携
- 再現性:同じコードから同じ環境を確実に構築
- レビュープロセス:コードレビューによる品質向上
Terraformの基本概念
Terraformとは
Terraformは、HashiCorp社が開発したオープンソースのIaCツールです。HCL(HashiCorp Configuration Language)という専用の言語を使用して、インフラストラクチャをコードで定義します。
主要な構成要素
1. プロバイダー(Provider)
プロバイダーは、Terraformが各種クラウドサービスやAPIと連携するためのプラグインです:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
2. リソース(Resource)
リソースは、実際に作成・管理するインフラストラクチャコンポーネントを定義します:
resource "aws_instance" "web_server" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
tags = {
Name = "WebServer"
Environment = "production"
}
}
3. データソース(Data Source)
既存のリソース情報を取得するために使用します:
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
4. 変数(Variables)
設定値を動的に変更できるようにします:
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "environment" {
description = "Environment name"
type = string
validation {
condition = can(regex("^(dev|staging|prod)$", var.environment))
error_message = "Environment must be dev, staging, or prod."
}
}
Terraform State(状態管理)
Terraformの重要な概念の一つが状態管理です。Terraformはterraform.tfstate
ファイルで現在のインフラの状態を追跡しています。
# ローカル状態管理(開発環境)
terraform {
backend "local" {
path = "terraform.tfstate"
}
}
# リモート状態管理(本番環境推奨)
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
2025年最新:TerraformとOpenTofuの選択ガイド
ライセンス変更の影響
2023年8月、HashiCorpは Terraform を含む主要製品のライセンスを Mozilla Public License (MPL) 2.0 から Business Source License (BSL) に変更しました。これにより、2025年7月以降、商用利用には制限が加わる可能性があります。
OpenTofuという選択肢
OpenTofuは、Terraformのオープンソースフォークとして2023年に開始されたプロジェクトです:
- 完全オープンソース:MPL 2.0ライセンス継続
- Terraform互換:既存のTerraformコードがそのまま使用可能
- コミュニティ主導:Linux Foundationの傘下で開発
- 活発な開発:機能追加とバグ修正が継続的に実施

選択の指針
項目 | Terraform | OpenTofu |
---|---|---|
ライセンス | BSL(商用制限あり) | MPL 2.0(完全オープンソース) |
機能 | 最新機能が先行実装 | 互換性重視、安定性優先 |
サポート | HashiCorp公式サポート | コミュニティサポート |
移行コスト | – | ほぼゼロ(互換性が高い) |
実践例:AWS VPCとEC2の構築
プロジェクト構成
実践的なTerraformプロジェクトを構築してみましょう。以下のファイル構成で進めます:
terraform-practice/
├── main.tf # メインの設定
├── variables.tf # 変数定義
├── outputs.tf # 出力値
├── terraform.tfvars # 変数値(Gitに含めない)
└── modules/
└── vpc/
├── main.tf
├── variables.tf
└── outputs.tf
VPCモジュールの作成
modules/vpc/variables.tf
variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "Availability zones"
type = list(string)
default = ["ap-northeast-1a", "ap-northeast-1c"]
}
variable "public_subnet_cidrs" {
description = "CIDR blocks for public subnets"
type = list(string)
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "private_subnet_cidrs" {
description = "CIDR blocks for private subnets"
type = list(string)
default = ["10.0.10.0/24", "10.0.20.0/24"]
}
variable "tags" {
description = "Tags to apply to resources"
type = map(string)
default = {}
}
modules/vpc/main.tf
# VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(var.tags, {
Name = "main-vpc"
})
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(var.tags, {
Name = "main-igw"
})
}
# Public Subnets
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = merge(var.tags, {
Name = "public-subnet-${count.index + 1}"
Type = "public"
})
}
# Private Subnets
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = merge(var.tags, {
Name = "private-subnet-${count.index + 1}"
Type = "private"
})
}
メイン設定とEC2インスタンス
main.tf
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
# 共通タグ
locals {
common_tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "terraform"
}
}
# VPCモジュールの使用
module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]
public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"]
private_subnet_cidrs = ["10.0.10.0/24", "10.0.20.0/24"]
tags = local.common_tags
}
# 最新のAmazon Linux 2 AMIを取得
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
# Security Group for Web Server
resource "aws_security_group" "web" {
name_prefix = "${var.project_name}-web-"
vpc_id = module.vpc.vpc_id
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # 本番環境では特定のIPに制限
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(local.common_tags, {
Name = "${var.project_name}-web-sg"
})
}
# EC2 Instance
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
key_name = var.key_pair_name
vpc_security_group_ids = [aws_security_group.web.id]
subnet_id = module.vpc.public_subnet_ids[0]
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from Terraform!</h1>" > /var/www/html/index.html
EOF
tags = merge(local.common_tags, {
Name = "${var.project_name}-web-server"
})
}
実行手順
1. 初期化
terraform init
2. 計画確認
terraform plan -var="key_pair_name=your-key-pair"
3. 実行
terraform apply -var="key_pair_name=your-key-pair"
4. 確認
terraform show
terraform output
ベストプラクティス
1. モジュール化
再利用可能なコンポーネントはモジュールとして分離します。これにより、コードの再利用性と保守性が向上します。
# 良い例:モジュール化されたVPC
module "production_vpc" {
source = "./modules/vpc"
environment = "production"
vpc_cidr = "10.1.0.0/16"
}
module "staging_vpc" {
source = "./modules/vpc"
environment = "staging"
vpc_cidr = "10.2.0.0/16"
}
2. 環境分離
本番、ステージング、開発環境を分離した構成を採用します:
environments/
├── production/
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
├── staging/
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
└── modules/
├── vpc/
├── ec2/
└── rds/
3. 状態ファイルの管理
本番環境では必ずリモートバックエンドを使用し、状態ファイルのロックを有効にします:
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "production/infrastructure.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-state-locks"
}
}
4. 変数の管理
機密情報を含む変数は環境変数またはSecrets Managerから取得します:
# 環境変数
export TF_VAR_db_password="secure_password"
# AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "production/database/password"
}
resource "aws_db_instance" "main" {
password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}
5. タグ付けの標準化
すべてのリソースに一貫したタグ付けを行います:
locals {
common_tags = {
Environment = var.environment
Project = var.project_name
Owner = var.team_name
ManagedBy = "terraform"
CostCenter = var.cost_center
CreatedDate = formatdate("YYYY-MM-DD", timestamp())
}
}

よくある失敗と解決方法
1. 状態ファイルの競合
問題:複数人が同時にterraform apply
を実行し、状態ファイルが破損する。
解決方法:
- DynamoDBを使用した状態ロック機能を有効にする
- CI/CDパイプラインでの自動実行を導入
- チーム内での実行タイミングを調整
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "infrastructure.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-locks" # ロックテーブル
}
}
2. プロバイダーバージョンの不整合
問題:チームメンバー間でプロバイダーバージョンが異なり、動作に差異が発生する。
解決方法:
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.31.0" # 具体的なバージョンを指定
}
}
}
# .terraform.lock.hclをGitにコミット
3. リソースの削除防止
問題:誤って重要なリソースを削除してしまう。
解決方法:
resource "aws_s3_bucket" "important_data" {
bucket = "company-important-data"
lifecycle {
prevent_destroy = true # 削除防止
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
lifecycle {
ignore_changes = [ami] # AMI変更を無視
}
}
4. 大量リソースの同時作成による制限
問題:AWSのAPI制限により、大量のリソース作成が失敗する。
解決方法:
resource "aws_instance" "web" {
count = 100
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
}
}
# パラレリズムを制限
# terraform apply -parallelism=5
5. 機密情報のハードコーディング
問題:パスワードやAPIキーをTerraformファイルに直接記述してしまう。
解決方法:
# ❌ 悪い例
resource "aws_db_instance" "main" {
password = "super_secret_password"
}
# ✅ 良い例
resource "aws_db_instance" "main" {
manage_master_user_password = true # AWS管理パスワード
}
# または
resource "random_password" "db_password" {
length = 16
}
resource "aws_secretsmanager_secret" "db_password" {
name = "database-password"
}
resource "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret.db_password.id
secret_string = random_password.db_password.result
}
テスト戦略
Terraform Testフレームワーク
Terraform 1.6以降では、組み込みテスト機能が利用できます:
# tests/vpc_test.tftest.hcl
run "create_vpc" {
command = plan
assert {
condition = aws_vpc.main.cidr_block == "10.0.0.0/16"
error_message = "VPC CIDR should be 10.0.0.0/16"
}
}
run "validate_subnets" {
command = plan
assert {
condition = length(aws_subnet.public) == 2
error_message = "Should create exactly 2 public subnets"
}
}
外部ツールによるテスト
Terratest(Go)を使用した例:
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestTerraformVPC(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../",
Vars: map[string]interface{}{
"environment": "test",
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vpcId := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcId)
}
CI/CDパイプライン統合
GitHub Actionsでの自動化
# .github/workflows/terraform.yml
name: Terraform CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform Format Check
run: terraform fmt -check -recursive
- name: Terraform Init
run: terraform init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan -no-color
if: github.event_name == 'pull_request'
- name: Terraform Apply
run: terraform apply -auto-approve
if: github.ref == 'refs/heads/main'
運用とモニタリング
ドリフト検出
インフラの設定変更を検出するため、定期的にドリフト検出を実行します:
# ドリフト検出スクリプト
#!/bin/bash
terraform plan -detailed-exitcode
case $? in
0)
echo "No changes detected"
;;
1)
echo "Error occurred"
exit 1
;;
2)
echo "Changes detected - infrastructure has drifted"
# Slackやメール通知
;;
esac
コスト管理
Terraformでのコスト可視化と制御:
# Infracostを使用したコスト見積もり
name: Cost Estimation
on: [pull_request]
jobs:
infracost:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Infracost
uses: infracost/actions/setup@v2
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Generate cost estimate
run: |
infracost breakdown --path . \
--format json \
--out-file /tmp/infracost.json
infracost comment github \
--path /tmp/infracost.json \
--repo $GITHUB_REPOSITORY \
--github-token ${{ github.token }} \
--pull-request ${{ github.event.pull_request.number }}
まとめ
Terraformを使用したInfrastructure as Codeは、現代のクラウド環境管理において必須の技術となっています。本記事で紹介した内容を実践することで、以下の利益を得られます:
- 再現性の確保:同じコードから一貫した環境を構築
- バージョン管理:インフラ変更の履歴管理とロールバック
- 自動化:CI/CDパイプラインとの統合による効率向上
- コスト最適化:リソースの可視化と無駄な支出の削減
- 品質向上:コードレビューによるインフラ設計の改善
2025年現在、ライセンスの変更によりOpenTofuという選択肢も生まれています。組織の方針に応じて適切なツールを選択し、継続的な改善を行いながら、効率的なインフラ管理を実現してください。

インフラ管理の自動化は、開発チーム全体の生産性向上につながる重要な投資です。ぜひ今日から始めてみてください。
関連記事