TerraformでシンプルなAWS構成を作ろう

このハンズオンでは、これまで学んだTerraformの基本を活用して、実際にAWSインフラを構築する方法を体験します。

  • VPC、サブネット、インターネットゲートウェイの作成
  • ルートテーブルの設定
  • セキュリティグループの作成
  • EC2インスタンスの起動
  • リソース間の参照を活用した構成

1. 事前準備

この講座のハンズオンでは、以下のツールやアカウントが必要です。まだ準備できていない場合は、リンク先の手順に沿って準備をお願いします。

2. ハンズオンの概要

このハンズオンでは、AWS上に基本的なネットワーク構成とEC2インスタンスをTerraformで構築します。AWS講座で手動で作成したものと同様の構成を、コードで管理する方法を学びます。

2.1 構成図

このハンズオンで構築するAWSインフラの全体像を以下に示します。

構成図の各要素について説明します。

まず、terraform-vpc(CIDR: 10.0.0.0/16)がネットワーク全体の土台となるVPCです。このVPCの中に、すべてのリソースを配置します。

VPCの中には、terraform-public-subnet(CIDR: 10.0.1.0/24)というパブリックサブネットを作成します。パブリックサブネットとは、インターネットからアクセス可能なサブネットのことです。

VPCとインターネットの間には、terraform-igw(インターネットゲートウェイ)を配置します。インターネットゲートウェイは、VPC内のリソースがインターネットと通信するための出入り口です。

terraform-public-rt(ルートテーブル)は、パブリックサブネット内のトラフィックの行き先を決定します。すべてのトラフィック(0.0.0.0/0)をインターネットゲートウェイに向けるルートを設定することで、サブネットがインターネットにアクセスできるようになります。

パブリックサブネットの中には、terraform-ec2-sg(セキュリティグループ)で保護されたterraform-ec2(EC2インスタンス)を配置します。セキュリティグループはファイアウォールの役割を果たし、SSH(ポート22)とHTTP(ポート80)の受信のみを許可します。

このハンズオンでは、上記の6つのリソースをTerraformのコードで1つずつ作成していきます。

3. プロジェクトの準備

3.1 作業ディレクトリの作成

任意の場所にterraform-aws-basicフォルダを作成し、Visual Studio Codeの「ファイル」→「フォルダーを開く」から、作成したterraform-aws-basicフォルダを開きます。

terraform-aws-basic  ← このフォルダを作成

4. プロバイダーの設定

最初に、AWSプロバイダーを設定するmain.tfファイルを作成します。Visual Studio Codeのエクスプローラーでterraform-aws-basicフォルダを右クリックし、「新しいファイル」を選択してmain.tfという名前で作成してください。

terraform-aws-basic
└── main.tf  ← このファイルを作成

作成したファイルに以下の内容を記述して保存します。

# AWSプロバイダーの設定
provider "aws" {
  region = "ap-northeast-1"
}

5. Terraformの初期化

プロバイダーの設定が完了したら、Terraformを初期化します。ターミナルでterraform-aws-basicフォルダに移動し、以下のコマンドを実行してください。

terraform init

以下のような出力が表示されれば成功です。

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v5.x.x...
- Installed hashicorp/aws v5.x.x (signed by HashiCorp)

Terraform has been successfully initialized!
⚠️ エラーが出る場合
「error configuring Terraform AWS Provider」などのエラーが出る場合、AWS CLIの認証情報が正しく設定されているか確認してください。aws configureコマンドで設定を確認・更新できます。

これで、コードを追加するたびにterraform planで差分を確認し、terraform applyで反映できる状態になりました。

6. VPCの作成

6.1 コードの記載

ここでは、AWSのネットワークの土台となるVPC(Virtual Private Cloud)を作成します。Terraformでは、resource "aws_vpc"を使ってVPCを定義します。以下の内容をmain.tfに追記してください。

# ====================
# VPC
# ====================
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "terraform-vpc"
  }
}

今回設定した内容について解説します。

設定項目 設定内容 設定の基準
cidr_block VPCのIPv4 CIDRブロック 10.0.0.0/16 約65,000個のIPアドレスを確保できる範囲
enable_dns_hostnames DNSホスト名の有効化 true EC2インスタンスにDNSホスト名を付与するため
enable_dns_support DNS解決の有効化 true VPC内でDNS解決を有効にするため
tags.Name AWSコンソールに表示される名前タグ terraform-vpc リソースを識別するため

6.2 差分の確認

terraform planで作成されるリソースを確認します。

terraform plan

以下のような実行結果が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags                                 = {
          + "Name" = "terraform-vpc"
        }
      + tags_all                             = {
          + "Name" = "terraform-vpc"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

+ createの記号が示すとおり、VPCが1つ新規作成されることがわかります。cidr_blockenable_dns_hostnamesなど、コードで指定した値が反映されていることを確認してください。(known after apply)と表示されている項目は、実際にリソースが作成された後にAWSから割り当てられる値です。

6.3 AWSへの反映

問題なければterraform applyで反映します。

terraform apply

確認プロンプトが表示されたら、yesと入力して実行します。以下のように、VPCが1件作成されたことを確認します。

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

6.4 AWSコンソールでの確認

AWSマネジメントコンソールで VPC のダッシュボードを開き、「terraform-vpc」という名前のVPCが作成されていることを確認してください。VPCを選択し、以下の項目がコードで設定した内容と一致していることも確認しましょう。

  • IPv4 CIDR が「10.0.0.0/16」であること
  • DNSホスト名 が「有効」であること
  • DNS解決 が「有効」であること

7. サブネットの作成

7.1 コードの記載

ここでは、VPC内にEC2インスタンスを配置するためのパブリックサブネットを作成します。Terraformでは、resource "aws_subnet"を使ってサブネットを定義します。以下の内容をmain.tfに追記してください。

# ====================
# サブネット
# ====================
# パブリックサブネット
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "terraform-public-subnet"
  }
}

今回設定した内容について解説します。

設定項目 設定内容 設定の基準
vpc_id サブネットを作成するVPC aws_vpc.main.id 作成したVPCに所属させるため(リソース参照)
cidr_block サブネットのIPv4 CIDRブロック 10.0.1.0/24 約250個のIPアドレスを確保できる範囲
availability_zone サブネットを配置するアベイラビリティゾーン ap-northeast-1a 東京リージョンのAZを指定
map_public_ip_on_launch パブリックIP自動割り当て true EC2起動時に自動でパブリックIPを付与するため
tags.Name AWSコンソールに表示される名前タグ terraform-public-subnet リソースを識別するため
💡 ポイント
vpc_id = aws_vpc.main.idの部分で、先ほど定義したVPCのIDを参照しています。前の講座で学んだ「リソース間の参照」を活用しています。Terraformはこの参照を解析し、VPCが作成されてからサブネットを作成します。

7.2 差分の確認

terraform planで差分を確認します。

terraform plan

以下のような実行結果が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_subnet.public will be created
  + resource "aws_subnet" "public" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = true
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "terraform-public-subnet"
        }
      + tags_all                                       = {
          + "Name" = "terraform-public-subnet"
        }
      + vpc_id                                         = "vpc-xxxxxxxxxxxxxxxxx"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

パブリックサブネットが1つ作成されることがわかります。map_public_ip_on_launch = trueが設定されていることを確認してください。また、vpc_idには先ほど作成したVPCのIDが自動的に設定されています。

7.3 AWSへの反映

問題なければterraform applyで反映します。

terraform apply

確認プロンプトが表示されたら、yesと入力して実行します。以下のように、サブネットが1件作成されたことを確認します。

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

7.4 AWSコンソールでの確認

AWSマネジメントコンソールで VPC > サブネット を開き、「terraform-public-subnet」が作成されていることを確認してください。サブネットを選択し、以下の項目がコードで設定した内容と一致していることも確認しましょう。

  • VPC が「terraform-vpc」であること
  • IPv4 CIDR が「10.0.1.0/24」であること
  • アベイラビリティゾーン が「ap-northeast-1a」であること
  • パブリックIPv4アドレスを自動割り当て が「はい」であること

8. インターネットゲートウェイの作成

8.1 コードの記載

ここでは、VPCからインターネットへ接続するためのインターネットゲートウェイを作成します。Terraformでは、resource "aws_internet_gateway"を使ってインターネットゲートウェイを定義します。以下の内容をmain.tfに追記してください。

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

  tags = {
    Name = "terraform-igw"
  }
}

今回設定した内容について解説します。

設定項目 設定内容 設定の基準
vpc_id アタッチするVPC aws_vpc.main.id 作成したVPCにインターネットゲートウェイをアタッチするため
tags.Name AWSコンソールに表示される名前タグ terraform-igw リソースを識別するため

8.2 差分の確認

terraform planで差分を確認します。

terraform plan

以下のような実行結果が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_internet_gateway.main will be created
  + resource "aws_internet_gateway" "main" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "Name" = "terraform-igw"
        }
      + tags_all = {
          + "Name" = "terraform-igw"
        }
      + vpc_id   = "vpc-xxxxxxxxxxxxxxxxx"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

インターネットゲートウェイが1つ作成されることがわかります。vpc_idに先ほど作成したVPCのIDが設定されていることを確認してください。

8.3 AWSへの反映

問題なければterraform applyで反映します。

terraform apply

確認プロンプトが表示されたら、yesと入力して実行します。以下のように、インターネットゲートウェイが1件作成されたことを確認します。

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

8.4 AWSコンソールでの確認

AWSマネジメントコンソールで VPC > インターネットゲートウェイ を開き、「terraform-igw」が作成されていることを確認してください。以下の項目がコードで設定した内容と一致していることも確認しましょう。

  • 状態 が「Attached」であること
  • VPC ID が「terraform-vpc」のVPCであること

9. ルートテーブルの作成

9.1 コードの記載

ここでは、パブリックサブネットからインターネットへの通信経路を定義するルートテーブルを作成し、サブネットに関連付けます。Terraformでは、resource "aws_route_table"でルートテーブルを定義し、resource "aws_route_table_association"でサブネットとの関連付けを行います。以下の内容をmain.tfに追記してください。

# ====================
# ルートテーブル
# ====================
# パブリックサブネット用ルートテーブル
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
  }

  tags = {
    Name = "terraform-public-rt"
  }
}

# ルートテーブルとサブネットの関連付け
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

今回設定した内容について解説します。

設定項目 設定内容 設定の基準
vpc_id ルートテーブルを作成するVPC aws_vpc.main.id 作成したVPCに所属させるため
route.cidr_block ルートの送信先(AWSコンソールの「送信先」) 0.0.0.0/0 すべてのトラフィックを対象にするため
route.gateway_id ルートのターゲット(AWSコンソールの「ターゲット」) aws_internet_gateway.main.id インターネットゲートウェイに転送するため
tags.Name AWSコンソールに表示される名前タグ terraform-public-rt リソースを識別するため
subnet_id 関連付けるサブネット aws_subnet.public.id パブリックサブネットにルートテーブルを適用するため
route_table_id 関連付けるルートテーブル aws_route_table.public.id 作成したルートテーブルを使用するため
📝 ルートテーブルについて
ルートテーブルは、サブネット内のトラフィックの送信先を決定します。0.0.0.0/0(すべての宛先)をインターネットゲートウェイに向けることで、パブリックサブネットからインターネットへのアクセスが可能になります。

9.2 差分の確認

terraform planで差分を確認します。

terraform plan

以下のような実行結果が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_route_table.public will be created
  + resource "aws_route_table" "public" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                 = "0.0.0.0/0"
              + gateway_id                 = (known after apply)
              + carrier_gateway_id         = ""
              + core_network_arn           = ""
              + destination_prefix_list_id = ""
              + egress_only_gateway_id     = ""
              + ipv6_cidr_block            = ""
              + local_gateway_id           = ""
              + nat_gateway_id             = ""
              + network_interface_id       = ""
              + transit_gateway_id         = ""
              + vpc_endpoint_id            = ""
              + vpc_peering_connection_id  = ""
            },
        ]
      + tags             = {
          + "Name" = "terraform-public-rt"
        }
      + tags_all         = {
          + "Name" = "terraform-public-rt"
        }
      + vpc_id           = "vpc-xxxxxxxxxxxxxxxxx"
    }

  # aws_route_table_association.public will be created
  + resource "aws_route_table_association" "public" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = "subnet-xxxxxxxxxxxxxxxxx"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

ルートテーブルとサブネットの関連付けの2つが作成されることがわかります。ルートテーブルのroutecidr_block = "0.0.0.0/0"が設定されており、すべてのトラフィックがインターネットゲートウェイに向けられることを確認してください。

9.3 AWSへの反映

問題なければterraform applyで反映します。

terraform apply

確認プロンプトが表示されたら、yesと入力して実行します。以下のように、ルートテーブルとサブネットの関連付けの2件が作成されたことを確認します。

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

9.4 AWSコンソールでの確認

AWSマネジメントコンソールで VPC > ルートテーブル を開き、「terraform-public-rt」が作成されていることを確認してください。ルートテーブルを選択し、以下の項目がコードで設定した内容と一致していることも確認しましょう。

  • VPC が「terraform-vpc」であること
  • 「ルート」タブで、送信先0.0.0.0/0のターゲットが「terraform-igw」(インターネットゲートウェイ)になっていること
  • 「サブネットの関連付け」タブで、「terraform-public-subnet」が関連付けられていること

10. セキュリティグループの作成

10.1 コードの記載

ここでは、EC2インスタンスへの通信を制御するセキュリティグループを作成します。Terraformでは、resource "aws_security_group"を使ってセキュリティグループを定義し、ingressブロックで受信ルール、egressブロックで送信ルールを設定します。以下の内容をmain.tfに追記してください。

# ====================
# セキュリティグループ
# ====================
resource "aws_security_group" "ec2" {
  name        = "terraform-ec2-sg"
  description = "Security group for EC2 instance"
  vpc_id      = aws_vpc.main.id

  # SSH接続を許可
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow SSH"
  }

  # HTTP接続を許可
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow HTTP"
  }

  # アウトバウンド通信を全て許可
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow all outbound traffic"
  }

  tags = {
    Name = "terraform-ec2-sg"
  }
}

今回設定した内容について解説します。

設定項目 設定内容 設定の基準
name セキュリティグループ名(AWSコンソールの「セキュリティグループ名」) terraform-ec2-sg リソースを識別するため
description セキュリティグループの説明(AWSコンソールの「説明」) Security group for EC2 instance セキュリティグループの用途を明示するため
vpc_id セキュリティグループを作成するVPC aws_vpc.main.id 作成したVPCに所属させるため
ingress(SSH) インバウンドルール(AWSコンソールの「インバウンドルール」) ポート22, TCP, 0.0.0.0/0 SSH接続を許可するため
ingress(HTTP) インバウンドルール(AWSコンソールの「インバウンドルール」) ポート80, TCP, 0.0.0.0/0 HTTP接続を許可するため
egress アウトバウンドルール(AWSコンソールの「アウトバウンドルール」) 全ポート, 全プロトコル, 0.0.0.0/0 すべてのアウトバウンド通信を許可するため
tags.Name AWSコンソールに表示される名前タグ terraform-ec2-sg リソースを識別するため
💡 ポイント
このハンズオンでは簡略化のため、SSH接続を0.0.0.0/0(全世界)から許可しています。本番環境では、自分のIPアドレスのみを許可するなど、適切なセキュリティ設定を行ってください。

10.2 差分の確認

terraform planで差分を確認します。

terraform plan

以下のような実行結果が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_security_group.ec2 will be created
  + resource "aws_security_group" "ec2" {
      + arn                    = (known after apply)
      + description            = "Security group for EC2 instance"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = "Allow all outbound traffic"
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = "Allow HTTP"
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = "Allow SSH"
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
            },
        ]
      + name                   = "terraform-ec2-sg"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags                   = {
          + "Name" = "terraform-ec2-sg"
        }
      + tags_all               = {
          + "Name" = "terraform-ec2-sg"
        }
      + vpc_id                 = "vpc-xxxxxxxxxxxxxxxxx"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

セキュリティグループが1つ作成されることがわかります。ingressにSSH(ポート22)とHTTP(ポート80)の2つのインバウンドルール、egressにすべてのアウトバウンドトラフィックを許可するルールが設定されていることを確認してください。

10.3 AWSへの反映

問題なければterraform applyで反映します。

terraform apply

確認プロンプトが表示されたら、yesと入力して実行します。以下のように、セキュリティグループが1件作成されたことを確認します。

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

10.4 AWSコンソールでの確認

AWSマネジメントコンソールで EC2 > セキュリティグループ を開き、「terraform-ec2-sg」が作成されていることを確認してください。セキュリティグループを選択し、以下の項目がコードで設定した内容と一致していることも確認しましょう。

  • セキュリティグループ名 が「terraform-ec2-sg」であること
  • 説明 が「Security group for EC2 instance」であること
  • VPC が「terraform-vpc」であること
  • 「インバウンドルール」タブで、SSH(ポート22, TCP, ソース0.0.0.0/0)が許可されていること
  • 「インバウンドルール」タブで、HTTP(ポート80, TCP, ソース0.0.0.0/0)が許可されていること
  • 「アウトバウンドルール」タブで、すべてのトラフィック(全プロトコル, 送信先0.0.0.0/0)が許可されていること

11. EC2インスタンスの作成

11.1 コードの記載

ここでは、パブリックサブネット上にEC2インスタンス(仮想サーバ)を作成します。Terraformでは、resource "aws_instance"を使ってEC2インスタンスを定義します。以下の内容をmain.tfに追記してください。

# ====================
# EC2インスタンス
# ====================
resource "aws_instance" "web" {
  ami                    = "ami-088b486f20fab3f0e"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.public.id
  vpc_security_group_ids = [aws_security_group.ec2.id]

  tags = {
    Name = "terraform-ec2"
  }
}
💡 ポイント
このハンズオンではAMI IDを直接指定しています。AMI IDはリージョンや更新によって変わるため、もしEC2の作成時にAMIが見つからないエラーが出た場合は、AWSコンソールの EC2 > AMIカタログ で「Amazon Linux 2023」の最新のAMI IDを確認し、値を置き換えてください。この後の講座で学ぶデータソース(data source)を使うと、最新のAMI IDを動的に取得できるため、実際のプロジェクトではデータソースを使うのが適切です。

今回設定した内容について解説します。

設定項目 設定内容 設定の基準
ami AMI ID(AWSコンソールの「Amazon マシンイメージ(AMI)」) ami-088b486f20fab3f0e Amazon Linux 2023を使用するため(AMI IDは環境により異なる)
instance_type インスタンスタイプ(AWSコンソールの「インスタンスタイプ」) t2.micro 無料利用枠の範囲内
subnet_id 配置先サブネット(AWSコンソールの「サブネット」) aws_subnet.public.id パブリックサブネットに配置するため
vpc_security_group_ids 適用するセキュリティグループ(AWSコンソールの「セキュリティグループ」) [aws_security_group.ec2.id] 作成したセキュリティグループを適用するため
tags.Name AWSコンソールに表示される名前タグ terraform-ec2 リソースを識別するため

12. 出力の定義

12.1 コードの記載

ここでは、作成したリソースのIDやIPアドレスなどの情報をterraform applyの実行結果に表示するための出力(output)を定義します。Terraformでは、outputブロックを使って出力値を定義します。以下の内容をmain.tfに追記してください。

# ====================
# 出力
# ====================
output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.main.id
}

output "public_subnet_id" {
  description = "Public Subnet ID"
  value       = aws_subnet.public.id
}

output "ec2_public_ip" {
  description = "EC2 Public IP"
  value       = aws_instance.web.public_ip
}

output "ec2_instance_id" {
  description = "EC2 Instance ID"
  value       = aws_instance.web.id
}

12.2 差分の確認

terraform planで差分を確認します。

terraform plan

以下のような実行結果が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami                                  = "ami-088b486f20fab3f0e"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = "subnet-xxxxxxxxxxxxxxxxx"
      + tags                                 = {
          + "Name" = "terraform-ec2"
        }
      + tags_all                             = {
          + "Name" = "terraform-ec2"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_destroy         = false
      + vpc_security_group_ids               = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + ec2_instance_id  = (known after apply)
  + ec2_public_ip    = (known after apply)
  + public_subnet_id = "subnet-xxxxxxxxxxxxxxxxx"
  + vpc_id           = "vpc-xxxxxxxxxxxxxxxxx"

EC2インスタンスが1つ作成されることがわかります。amiにはAmazon Linux 2023のAMI IDが、instance_typeにはt2.microが、subnet_idにはパブリックサブネットのIDが設定されていることを確認してください。末尾のChanges to Outputsには、outputで定義した出力値が表示されています。

12.3 AWSへの反映

問題なければterraform applyで反映します。

terraform apply

確認プロンプトが表示されたら、yesと入力して実行します。以下のように、EC2インスタンスが1件作成され、定義した出力が表示されることを確認します。

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

ec2_instance_id = "i-xxxxxxxxxxxxxxxxx"
ec2_public_ip = "xx.xx.xx.xx"
public_subnet_id = "subnet-xxxxxxxxxxxxxxxxx"
vpc_id = "vpc-xxxxxxxxxxxxxxxxx"

12.4 AWSコンソールでの確認

AWSマネジメントコンソールで EC2 > インスタンス を開き、「terraform-ec2」が作成されていることを確認してください。インスタンスを選択し、以下の項目がコードで設定した内容と一致していることも確認しましょう。

  • AMI が「Amazon Linux 2023」であること
  • インスタンスタイプ が「t2.micro」であること
  • サブネット が「terraform-public-subnet」であること
  • セキュリティグループ が「terraform-ec2-sg」であること
  • パブリックIPv4アドレス が割り当てられていること

💡 ポイント
ec2_public_ipに表示されたIPアドレスを使って、EC2インスタンスに接続できます。ただし、このハンズオンではキーペアを設定していないため、SSHログインはできません。接続が必要な場合は、EC2 Instance Connectやセッションマネージャーを使用してください。

Terraformでも現在の状態を確認できます。terraform showコマンドで、管理しているすべてのリソースの詳細を表示できます。

terraform show

特定の出力だけを確認したい場合は、以下のコマンドを使用します。

terraform output ec2_public_ip

13. リソースの変更

Terraformの強みは、コードを変更するだけでインフラを更新できることです。試しにEC2インスタンスの名前を変更してみましょう。

13.1 コードの変更

main.tfのEC2インスタンスのタグを以下のように編集します。Nameの値を"terraform-ec2"から"terraform-web-server"に変更してください。

  tags = {
    Name = "terraform-web-server"
  }

13.2 差分の確認

変更を保存したら、terraform planで差分を確認します。

terraform plan

以下のような実行結果が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.web will be updated in-place
  ~ resource "aws_instance" "web" {
        id                                   = "i-xxxxxxxxxxxxxxxxx"
      ~ tags                                 = {
          ~ "Name" = "terraform-ec2" -> "terraform-web-server"
        }
      ~ tags_all                             = {
          ~ "Name" = "terraform-ec2" -> "terraform-web-server"
        }
        # (30 unchanged attributes hidden)
        # (8 unchanged blocks hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

~ update in-placeの記号は、リソースを削除・再作成せず、その場で更新されることを示しています。"terraform-ec2" -> "terraform-web-server"と表示され、名前タグだけが変更されることがわかります。

13.3 AWSへの反映

問題なければterraform applyで反映します。

terraform apply

確認プロンプトが表示されたら、yesと入力して実行します。以下のように、1件のリソースが変更されたことを確認します。

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

13.4 AWSコンソールでの確認

AWSマネジメントコンソールで EC2 > インスタンス を開き、インスタンスの名前が「terraform-web-server」に変更されていることを確認してください。

このように、Terraformではコードを変更してterraform planで差分を確認し、terraform applyで反映するだけで、インフラの変更が可能です。

14. 不要リソースの削除

ハンズオンが終わったら、課金を避けるため、作成したリソースを削除します。

terraform destroy

確認プロンプトが表示されたら、yesと入力して実行します。

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

削除が完了すると、以下のように表示されます。

Destroy complete! Resources: 6 destroyed.
💡 ポイント
terraform destroyは、Terraformで管理しているすべてのリソースを削除します。Terraformはリソース間の依存関係を理解しているため、正しい順序(EC2 → サブネット → VPCなど)で削除を行います。

14.1 作業ディレクトリの削除(任意)

プロジェクトフォルダを削除する場合は、以下のコマンドを実行します。

rm -rf ~/terraform-aws-basic

15. まとめ

このハンズオンでは、Terraformを使ってAWSの基本的なインフラを構築する方法を体験しました。

  • VPCサブネットインターネットゲートウェイをTerraformで定義した
  • ルートテーブルでパブリックサブネットからインターネットへのアクセスを設定した
  • セキュリティグループでEC2への通信を制御した
  • リソース間の参照を活用して、依存関係のあるリソースを作成した
  • コードを追加するたびにterraform planで差分を確認し、terraform applyで反映、AWSコンソールで実際のリソースを確認する流れを体験した
  • terraform plan~ update in-placeで、リソースを再作成せずに変更できることを確認した
  • terraform destroyでリソースを一括削除した

16. 次のステップ

🎉 おめでとうございます!TerraformでシンプルなAWS構成(VPC・EC2)を構築するところまで体験できました。

ここまでで学んだ内容は、Terraformを扱ううえでの基本中の基本です。ここから先は、より実践的な内容に進みます。

講座名 学べること
制御構文 count・for_each・lifecycleによる繰り返しと制御
モジュール モジュールによるコードの再利用
既存リソースを取り込む terraform importによる既存リソースのコード化
リモートバックエンド管理 S3・DynamoDBを使ったtfstateの共有管理
TerraformでWebアプリをデプロイしよう Terraformによる本格的なWebアプリインフラの構築

実務で通用するTerraformスキルを身につけたい方は、ぜひ次のステップに進んでみてください。