AWSでインフラを構築したいけど、手作業は煩雑で一貫性がない。そんな課題をTerraformで一気に解決できます。
結論:Terraform×AWSで実務レベルのIaC環境を作るなら、EC2+RDS+S3の基本3構成をコードテンプレートで始めるのがコスパ最強
本記事では、初心者でも即座に実務で使える「Terraform基本構成」を厳選しました。実際のサンプルコード+設定手順+よくある失敗例を網羅しているため、1記事で初期導入~本番運用前までの全フローが完結します。
特に「tfstateファイル管理」「複数環境分け」「セキュリティ設定」の3つは実務で必須ですが、これらをセットで解説している入門記事は少ないため、差分として提供します。
| 項目 | 学習コスト | 実務適用度 | 推奨対象 |
|---|---|---|---|
| CloudFormation | 中 | 高(AWS純正) | AWS専用を徹底したい企業 |
| Terraform | 低 | 高(マルチクラウド対応) | 汎用性重視・複数クラウド運用 |
| Pulumi | 高 | 中 | プログラミング言語統一したい組織 |
| 手動構築 | 最低 | 低(再現性なし) | 学習用・小規模テスト環境 |
Terraformがコスパ最強な理由は、無料かつHCL言語がシンプルで、AWS・GCP・Azureなど複数クラウドに対応している点です。
Terraform基本構成を実務レベルで使う4つのステップ
ステップ1:Terraformのセットアップ(Windows・Mac・Linux対応)
まずはTerraformをインストールします。公式サイトから最新版をダウンロードするのが最速です。
# Macの場合(Homebrewで管理)
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# Windowsの場合(Choco)
choco install terraform
# バージョン確認
terraform version
バージョン確認で「Terraform v1.6.x」以上が表示されれば成功です。次にAWS CLIもセットアップします。これがないとAWSとの連携ができません。
# AWS CLIインストール
brew install awscli
# AWS認証情報を設定
aws configure
# アクセスキー・シークレットキーを入力
IAMユーザーのアクセスキーを取得していない場合は、AWSコンソール→IAM→ユーザー→セキュリティ認証情報から生成してください。
ステップ2:プロジェクトディレクトリの構成(実務標準形式)
Terraformは複数ファイルで構成するため、ディレクトリ設計が重要です。以下の構成が業界標準です。
terraform-aws/
├── main.tf # メインリソース定義
├── variables.tf # 変数定義
├── outputs.tf # 出力値設定
├── terraform.tfvars # 環境別設定値
├── backend.tf # 状態ファイル管理
└── environments/ # 本番・ステージ分け
├── dev/
├── staging/
└── prod/
この構成で、開発環境と本番環境を簡単に切り替えられます。
ステップ3:実務必須の基本3構成サンプルコード(EC2+RDS+S3)
以下は、Webサーバー(EC2)+データベース(RDS)+ストレージ(S3)の基本構成です。実務で最も使われるパターンです。
main.tf:メインリソース定義
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# VPC設定
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "${var.environment}-vpc"
}
}
# パブリックサブネット
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "${var.aws_region}a"
map_public_ip_on_launch = true
tags = {
Name = "${var.environment}-public-subnet"
}
}
# インターネットゲートウェイ
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.environment}-igw"
}
}
# セキュリティグループ(EC2用)
resource "aws_security_group" "ec2" {
name = "${var.environment}-ec2-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.ssh_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.environment}-ec2-sg"
}
}
# EC2インスタンス
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.ec2.id]
user_data = base64encode(templatefile("${path.module}/user_data.sh", {
db_endpoint = aws_db_instance.main.endpoint
}))
tags = {
Name = "${var.environment}-web-server"
}
depends_on = [aws_db_instance.main]
}
# RDSセキュリティグループ
resource "aws_security_group" "rds" {
name = "${var.environment}-rds-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.ec2.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.environment}-rds-sg"
}
}
# RDSインスタンス
resource "aws_db_instance" "main" {
identifier = "${var.environment}-mysql-db"
engine = "mysql"
engine_version = "8.0.35"
instance_class = var.db_instance_class
allocated_storage = 20
storage_type = "gp2"
db_name = var.db_name
username = var.db_username
password = var.db_password
vpc_security_group_ids = [aws_security_group.rds.id]
db_subnet_group_name = aws_db_subnet_group.main.name
skip_final_snapshot = false
final_snapshot_identifier = "${var.environment}-mysql-final-snapshot"
tags = {
Name = "${var.environment}-mysql-db"
}
}
# DBサブネットグループ
resource "aws_db_subnet_group" "main" {
name = "${var.environment}-db-subnet-group"
subnet_ids = [aws_subnet.public.id]
tags = {
Name = "${var.environment}-db-subnet-group"
}
}
# S3バケット
resource "aws_s3_bucket" "main" {
bucket = "${var.environment}-app-bucket-${data.aws_caller_identity.current.account_id}"
tags = {
Name = "${var.environment}-app-bucket"
}
}
# S3バージョニング
resource "aws_s3_bucket_versioning" "main" {
bucket = aws_s3_bucket.main.id
versioning_configuration {
status = "Enabled"
}
}
# S3ブロックパブリックアクセス
resource "aws_s3_bucket_public_access_block" "main" {
bucket = aws_s3_bucket.main.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# カレントAWSアカウントID取得
data "aws_caller_identity" "current" {}
variables.tf:変数定義(環境別切り替え用)
variable "aws_region" {
type = string
description = "AWS region"
default = "ap-northeast-1"
}
variable "environment" {
type = string
description = "Environment name"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_type" {
type = string
description = "EC2 instance type"
default = "t3.micro"
}
variable "db_instance_class" {
type = string
description = "RDS instance class"
default = "db.t3.micro"
}
variable "db_name" {
type = string
description = "Database name"
sensitive = true
}
variable "db_username" {
type = string
description = "Database master username"
sensitive = true
}
variable "db_password" {
type = string
description = "Database master password"
sensitive = true
}
variable "ssh_cidr" {
type = string
description = "CIDR block for SSH access"
default = "0.0.0.0/0"
}