0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS初心者がTerraformでEC2とVPCを作って壊すまでの最短ハンズオン

Last updated at Posted at 2025-12-01

はじめに

はじめまして。Webアプリケーションのエンジニアをやっているえびです。
Terraformを使って見たかったのですが、なかなか機会がなくチャレンジできていませんでしたが、今度実務で使いそうなのでキャッチアップを兼ねてチュートリアルと見つけたコマンドをまとめようと思いました。
後発者の方の参考になりますと幸いです。

Terraform とは

Terraformは、HashiCorp社が開発したインフラストラクチャを安全かつ効率的に構築、変更、バージョン管理できるコードツールです。コンピューティングインスタンス、ストレージ、ネットワークといった低レベルコンポーネントから、DNSエントリやSaaS機能といった高レベルコンポーネントまで、あらゆるインフラストラクチャの構築、変更、バージョン管理をサポートします。

部分引用: https://developer.hashicorp.com/terraform

つまりIaCの一種です。

IaCとは

インフラ構築のコード化により、自動で環境構築をしてくれるため、手作業による人為的なミスを削減することが可能です。

引用: https://www.ntt-tx.co.jp/column/iac/230815/

チュートリアルをやってみる。

前提

AWSコマンドとterraformコマンドインストール

Dockerfileに以下を追加してリビルド

# AWS CLI
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
    && unzip awscliv2.zip \
    && ./aws/install \
    && rm -rf awscliv2.zip aws

# Terraform
RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg \
    && echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com bookworm main" > /etc/apt/sources.list.d/hashicorp.list \
    && apt-get update \
    && apt-get install -y terraform \
    && rm -rf /var/lib/apt/lists/*

簡単なEC2単体構成を作成

terraform.tfを作成

terraform {
  required_providers {
    aws = {
      source   = "hashicorp/aws"
      version = "~> 5.92"
    }
  }

  required_version = ">= 1.2"
}

main.tfを作成

provider "aws" {
  region = "us-west-2"
}

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "app_server" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"

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

ファイルをフォーマット

terraform fmt

Terraformワークスペースを初期化

main.tfと同じディレクトリで以下のコマンドを実行

$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.92"...
- Installing hashicorp/aws v5.98.0...
- Installed hashicorp/aws v5.98.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

内容を検証する

$ terraform validate
Success! The configuration is valid.

リソースを作成する

node@7621048e52f3:~/front/deploy (feature/#11) $ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0fe9a26b158ea885a]

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.app_server will be created
  + resource "aws_instance" "app_server" {
      + ami                                  = "ami-0fe9a26b158ea885a"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + enable_primary_ipv6                  = (known after apply)
      + force_destroy                        = false
      + 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_group_id                   = (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)
      + region                               = "us-west-2"
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "learn-terraform"
        }
      + tags_all                             = {
          + "Name" = "learn-terraform"
        }
      + tenancy                              = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification (known after apply)

      + cpu_options (known after apply)

      + ebs_block_device (known after apply)

      + enclave_options (known after apply)

      + ephemeral_block_device (known after apply)

      + instance_market_options (known after apply)

      + maintenance_options (known after apply)

      + metadata_options (known after apply)

      + network_interface (known after apply)

      + primary_network_interface (known after apply)

      + private_dns_name_options (known after apply)

      + root_block_device (known after apply)
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes ←yesと入力する

aws_instance.app_server: Creating...

ワークスペースのリソースのリストを確認

$ terraform state list
data.aws_ami.ubuntu
aws_instance.app_server

変数と出力

variables.tfを作成

variable "instance_name" {
  description = "Value of the EC2 instance's Name tag."
  type        = string
  default     = "learn-terraform"
}

variable "instance_type" {
  description = "The EC2 instance's type."
  type        = string
  default     = "t2.micro"
}

main.tfの以下の行を追加する

resource "aws_instance" "app_server" {
   ami           = data.aws_ami.ubuntu.id
-  instance_type = "t2.micro"
+  instance_type = var.instance_type

  tags = {
   Name = "learn-terraform"
   Name = var.instance_name
  }
}

outputs.tfを作成

output "instance_hostname" {
  description = "Private DNS name of the EC2 instance."
  value       = aws_instance.app_server.private_dns
}

変更を反映

$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0026a04369a3093cc]
aws_instance.app_server: Refreshing state... [id=i-0c636e158c30e48f9]

Changes to Outputs:
  + instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


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

Outputs:

instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"

変更を確認

$ terraform output
instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"

モジュールブロック

main.tfを修正

下記を追記

+ module "vpc" {
+  source  = "terraform-aws-modules/vpc/aws"
+  version = "5.19.0"
+
+  name = "example-vpc"
+  cidr = "10.0.0.0/16"
+
+  azs             = ["us-west-2a", "us-west-2b", "us-west-2c"]
+  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
+  public_subnets  = ["10.0.101.0/24"]
+
+  enable_dns_hostnames    = true
+ }

下記2行を追加

resource "aws_instance" "app_server" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type

+  vpc_security_group_ids = [module.vpc.default_security_group_id]
+  subnet_id              = module.vpc.private_subnets[0]

  tags = {
    Name = var.instance_name
  }
}

VPCモジュールをインストール

$ terraform init
Initializing the backend...
Initializing modules...
Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 5.19.0 for vpc...
- vpc in .terraform/modules/vpc
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.98.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

変更を計画して適用

$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0026a04369a3093cc]
aws_instance.app_server: Refreshing state... [id=i-0c636e158c30e48f9]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.app_server must be replaced
-/+ resource "aws_instance" "app_server" {
      ~ arn                                  = "arn:aws:ec2:us-west-2:949008909725:instance/i-0c636e158c30e48f9" -> (known after apply)
      ~ associate_public_ip_address          = true -> (known after apply)
      ~ availability_zone                    = "us-west-2b" -> (known after apply)

##...

        }
    }

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

Changes to Outputs:
  ~ instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal" -> (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.app_server: Destroying... [id=i-0fbb487cbdf2eb6ed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m10s elapsed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m20s elapsed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m30s elapsed]
aws_instance.app_server: Destruction complete after 31s
module.vpc.aws_vpc.this[0]: Creating...
module.vpc.aws_vpc.this[0]: Still creating... [00m10s elapsed]
module.vpc.aws_vpc.this[0]: Creation complete after 12s [id=vpc-01e157ec1af2d7314]
module.vpc.aws_subnet.private[0]: Creating...
module.vpc.aws_route_table.private[1]: Creating...
module.vpc.aws_subnet.private[1]: Creating...
module.vpc.aws_route_table.private[0]: Creating...
module.vpc.aws_default_route_table.default[0]: Creating...
module.vpc.aws_subnet.public[0]: Creating...
module.vpc.aws_internet_gateway.this[0]: Creating...
module.vpc.aws_default_security_group.this[0]: Creating...

## ...

module.vpc.aws_route_table_association.private[1]: Creation complete after 0s [id=rtbassoc-0ea0b41646a73659c]
module.vpc.aws_route_table_association.private[0]: Creation complete after 0s [id=rtbassoc-0d51502051aa2c2af]
module.vpc.aws_default_network_acl.this[0]: Creation complete after 2s [id=acl-03b6b08c6f6a81e4a]
module.vpc.aws_route.public_internet_gateway[0]: Creation complete after 1s [id=r-rtb-0cde73c077eadf7e61080289494]
module.vpc.aws_default_security_group.this[0]: Creation complete after 3s [id=sg-04f350f66843618db]
module.vpc.aws_subnet.public[0]: Creation complete after 3s [id=subnet-02a7d5d6b6e8742d6]
module.vpc.aws_route_table_association.public[0]: Creating...
module.vpc.aws_route_table_association.public[0]: Creation complete after 1s [id=rtbassoc-03db7fad8ba24eca0]
aws_instance.app_server: Still creating... [00m10s elapsed]
aws_instance.app_server: Creation complete after 13s [id=i-0226232d8b6e9eea6]

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

Outputs:

instance_hostname = "ip-10-0-1-75.us-west-2.compute.internal"

リソース削除

main.tfを修正

main.tfのresourceをコメントアウトで囲います

+ /*
resource "aws_instance" "app_server" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type

  vpc_security_group_ids = [module.vpc.default_security_group_id]
  subnet_id              = module.vpc.private_subnets[0]

  tags = {
    Name = var.instance_name
  }
}
+ */

outputs.tfを修正

+ /*
output "instance_hostname" {
  description = "Private DNS name of the EC2 instance."
  value       = aws_instance.app_server.private_dns
}
+ */

削除を適用

$ terraform apply
data.aws_ami.ubuntu: Reading...
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-01e157ec1af2d7314]
aws_instance.app_server: Refreshing state... [id=i-0226232d8b6e9eea6]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0a605bc2ef5707a18]
module.vpc.aws_default_route_table.default[0]: Refreshing state... [id=rtb-0c1f047c07a84c278]
module.vpc.aws_default_security_group.this[0]: Refreshing state... [id=sg-04f350f66843618db]

## ...

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

Changes to Outputs:
  - instance_hostname = "ip-10-0-1-75.us-west-2.compute.internal" -> null

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.app_server: Destroying... [id=i-0226232d8b6e9eea6]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m10s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m20s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m30s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m40s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m50s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 01m00s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 01m10s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 01m20s elapsed]
aws_instance.app_server: Destruction complete after 1m22s

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

ワークスペースを削除

$ terraform destroy
data.aws_ami.ubuntu: Reading...
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-01e157ec1af2d7314]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0a605bc2ef5707a18]
module.vpc.aws_default_security_group.this[0]: Refreshing state... [id=sg-04f350f66843618db]
module.vpc.aws_default_route_table.default[0]: Refreshing state... [id=rtb-0c1f047c07a84c278]
module.vpc.aws_subnet.private[1]: Refreshing state... [id=subnet-0d2376b2fad1af4a6]
module.vpc.aws_subnet.private[0]: Refreshing state... [id=subnet-0d2219235033fe9d0]

## ...

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

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

module.vpc.aws_route_table_association.private[1]: Destroying... [id=rtbassoc-0ea0b41646a73659c]
module.vpc.aws_default_security_group.this[0]: Destroying... [id=sg-04f350f66843618db]
module.vpc.aws_default_route_table.default[0]: Destroying... [id=rtb-0c1f047c07a84c278]
module.vpc.aws_route.public_internet_gateway[0]: Destroying... [id=r-rtb-0cde73c077eadf7e61080289494]

## ...

module.vpc.aws_subnet.private[1]: Destruction complete after 1s
module.vpc.aws_subnet.public[0]: Destruction complete after 1s
module.vpc.aws_subnet.private[0]: Destruction complete after 1s
module.vpc.aws_internet_gateway.this[0]: Destruction complete after 0s
module.vpc.aws_route_table.private[1]: Destruction complete after 1s
module.vpc.aws_route_table.private[0]: Destruction complete after 1s
module.vpc.aws_route_table.public[0]: Destruction complete after 1s
module.vpc.aws_vpc.this[0]: Destroying... [id=vpc-01e157ec1af2d7314]
module.vpc.aws_vpc.this[0]: Destruction complete after 1s

Destroy complete! Resources: 15 destroyed.

コマンドまとめ

コマンド 説明
terraform fmt .tfファイルをフォーマットする
terraform init .tfファイルに記述されているモジュールなどをインストール
terraform validate .tfファイルの内容を検証する
terraform apply デプロイを行う
terraform output outputブロックで定義した出力を確認
terraform state list ワークスペースのリソースのリストを確認
terraform show ワークスペース全体の状態を出力
terraform destroy ワークスペースの破棄
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?