AWSを学びたいものの、料金やリソースの消し忘れが怖く、なかなか手を動かせずにいた。そこで今回はTerraformを使い、空のS3バケットを作成して削除するまでを試した。
TL;DR:この記事でやること
要するに、Terraformを使って東京リージョンに空のS3バケットを1つ作る。AWS上に作成できたことを確認したら、terraform destroyで削除する。
今回はTerraformを体系的に学び切ろうとはせず、plan → apply → 確認 → destroyという「作って壊す」一連の流れを一周することが目標。

なぜTerraformを使うのか
Terraformは、IaC(Infrastructure as Code)と呼ばれる考え方を実現するためのツールの1つ。サーバーやストレージなどの構成を、管理画面の手動操作ではなくコードで定義する。
AWSコンソールでリソースを作ると、どの項目を設定したのか後から思い出せないことがある。Terraformなら、作りたい構成をmain.tfへ残し、実行前にterraform planで変更内容を確認可能。
さらに、今回作ったリソースはterraform destroyで一括削除できる。「何を作るのか」「実行すると何が変わるのか」「最後に何を消すのか」を確認しながら進められるため、AWSの操作が不安な初心者にも向いている。
事前準備
- Terraformのインストール: Homebrewを使用
- Terraformのバージョン: v1.16.0(darwin_arm64)
- AWS CLI: v2.36.34(Python 3.14.7、Darwin 25.6.0、arm64)
- AWS認証の状態:
aws login --region ap-northeast-1によるブラウザ認証に成功 - 使用リージョン: アジアパシフィック(東京)
ap-northeast-1
AWSの認証情報はTerraform設定へ直接書かない。AWSアカウントIDやARNがターミナルやAWSコンソールに表示された場合は、スクリーンショットを公開する前にマスクする。
Terraformをインストールする
今回はApple Silicon搭載MacでHomebrewを使用。
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
terraform version
brew tapは、HashiCorp公式のHomebrewリポジトリを追加する操作。続くbrew installでTerraform本体をインストールする。
最後にterraform versionを実行し、Terraform v1.16.0がdarwin_arm64向けにインストールされたことを確認。HashiCorp公式のインストール手順を参照。

AWS CLIをインストールする
TerraformがAWSを操作できるようにする準備として、AWS CLIもHomebrewでインストール。
brew install awscli
aws --version
今回の環境では、依存パッケージの一覧とともにインストールを続行するか確認され、yを入力。環境によってはこの確認が表示されない場合がある。
インストール後、AWS CLI v2.36.34がarm64向けに導入されたことを確認。
AWSへログインする
AWS CLIをインストールしただけでは、どのAWSアカウントを操作するのか決まっていない。
今回は長期Access Keyを保存せず、AWS公式ドキュメントで案内されているaws loginによるブラウザ認証を使用。
aws login --region ap-northeast-1
ブラウザで認証した後、AWS CLIから現在の認証状態を確認。
aws sts get-caller-identity
このコマンドの結果にはAWSアカウントIDやARNが含まれる。スクリーンショットや記事へ載せる場合は、必ず該当箇所をマスクする。
なお、今回の環境ではaws loginにリージョンを指定しても、AWS CLIの既定リージョンは設定されなかった。後の確認作業で次のコマンドを実行。
aws configure set region ap-northeast-1
今回のTerraform設定
今回作るのは、東京リージョンap-northeast-1に置く空のS3バケット1個。画像や文書などのオブジェクトはアップロードせず、Webサイト公開、バージョニング、Object Lockなども設定しない。

main.tfやTerraformのstateは自分のMac側にあり、実物のS3バケットはAWS側に作られる。Terraformは両者の間に入り、設定を読み取ってAWSへ作成・削除を指示し、その結果をstateへ記録する仲介役。
バケット名はterraform-practice-から始まり、重複を避けるため末尾をTerraformに自動生成してもらう。学習用だと分かるタグも1つ付与。
実験後のディレクトリ構成:
ディレクトリ構成
terraform-s3/
├── .gitignore
├── .terraform/
├── .terraform.lock.hcl
├── main.tf
├── terraform.tfstate
└── terraform.tfstate.backup
自分で用意したのはmain.tfと.gitignore。.terraform.lock.hclは選択されたProviderのバージョンを再現するためGitで管理する。一方、.terraform/とterraform.tfstate、terraform.tfstate.backupはTerraformがローカルに生成。stateには構成によって機密情報が含まれる可能性があるため、Gitへ追加しない。
実際に使用したmain.tf(認証情報やAWSアカウントIDは記載しない):
main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_s3_bucket" "practice" {
bucket_prefix = "terraform-practice-"
tags = {
Purpose = "terraform-practice"
}
}
output "bucket_name" {
description = "Terraformで作成したS3バケット名"
value = aws_s3_bucket.practice.bucket
}
各ブロックの役割:
required_providers: この設定でHashiCorp公式のAWS Providerを使用。~> 6.0は6系のバージョンを使用する指定。今回のterraform initではv6.62.0が選択された。provider "aws": TerraformがAWSを操作するときの共通設定。今回は東京リージョンを指定。resource "aws_s3_bucket" "practice": 実際に作るS3バケット。practiceはTerraform設定内で参照するための名前であり、AWS上のバケット名ではない。bucket_prefix: バケット名の先頭だけを指定し、末尾を自動生成。S3バケット名の重複を避けやすくなる。tags: AWSコンソール上でリソースの目的を識別するためのメタ情報。output "bucket_name":apply後に確定した実際のバケット名をターミナルへ表示。新しいAWSリソースを作る設定ではない。
この設定ではforce_destroyを指定していないため、Providerの既定値はfalseになる。バケット内にオブジェクトが存在する場合、それらをTerraformが勝手に消してバケットを削除する動作にはならない。
また、新規S3バケットはAWSの既定設定でパブリックアクセスがブロックされる。今回のmain.tfではBlock Public AccessをTerraformのリソースとして明示的には管理していない。AWS公式ドキュメントでも、パブリックアクセスを許可する明確な理由がなければブロックを維持することが推奨されている。
terraform init
terraform initは、設定で必要になるProviderなどを準備するコマンド。
初期化に成功し、HashiCorp公式のAWS Provider v6.62.0がインストールされた。Providerの選択バージョンを記録する.terraform.lock.hclも生成。
この時点ではAWSリソースはまだ作成されていない。
initはS3を作るコマンドではなく、このTerraformプロジェクトを実行できる状態に準備する工程。

terraform validate
terraform validateで、Terraform設定の文法と構成が有効かを確認。
Success! The configuration is valid.
このコマンドもAWSリソースを作らない。planへ進む前に、設定ファイルの基本的な誤りを見つけるための確認工程。
terraform plan
terraform planは、実際にAWSへ変更を加える前に、何が作成・変更・削除される予定かを確認するコマンド。
確認したポイント:
- 作成対象は
aws_s3_bucket.practiceの1件だけ Plan: 1 to add, 0 to change, 0 to destroy.と表示された- 予想外の変更や削除はなし
出力中のknown after applyは、バケット名などの値がAWSで作成された後に確定することを表す。planの時点で値が見えないこと自体は正常な動作。

terraform apply
terraform applyで、確認した構成をAWSへ反映。
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.と表示され、S3バケット1個の作成に成功。
バケットの作成自体は約1秒、コマンド全体は約31秒。
承認画面では、作成1件・変更0件・削除0件であることを再確認してからyesを入力。作成後、outputに実際のバケット名が表示された。
AWS上でS3バケットを確認
AWSコンソールまたはAWS CLIで、S3バケットが作成されたことを確認。
AWSコンソールの汎用バケット一覧で、terraform-practice-から始まるバケットが1件存在し、AWSリージョンがアジアパシフィック(東京)ap-northeast-1であることを確認。AWS CLIのget-bucket-locationでもLocationConstraint: ap-northeast-1が返り、同じ結果を確認できた。
Terraform側でもterraform state listを実行し、管理対象としてaws_s3_bucket.practiceだけが記録されていることを確認。

terraform destroy
terraform destroyの実行前に、削除対象が今回作成したリソースだけであることを確認。承認後、削除完了をAWS側でも確認する。

実行前にPlan: 0 to add, 0 to change, 1 to destroy.と、削除対象がaws_s3_bucket.practiceの1件だけであることを確認。
yesで承認後、Destroy complete! Resources: 1 destroyed.と表示され、Terraformによる削除に成功。バケットの削除自体は0秒、コマンド全体は約1分38秒。
削除後にterraform state listを再実行すると何も表示されず、Terraformの管理対象が0件になったことも確認できた。

destroy後にAWSコンソールを更新するとS3の開始画面へ戻り、作成したバケットが一覧に残っていないことを確認。
さらに、AWS CLIでバケット一覧を名前の接頭辞で絞り込み。
aws s3api list-buckets \
--query "Buckets[?starts_with(Name, 'terraform-practice-')].Name"
結果が空の配列になり、terraform-practice-から始まるバケットがAWS側に残っていないことを確認。Terraformのstateが空であることと、AWS上に実物が残っていないことは別々に確認すると確実。Terraformのstateに存在しないAWSリソースは、terraform destroyの管理対象外になるため。

ハマったこと
AWS CLIをインストールしただけではAWSを操作できなかった
- エラー:
NoCredentials: Unable to locate credentials - 最初の予想: AWS CLIのインストールに失敗している可能性を推測
- 原因: AWS CLI本体は動作していたが、AWSアカウントへ接続するための認証情報がまだ未設定
- 解決方法:
aws login --region ap-northeast-1を実行し、ブラウザで認証。AWSから認証情報の共有成功が表示された - 初心者向けに言い換えると: AWS CLIという道具は用意できたが、誰としてAWSを操作するのかをまだAWSへ伝えていない状態だった
AWS CLIでリージョン未指定エラーになった
- エラー:
NoRegion: You must specify a region. - 最初の予想: IAM権限に問題がある可能性を推測
- 原因: IAM権限ではなく、AWS CLIで使用する既定リージョンが未設定
- 解決方法:
aws configure set region ap-northeast-1で既定リージョンを東京に設定。aws configure get regionとget-bucket-locationを再実行し、ap-northeast-1を確認 - 初心者向けに言い換えると: Terraformには東京リージョンを指定していたが、その設定はAWS CLIには自動共有されない。TerraformとAWS CLIはそれぞれ個別にリージョン設定を持つ

実際にかかった費用
実験直後にAWSの請求画面を確認したところ、2026年8月のS3コストは$0.00と表示されていた。今回は空のバケットを短時間だけ作成し、オブジェクトは保存していない。
ただし、これを「費用が完全に0だった」と断定することはできない。Amazon S3では保存容量だけでなく、PUT、LIST、GETなどのリクエストにも料金が設定されている。Amazon S3の公式料金ページによると、AWSコンソールでの閲覧もGETやLISTなどのリクエストとして扱われ、DELETEリクエストは無料。
また、AWSの請求データには反映まで最大約24時間かかる場合があり、表示時の丸めによって微小な料金が$0.00に見える可能性もある。そのため、本記事では「確認時点の請求画面ではS3が$0.00だった」とする。AWS Billingの公式ドキュメント
やってみて理解したこと
- Terraform CLIからAWS Providerを通してS3バケットを作成できる。AWS CLIは、作成後のリージョン確認や削除後の検索に利用可能。
bucket_prefixを使うと、意味のある接頭辞を残しつつ、重複しにくいバケット名をTerraformに生成してもらえる。- S3バケット名はAWSパーティション内で一意であり、URLにも現れる公開識別子。秘密情報を名前に含めず、記事では不要な部分をマスクする。
terraform state listが空であることと、AWS上にリソースが残っていないことは別々に確認する。両方を確認すると、消し忘れへの不安を解消できる。
次に試すこと
- S3の設定を1つ追加し、
terraform planの差分を読む - EC2を作る前に料金と削除対象を確認する
- S3 + CloudFrontの構成へ進む
まとめ
Terraformを使い、東京リージョンに空のS3バケットを1個作成し、AWSコンソールとCLIで確認した後、terraform destroyで削除できた。planで変更内容を事前確認し、削除後はTerraformのstateとAWS上の実物を別々に確認することで、「何を作り、何を消したのか」を追いやすかった。
一方、AWS CLIの認証とリージョン設定はTerraformとは別に必要であり、実際に手を動かして初めて違いを理解できた。小さなS3バケットから「作って壊す」サイクルを一度通したことで、次のAWSリソースも同じ確認手順で試せそうだ。