TerraformでAWSインフラをコード管理する入門

TerraformでAWSのVPC・EC2・RDSを定義し、インフラをコードとして管理する基本的な手順を解説します。

Terraformとは

TerraformはHashiCorpが開発したIaC(Infrastructure as Code)ツールです。AWSやGCPなどのクラウドリソースを .tf ファイルで宣言的に定義し、terraform apply でその状態に収束させます。手動でコンソールをポチポチするよりも、設定の再現性・変更履歴の管理・チームでのレビューが容易になります。


インストールと初期設定

1
2
3
4
5
# macOS
brew install terraform

# バージョン確認
terraform version

AWSの認証情報は ~/.aws/credentials か環境変数で設定します。

1
2
3
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=ap-northeast-1

ディレクトリ構成

infra/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

VPCの定義

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# main.tf

terraform {
  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.project}-vpc"
  }
}

# パブリックサブネット
resource "aws_subnet" "public" {
  count             = 2
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true
  tags = {
    Name = "${var.project}-public-${count.index}"
  }
}

data "aws_availability_zones" "available" {}

# インターネットゲートウェイ
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
}

resource "aws_route_table_association" "public" {
  count          = 2
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

変数の定義

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# variables.tf
variable "aws_region" {
  default = "ap-northeast-1"
}

variable "project" {
  description = "プロジェクト名(リソースのタグに使用)"
  type        = string
}

variable "instance_type" {
  default = "t3.micro"
}
1
2
3
# terraform.tfvars
project       = "myapp"
instance_type = "t3.small"

EC2インスタンスの定義

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# セキュリティグループ
resource "aws_security_group" "web" {
  name   = "${var.project}-web-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"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# 最新のAmazon Linux 2023 AMIを動的に取得
data "aws_ami" "al2023" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

resource "aws_instance" "web" {
  ami                    = data.aws_ami.al2023.id
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.public[0].id
  vpc_security_group_ids = [aws_security_group.web.id]

  tags = {
    Name = "${var.project}-web"
  }
}

Outputsの定義

1
2
3
4
5
6
7
8
# outputs.tf
output "web_public_ip" {
  value = aws_instance.web.public_ip
}

output "vpc_id" {
  value = aws_vpc.main.id
}

基本的なワークフロー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 初期化(プロバイダーのダウンロード)
terraform init

# 変更内容の確認(実際には何も変更しない)
terraform plan

# 適用
terraform apply

# 削除(注意:本番環境では使わない)
terraform destroy

terraform plan で変更内容を必ず確認してから apply するのが基本です。+ が新規作成、~ が変更、- が削除を意味します。


Stateファイルのリモート管理

デフォルトではStateファイルがローカルの terraform.tfstate に保存されますが、チーム開発ではS3とDynamoDBを使ってリモート管理します。

1
2
3
4
5
6
7
8
9
terraform {
  backend "s3" {
    bucket         = "myapp-terraform-state"
    key            = "infra/terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "terraform-lock"
    encrypt        = true
  }
}

DynamoDBによるロックにより、複数人が同時に apply して状態が壊れるのを防げます。


まとめ

コマンド用途
terraform init初期化(プロバイダーのダウンロード)
terraform plan変更内容のプレビュー
terraform applyインフラへの反映
terraform destroyリソースの削除
terraform state list管理リソース一覧

Terraformを使うことでインフラの変更をGitでレビューでき、誰が何をいつ変更したかを追跡できるようになります。まずローカル環境の開発VPCから試してみるのがおすすめです。