サーバーの設定をGUIで手動操作していたら、同じ環境を再現できなくなった——こうした経験をお持ちの方は多いのではないでしょうか。Infrastructure as Code(IaC)は、インフラの構成をコードで管理することで、再現性・効率性・安全性を大幅に向上させるアプローチです。
本記事では、IaCの基本概念から、Terraform・Pulumi・AWS CDKという3つの主要ツールの比較、そして実際の導入手順までを体系的に解説します。
Infrastructure as Code(IaC)とは何か
Infrastructure as Code(IaC)とは、サーバー、ネットワーク、データベースなどのインフラリソースの構成を、コード(設定ファイル)として定義・管理する手法です。従来は管理コンソールからの手動操作やシェルスクリプトで行っていた作業を、宣言的なコードで記述します。
手動管理の課題
IaCが必要とされる背景には、手動管理が抱える深刻な課題があります。
再現性の欠如
手動で構築した環境は「手順書どおり」に再現しようとしても、細かな設定の漏れや手順の違いにより、同一の環境を作ることが困難です。開発環境と本番環境の差異が原因で障害が発生するケースは珍しくありません。
スノーフレーク問題
各サーバーが手動で個別に設定されることで、微妙に異なる「雪の結晶(スノーフレーク)」のような状態になります。特定のサーバーだけで発生する問題の調査に多大な時間がかかります。
変更履歴の追跡困難
誰がいつどのような変更を行ったかを追跡する仕組みがないため、障害発生時の原因特定が困難です。
スケーリングの限界
手動操作ではサーバー数が増えるほど管理コストが線形に増加し、ヒューマンエラーのリスクも高まります。
IaCの基本原則
IaCは以下の原則に基づいています。
宣言的なアプローチ
「何をするか(手順)」ではなく「どうあるべきか(あるべき姿)」を記述します。ツールが現在の状態とあるべき姿の差分を自動的に計算し、必要な変更だけを適用します。
べき等性(Idempotency)
同じコードを何度実行しても同じ結果になることが保証されます。「すでに存在するリソースを二重に作ってしまう」といった問題が起きません。
バージョン管理
インフラのコードをGitで管理することで、変更履歴の追跡、コードレビュー、ロールバックが可能になります。
自動化
CI/CDパイプラインと統合することで、コードの変更からインフラの更新までを自動化できます。
IaCツールの全体像と分類
IaCツールは大きく2つのカテゴリに分類できます。目的に応じた適切な選択が重要です。
プロビジョニングツール
クラウドリソース(サーバー、ネットワーク、データベースなど)の作成・管理を担うツールです。
Terraform
HashiCorp社が開発したオープンソースツールで、IaCの事実上のスタンダードです。HCL(HashiCorp Configuration Language)という独自の宣言的言語を使用します。AWS、GCP、Azureなど、主要なクラウドプロバイダーすべてに対応しています。
Pulumi
TypeScript、Python、Go、C#などのプログラミング言語でインフラを定義できるツールです。独自のDSLを覚える必要がなく、既存の言語スキルを活用できる点が特徴です。
AWS CDK(Cloud Development Kit)
AWSが提供するツールで、TypeScript、Python、Javaなどのプログラミング言語でAWSリソースを定義します。内部的にはCloudFormationテンプレートを生成します。
構成管理ツール
プロビジョニング済みのサーバー内部のソフトウェアや設定を管理するツールです。
Ansible
エージェントレスでSSH経由でサーバーを構成管理できるツールです。YAMLで記述するPlaybookの可読性が高く、学習コストが低いのが特徴です。
Chef / Puppet
より大規模な環境の構成管理に使われてきたツールです。コンテナ時代にはプロビジョニングツールの重要性が増しており、構成管理ツールの出番は減少傾向にあります。
現代のクラウドネイティブ環境では、コンテナ化によりサーバー内部の構成管理の必要性が減少し、プロビジョニングツール(Terraform、Pulumi、CDK)が主役となっています。
Terraform入門:最も普及したIaCツール
Terraformは2026年現在、最も広く使われているIaCツールです。基本的な使い方を見ていきましょう。
HCLの基本構文
TerraformはHCL(HashiCorp Configuration Language)を使ってインフラを定義します。
# main.tf - AWSのVPCとEC2インスタンスを定義する例
# プロバイダーの設定
terraform {
required_version = ">= 1.8.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
# 状態ファイルをS3に保存
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "ap-northeast-1"
}
}
provider "aws" {
region = "ap-northeast-1"
}
# VPCの定義
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "main-vpc"
Environment = var.environment
}
}
# パブリックサブネットの定義
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-${count.index + 1}"
}
}
# EC2インスタンスの定義
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
subnet_id = aws_subnet.public[0].id
tags = {
Name = "web-server"
}
}
変数とモジュールによる再利用
Terraformでは変数を使って環境ごとの設定値を外部化し、モジュールでリソース定義を再利用可能にします。
# variables.tf - 変数の定義
variable "environment" {
description = "デプロイ先の環境名"
type = string
default = "production"
}
variable "instance_type" {
description = "EC2インスタンスタイプ"
type = string
default = "t3.micro"
}
variable "allowed_cidr_blocks" {
description = "アクセスを許可するCIDRブロック"
type = list(string)
default = ["0.0.0.0/0"]
}
# モジュールの利用例
module "web_server" {
source = "./modules/web-server"
environment = "production"
instance_type = "t3.small"
vpc_id = aws_vpc.main.id
subnet_ids = aws_subnet.public[*].id
}
module "web_server_staging" {
source = "./modules/web-server"
environment = "staging"
instance_type = "t3.micro"
vpc_id = aws_vpc.main.id
subnet_ids = aws_subnet.public[*].id
}
Terraformの基本ワークフロー
Terraformの操作は3つのコマンドが基本です。
# 1. 初期化:プロバイダーのダウンロードとバックエンドの設定
terraform init
# 2. 計画:現在の状態と設定の差分を確認(実際の変更は行わない)
terraform plan
# 3. 適用:計画された変更を実行
terraform apply
# その他の便利なコマンド
terraform fmt # コードのフォーマット
terraform validate # 設定ファイルの文法チェック
terraform destroy # リソースの削除
terraform state list # 管理中のリソース一覧
terraform planは非常に重要なコマンドです。実際に変更を適用する前に「何が作成され、何が変更され、何が削除されるか」をプレビューできます。本番環境への適用前に必ずplanの結果をレビューしましょう。
Pulumi入門:プログラミング言語でインフラを定義する
Pulumiは、既存のプログラミング言語の知識を活用してインフラを定義できる点が大きな特徴です。
TypeScriptでのインフラ定義
// index.ts - Pulumiで同等のインフラを定義する例
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const environment = config.get("environment") || "production";
const instanceType = config.get("instanceType") || "t3.micro";
// VPCの定義
const vpc = new aws.ec2.Vpc("main-vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsHostnames: true,
enableDnsSupport: true,
tags: {
Name: "main-vpc",
Environment: environment,
},
});
// サブネットの定義(ループで複数作成)
const publicSubnets: aws.ec2.Subnet[] = [];
const azs = aws.getAvailabilityZones({ state: "available" });
for (let i = 0; i < 2; i++) {
const subnet = new aws.ec2.Subnet(`public-subnet-${i}`, {
vpcId: vpc.id,
cidrBlock: `10.0.${i + 1}.0/24`,
availabilityZone: azs.then(az => az.names[i]),
mapPublicIpOnLaunch: true,
tags: {
Name: `public-subnet-${i + 1}`,
},
});
publicSubnets.push(subnet);
}
// EC2インスタンスの定義
const ami = aws.ec2.getAmi({
mostRecent: true,
owners: ["amazon"],
filters: [{
name: "name",
values: ["amzn2-ami-hvm-*-x86_64-gp2"],
}],
});
const webServer = new aws.ec2.Instance("web-server", {
ami: ami.then(a => a.id),
instanceType: instanceType,
subnetId: publicSubnets[0].id,
tags: {
Name: "web-server",
},
});
// 出力
export const vpcId = vpc.id;
export const publicSubnetIds = publicSubnets.map(s => s.id);
export const webServerPublicIp = webServer.publicIp;
Pulumiの利点
既存の言語スキルを活用
TypeScript、Python、Go、C#など、チームが使い慣れた言語でインフラを定義できます。IDEの補完機能、型チェック、リファクタリング機能をフル活用できます。
柔軟なロジック
条件分岐、ループ、関数、クラスなど、プログラミング言語の機能を使って複雑なインフラ構成を表現できます。Terraformでは難しい動的な構成も自然に記述できます。
テストの容易さ
一般的なテストフレームワーク(Jest、pytestなど)を使ってインフラのコードをテストできます。
// Pulumiのユニットテスト例(Jest)
import * as pulumi from "@pulumi/pulumi";
pulumi.runtime.setMocks({
newResource: (args) => ({ id: `${args.name}-id`, state: args.inputs }),
call: (args) => args.inputs,
});
describe("Infrastructure", () => {
let infra: typeof import("./index");
beforeAll(async () => {
infra = await import("./index");
});
test("VPCにEnvironmentタグが設定されている", async () => {
// テストでタグの存在を検証
const vpcId = await new Promise((resolve) =>
infra.vpcId.apply(resolve)
);
expect(vpcId).toBeDefined();
});
});
AWS CDK入門:AWSに特化した高レベルIaC
AWS CDKは、AWSリソースの定義に特化したIaCツールです。高レベルの抽象化により、少ないコードでベストプラクティスに沿ったインフラを構築できます。
CDKの基本構文とConstruct
// lib/web-stack.ts - AWS CDKでの定義例
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecs_patterns from 'aws-cdk-lib/aws-ecs-patterns';
import { Construct } from 'constructs';
export class WebStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPCの作成(サブネットやNAT Gatewayも自動構成)
const vpc = new ec2.Vpc(this, 'MainVpc', {
maxAzs: 2,
natGateways: 1,
});
// ECSクラスターの作成
const cluster = new ecs.Cluster(this, 'WebCluster', {
vpc,
});
// Fargateサービスの作成(ALB付き)
const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(
this, 'WebService', {
cluster,
cpu: 256,
memoryLimitMiB: 512,
desiredCount: 2,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry('nginx:latest'),
containerPort: 80,
},
publicLoadBalancer: true,
}
);
// オートスケーリングの設定
const scaling = fargateService.service.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 10,
});
scaling.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
});
// 出力
new cdk.CfnOutput(this, 'LoadBalancerDNS', {
value: fargateService.loadBalancer.loadBalancerDnsName,
});
}
}
上記のコードでは、VPC、ECSクラスター、Fargateサービス、ALB、オートスケーリングまでを約40行で定義しています。CDKの高レベルConstruct(L2/L3 Construct)が、セキュリティグループやIAMロールなどの関連リソースを自動的に設定してくれるためです。
CDKのワークフロー
# CDKプロジェクトの初期化
cdk init app --language typescript
# CloudFormationテンプレートを生成して差分を確認
cdk diff
# デプロイ
cdk deploy
# リソースの削除
cdk destroy
# 生成されるCloudFormationテンプレートを確認
cdk synth
Terraform・Pulumi・CDKの比較と選び方
3つのツールにはそれぞれ強みがあり、チームの状況やプロジェクトの要件に応じて選択する必要があります。
比較表
主要な観点での比較は以下のとおりです。
言語
Terraformは独自のHCLを使用します。学習が必要ですが、シンプルで読みやすい言語です。PulumiとCDKは汎用プログラミング言語を使用します。
マルチクラウド対応
Terraformは600以上のプロバイダーに対応し、マルチクラウド環境で最も強力です。Pulumiも主要クラウドに対応しています。CDKはAWS専用です(CDKTF経由でTerraformプロバイダーを使う方法もあります)。
学習コスト
Terraformは独自言語の学習が必要ですが、言語自体はシンプルです。PulumiとCDKは既存の言語スキルを活かせますが、各ツール固有の概念(Pulumiのリソース、CDKのConstruct)を学ぶ必要があります。
エコシステム
Terraformのエコシステムが最も成熟しています。公開モジュールの数、ドキュメントの充実度、コミュニティの規模で圧倒的です。
状態管理
Terraformはtfstateファイルで状態を管理します。Pulumiは独自のState管理サービスまたはセルフホストを選択できます。CDKはCloudFormationが状態を管理します。
選定ガイドライン
Terraformを選ぶべきケース
- マルチクラウド環境を管理する場合
- チーム内にIaC経験者がいる場合(Terraformスキルの市場価値が高い)
- 豊富な公開モジュールやドキュメントを活用したい場合
- インフラ専任チームが管理する場合
Pulumiを選ぶべきケース
- チームが特定のプログラミング言語に精通している場合
- 複雑な条件分岐やループが必要な構成がある場合
- インフラのユニットテストを重視する場合
- アプリケーション開発者がインフラも管理する場合
AWS CDKを選ぶべきケース
- AWSのみを使用する場合
- AWSのベストプラクティスに沿った構成を素早く構築したい場合
- 高レベルの抽象化で記述量を減らしたい場合
- CloudFormationの知識がチームにある場合
IaC導入のベストプラクティス
どのツールを選んでも共通する、IaC導入時のベストプラクティスを紹介します。
ディレクトリ構成の設計
Terraformプロジェクトの推奨ディレクトリ構成例です。
infrastructure/
├── modules/ # 再利用可能なモジュール
│ ├── networking/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── compute/
│ └── database/
├── environments/ # 環境ごとの設定
│ ├── production/
│ │ ├── main.tf # モジュールの呼び出し
│ │ ├── terraform.tfvars # 環境固有の変数値
│ │ └── backend.tf # 状態ファイルの保存先
│ ├── staging/
│ └── development/
└── .github/
└── workflows/
└── terraform.yml # CI/CDパイプライン
CI/CDパイプラインとの統合
IaCの変更をPull Requestベースで管理し、CI/CDパイプラインで自動的にplanとapplyを実行する仕組みを構築しましょう。
# .github/workflows/terraform.yml
name: Terraform CI/CD
on:
pull_request:
paths: ['infrastructure/**']
push:
branches: [main]
paths: ['infrastructure/**']
jobs:
plan:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
working-directory: infrastructure/environments/production
- name: Terraform Plan
run: terraform plan -no-color -out=plan.tfplan
working-directory: infrastructure/environments/production
- name: PRにPlan結果をコメント
uses: actions/github-script@v7
with:
script: |
// Plan結果をPRコメントとして投稿するスクリプト
apply:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
working-directory: infrastructure/environments/production
- name: Terraform Apply
run: terraform apply -auto-approve
working-directory: infrastructure/environments/production
セキュリティとガバナンス
IaC導入時に注意すべきセキュリティのポイントです。
シークレットの管理
パスワードやAPIキーをコードに直接記述してはいけません。AWS Secrets Manager、HashiCorp Vaultなどのシークレット管理サービスを使用します。
最小権限の原則
Terraformの実行に使用するIAMロールには、必要最小限の権限のみを付与します。本番環境のapplyは専用のCI/CDパイプラインからのみ実行できるようにしましょう。
ポリシーチェック
Open Policy Agent(OPA)やTerraform Sentinelを使って、セキュリティポリシーに違反するリソース定義を自動的に検出・ブロックします。
状態ファイルの保護
Terraformの状態ファイルにはセンシティブな情報が含まれるため、暗号化が有効なS3バケットなどに保存し、アクセス権限を厳密に管理します。
まとめ
Infrastructure as Codeは、現代のインフラ管理において欠かせないプラクティスです。本記事のポイントを振り返りましょう。
IaCの基本価値
再現性、バージョン管理、自動化を実現し、手動管理の課題を根本的に解決します。
ツールの選択
Terraformはマルチクラウド対応とエコシステムの充実が強み、Pulumiは汎用言語でのインフラ定義が強み、CDKはAWS環境での高レベル抽象化が強みです。
段階的な導入
いきなりすべてのインフラをIaC化するのではなく、新規リソースからコード管理を始め、既存リソースは段階的にインポートしていく方法が現実的です。
CI/CDとの統合
Pull Requestベースのワークフローとパイプラインの自動化により、安全で効率的なインフラ変更プロセスを実現します。
まずは小さなプロジェクトでTerraformを使ってみることをおすすめします。VPCとEC2インスタンス1台の構成からでも、IaCの恩恵を実感できるはずです。そこから徐々に管理範囲を広げていきましょう。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像