AWS の画面と aws コマンドで作ったサーバー一式を、main.tf というファイル1本に書き直しました。
やってみて一番意外だったのは、1回目はむしろ遅くなったことです。ファイルを書き上げるまでに3日かかりました。画面から手で作れば1〜2時間で終わっていたものです。
速くなったのは2回目からでした。同じ7つが、こうなります。
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
31秒です。消すほうは43秒でした。
この記事では、Terraform を入れるところから、S3 バケット1つだけの小さい状態を作り、そこに1つずつ足して EC2 まで立て、最後に全部消してもう一度作るまでを 9つの段に分けて書きます。途中で止まった5か所も残します(エラーが出たものは原文のまま載せました)。
この記事でできること
AWS のアカウントがある状態から、サーバー1台とその周りのもの、合わせて7つを main.tf に書いて、terraform apply 1本で立てられるようにします。
- 対象読者:コードは書けるけれど AWS はこれからの方
- かかるお金:4〜5円(サーバーが動いていたのは合計15分でした)
- 試した日:2026年9月16日
- 試した環境:iMac(2017・Intel)/macOS Ventura 13.7.8/Terraform v1.16.2/awscli 2.36.44/東京リージョン(ap-northeast-1)
この記事は前の3本の続きです。AWS のアカウントの作り方は1本目、aws コマンドを入れるところは2本目、サーバーを画面から立てるところは3本目に書きました。
「無料プラン」なのにお金がかかるのはなぜか
いまの無料プランでは、アカウントを作ると最大200ドルぶんのクレジットが配られます。サーバーを動かすと、その金額からクレジットが減ります。クレジットカードから引き落とされるわけではありません。この記事の4〜5円も、クレジットから引かれたぶんです。
Terraform は何をするものか
「AWS にこれを作ってほしい」という文章をファイルに書いておいて、コマンド1本でそのとおりに作らせる道具です。
たとえば「S3 バケットを1つ作る」は、こう書きます。
resource "aws_s3_bucket" "study" {
bucket = "mitsuhashi-tf-2026"
}
この3行を書いて terraform apply と打つと、AWS にバケットが1つできます。画面を開いてボタンを押す代わりに、この文章を書きます。
覚える言葉は最初のうち3つで足ります。
| 言葉 | 意味 |
|---|---|
resource |
AWS にモノを作るという宣言。上の例ならバケット1つ |
terraform apply |
書いたとおりに作らせるコマンド |
terraform destroy |
作ったものを全部消すコマンド |
あとの言葉は、出てきたところで説明します。
1章 Terraform を入れる
⛔ brew install terraform では入りません
macOS なので brew install terraform から始めようとしました。通りません。
Error: No available formula with the name "terraform". Did you mean terraformer or terraform-ls?
2023年に Homebrew が Terraform を本体から外したためです(Terraform のライセンスが変わったのが理由)。入門記事の多くがこの1行を書いていますが、いまは動きません。
作った会社が自分で置き場を配っているので、そちらも試しました。こちらも通りません。
Error: Invalid cask (Linux on ARM64): /usr/local/Homebrew/Library/Taps/hashicorp/homebrew-tap/Casks/hashicorp-vagrant.rb
Error: Cannot tap hashicorp/tap: invalid syntax in tap!
置き場の中の別ファイルが壊れていて、置き場ごと読み込めない状態でした。こちらでは直せません。
1-1. 公式サイトの zip を落として置く
結局これが一番早かったです。3つ打つだけで終わります。
# いまの最新版を調べる
curl -s https://api.releases.hashicorp.com/v1/releases/terraform/latest | python3 -c "import sys,json;print(json.load(sys.stdin)['version'])"
# 落とす(Intel Mac の場合。Apple シリコンなら darwin_arm64)
cd ~/Downloads
curl -LO https://releases.hashicorp.com/terraform/1.16.2/terraform_1.16.2_darwin_amd64.zip
curl -LO https://releases.hashicorp.com/terraform/1.16.2/terraform_1.16.2_SHA256SUMS
落としたものが本物かを、同じ場所に置いてある一覧と突き合わせます。
shasum -a 256 terraform_1.16.2_darwin_amd64.zip
grep darwin_amd64 terraform_1.16.2_SHA256SUMS
2行の先頭の文字列が同じなら本物です。私の場合は 275f50ed172af2f... で一致しました。
unzip terraform_1.16.2_darwin_amd64.zip
mv terraform /usr/local/bin/
/usr/local/bin は自分のものなので sudo は要りませんでした。
1-2. 入ったか確かめる
terraform version
Terraform v1.16.2
on darwin_amd64
この入れ方だと brew upgrade では上がりません
Homebrew の管理の外にあるためです。版を上げるときは、同じ手順で新しい zip を落として /usr/local/bin/terraform を置き換えます。
2章 いちばん小さいものを1つ作る
いきなり全部を書きません。バケット1つだけの状態を作って、作る・確かめる・消すを1周します。
2-1. 場所を用意する
mkdir ~/Documents/aws-terraform-study
cd ~/Documents/aws-terraform-study
touch main.tf
2-2. 1つめのかたまり=どこの AWS を使うか
main.tf に、まずこれを書きます。
# AWS の東京リージョンを使う
provider "aws" {
region = "ap-northeast-1"
}
provider は「どのクラウドの、どの地域を相手にするか」の宣言です。この4行があると、以降に書くものは全部その地域に作られます。
⚠ 鍵もパスワードもどこにも書きません
上の4行をよく見ると、どの AWS アカウントに作るのかが書いてありません。ID もパスワードもアクセスキーも出てきません。それでも自分のアカウントに作られます。
Terraform が ~/.aws/credentials を勝手に読みに行くからです。2本目の記事で aws configure を済ませていれば、そのときに置かれたキーがこのファイルに入っています。
先に確かめておきます。
aws sts get-caller-identity
{
"UserId": "AIDA...",
"Account": "(12桁の番号)",
"Arn": "arn:aws:iam::(12桁の番号):user/hiro-cli"
}
これが返れば、Terraform も同じ相手に作ります。
⚠ Unable to locate credentials と出たら、まだ aws configure が済んでいません。先に2本目の記事の手順を済ませてください。
アクセスキーを .tf ファイルに書かないでください
provider "aws" の中に access_key = "..." と書くこともできます。⛔ ですがそのファイルを git に入れた瞬間、キーが記録に残ります。消してもコミットの履歴からは消えません。書かなければ ~/.aws/credentials が使われるので、書く必要はありません。
2-3. 2つめのかたまり=バケットを1つ作る
# S3 バケットを1つ作る
resource "aws_s3_bucket" "study" {
bucket = "mitsuhashi-tf-2026"
}
resource の後ろの2つの言葉は役割が違います。
| 場所 | 例 | 意味 |
|---|---|---|
| 1つめ | aws_s3_bucket |
何を作るかの型名。Terraform が決めている名前 |
| 2つめ | study |
この文章の中での呼び名。自分で好きに付ける。AWS 側には出ない |
| 中身 | bucket = "..." |
AWS 側での実際の名前 |
2つめの study は、あとで他のかたまりから aws_s3_bucket.study.arn のように指すのに使います。
バケットの名前は世界で1つです
mitsuhashi-tf-2026 はすでに私が使ったので、真似すると弾かれます。自分の名前や日付を混ぜて、他と重ならない名前にしてください。
2-4. terraform init
terraform init
AWS を操作するためのプログラム本体が .terraform/ というフォルダに落ちてきます。839MB あります。最初の1回だけ必要です。
2-5. terraform plan
terraform plan
Plan: 1 to add, 0 to change, 0 to destroy.
「1つ作ります、0個変えます、0個消します」 という意味です。⛔ この時点では AWS にまだ何も作られていません。これから何が起きるかを見せるだけのコマンドで、お金もかかりません。
2-6. terraform apply
terraform apply
同じ一覧が出たあと、打ち込み待ちになります。
Do you want to perform these actions?
Enter a value:
yes と打つと作られます。⚠ y では通りません。yes と3文字です。
2-7. 自分の目で確かめる
aws s3 ls
2026-09-15 21:33:52 mitsuhashi-tf-2026
Terraform が作ったものが、aws コマンドからも見えます。当たり前のようですが、ここを1回自分で見ておくと以降の安心感が違います。
2-8. terraform.tfstate を覗く
apply のあと、フォルダに見慣れないファイルができています。
ls -a
.terraform .terraform.lock.hcl main.tf terraform.tfstate
⚠ -a を付けないと、頭が . で始まるものは出てきません。
terraform.tfstate は「AWS にいま何があるか」の控えです。バケット1つ作っただけで30項目ほど記録されています。
これが重要なのは、terraform destroy が見るのは main.tf ではなくこちらだからです。あとの8章で効いてきます。
terraform.tfstate は git に上げないでください
AWS の中身がそのまま書いてあります。この記事の範囲では出ませんが、データベースのパスワードなどを扱うとそれも入ります。
3つの関係を1枚にするとこうなります。
⭐ destroy の矢印が main.tf から出ていないところが肝です。見ているのは terraform.tfstate のほうです。
2-9. 消して1周する
terraform destroy
yes と打つと消えます。
aws s3 ls
2026-09-13 07:55:38 mitsuhashi-study-2026
⭐ 前の記事で画面から手で作ったバケットは残っています。terraform destroy が消すのは terraform.tfstate に載っているものだけで、手で作ったものには手を出しません。
3章 git に入れる
.tf はプログラムと同じように記録を残せます。ただし、そのまま git init すると 839MB の .terraform/ まで記録しようとします。
3-1. 先に .gitignore を書く
git init
.gitignore にこれを書きます。
# AWS を操作するプログラム本体(839MB)。terraform init で取り直せる
.terraform/
# AWS の中身が書いてあるので git に上げない
*.tfstate
*.tfstate.backup
# plan を保存したファイル(中身は上と同じ)
*.tfplan
⭐ 逆に .terraform.lock.hcl は git に入れます。使う部品の版を固定しておくファイルなので、これがあると別のパソコンでも同じ版で動きます。
3-2. 記録する
git add .
git commit -m "S3 バケットを Terraform で作る(main.tf 4行)"
3 files changed, 43 insertions(+)
839MB のうち、記録されたのは 43行の3ファイルだけです。
4章 ssh の通り道を足す
ここから「1つ足して apply、また1つ足して apply」を繰り返します。
4-1. 自分の IP を調べる
curl -s https://checkip.amazonaws.com
自宅の回線の番号が返ってきます。この番号からだけ、あとで作るサーバーに入れるようにします。
4-2. 番号を別ファイルに置く
variables.tf という名前で新しいファイルを作ります。
# ssh を許す IP。自宅のグローバル IP が変わったらここだけ書き直す
variable "my_ip" {
description = "自宅のグローバル IP(curl -s https://checkip.amazonaws.com で調べる)"
type = string
default = "203.0.113.45/32"
}
⚠ 203.0.113.45 は説明用の番号です(実在しません)。ここは自分の番号に置き換えてください。
なぜ別ファイルにするのか——自宅の番号は時々変わります。main.tf の中に直接書くと、変わるたびに本体を探して直すことになります。ここに集めておけば直す場所が1つで済みます。
末尾の /32 は「この1つの番号だけ」という意味です。
4-3. 通行許可証を足す
main.tf に追記します。
# EC2 の前に置く通行許可証。22番(ssh)を自宅の IP からだけ通す
resource "aws_security_group" "study" {
name = "aws-study-tf-sg"
description = "ssh from my home only"
ingress {
description = "ssh"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.my_ip]
}
egress {
description = "all outbound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
-
ingressは入ってくる方向。22番(ssh)を、var.my_ip(さっきのvariables.tfの値)からだけ通します -
egressは出ていく方向。0.0.0.0/0は「どこへでも」。サーバーから外のサイトへつなげないと、あとでソフトを入れられません
4-4. plan で数を見る
terraform plan
Plan: 2 to add, 0 to change, 0 to destroy.
⭐ 1 ではなく 2 です。2-9 でバケットを消したので、通行許可証と一緒にバケットも作り直されます。
terraform apply は「差分を埋める」コマンドだからです。main.tf に書いてあるもののうち、AWS に無いものを全部作ります。
4-5. apply
terraform apply
yes で2つできます。
5章 サーバーに身分証を持たせる
サーバーから S3 のファイルを読めるようにします。⛔ アクセスキーをサーバーに置く方法は使いません(置いたキーが漏れると、そのキーでできること全部が漏れます)。
代わりに「このサーバーは、このバケットを読んでよい」という身分証を持たせます。
5-1. 役を作る
# EC2 が名乗れる役。これだけでは何の権限も無い(権限は 5-2 で足す)
resource "aws_iam_role" "study" {
name = "aws-study-tf-role"
# 「EC2 というサービスだけがこの役を名乗ってよい」という許可状
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = {
Service = "ec2.amazonaws.com"
}
}]
})
}
assume_role_policy は 「誰がこの役を名乗ってよいか」 だけを決めます。ここでは ec2.amazonaws.com、つまり EC2 というサービスだけです。何ができるかはまだ何も書いていません。
jsonencode({...}) は、中の書き方を Terraform 流のまま書いて、AWS に送るときだけ JSON に直す仕掛けです。二重引用符とカンマだらけの JSON を手で書かずに済みます。
5-2. 権限を足す
# 上の役に付ける権限。バケット1つを読むことだけ
resource "aws_iam_role_policy" "study" {
name = "read-study-bucket"
role = aws_iam_role.study.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:ListBucket",
]
Resource = [
aws_s3_bucket.study.arn,
"${aws_s3_bucket.study.arn}/*",
]
}]
})
}
ここで2つ、つまずきやすい点があります。
① Resource に2つ書く必要があります。
| 書くもの | 何に効くか |
|---|---|
aws_s3_bucket.study.arn |
バケットそのもの(中身の一覧を出す ListBucket に要る) |
"${aws_s3_bucket.study.arn}/*" |
中のファイル1つ1つ(GetObject に要る) |
片方だけだと「一覧は出せるがファイルが取れない」あるいはその逆になります。
② role = aws_iam_role.study.id の書き方。
ここが、5-1 で作った役を指しています。aws_iam_role(型名)+ study(自分で付けた呼び名)+ .id。この書き方をするだけで、Terraform は「役を先に作ってから権限を付ける」と判断します。順番はどこにも書きません。
5-3. ⛔ サーバーに役は直接付きません
ここが今回いちばん驚いたところです。EC2 に IAM ロールを直接くっつけることはできません。間にもう1つ要ります。
# 役を EC2 に取り付けるための入れ物。EC2 はロールを直接は持てない
resource "aws_iam_instance_profile" "study" {
name = "aws-study-tf-profile"
role = aws_iam_role.study.name
}
⚠ AWS の画面から作っているときは、この存在に気づけません。画面でサーバーに役を選ぶと、AWS が裏でこれを自動で作ってくれるからです。コードで書くと、自分で書かないと動きません。
くっつく順を図にするとこうです。
5-4. apply
terraform plan
terraform apply
3つ増えます。ここまで作ったものは全部無料です。
6章 サーバーを立てる
6-1. 鍵を預ける
# ssh の鍵。EC2 の回で作った鍵の「公開鍵」だけを AWS に預ける
resource "aws_key_pair" "study" {
key_name = "aws-study-tf-key"
public_key = file(pathexpand("~/.ssh/aws-study-key.pub"))
}
前の記事で鍵を作っていたので、新しく作らず使い回しました。手元にあるのが .pem(秘密鍵)だけなら、そこから公開鍵を取り出せます。
ssh-keygen -y -f ~/.ssh/aws-study-key.pem > ~/.ssh/aws-study-key.pub
-
.pem(1,675バイト)=手元だけに置くもの。これで入ります -
.pub(381バイト)=AWS に預けるもの。これだけでは入れません
⭐ 逆向き(公開鍵から秘密鍵)はできません。だから公開鍵は預けても大丈夫です。
file("~/.ssh/...") は動きません
~ が展開されず、そのままの文字として扱われます。file(pathexpand("~/...")) と書きます。
6-2. サーバーの元になるイメージ番号を引く
# Amazon Linux 2023 の最新イメージ番号を AWS に聞く。作るのではなく読むだけ
data "aws_ssm_parameter" "al2023" {
name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}
ここだけ resource ではなく data です。
| 何をするか | |
|---|---|
resource |
AWS にモノを作る。destroy で消える |
data |
AWS から値を読むだけ。⭐ destroy で消えるものが無い |
なぜ番号を直接書かないのか——サーバーの元になるイメージ(AMI)の番号は毎月変わります。私が試した間にも、9月15日の朝に更新されていました。ami-053ea429a1c73a5b7 のように書くと、翌月には古いものを掴みます。
6-3. サーバー本体
# EC2 本体。ここまでに作った通行許可証・入れ物・鍵・AMI を指しに行く
resource "aws_instance" "study" {
ami = data.aws_ssm_parameter.al2023.value
instance_type = "m7i-flex.large"
key_name = aws_key_pair.study.key_name
vpc_security_group_ids = [aws_security_group.study.id]
iam_instance_profile = aws_iam_instance_profile.study.name
# ディスク 30GB(ollama と llama3.2:3b を置くのに要る)
root_block_device {
volume_size = 30
volume_type = "gp3"
}
tags = {
Name = "aws-study-tf"
}
}
⭐ ここまでに作ったものを、4か所から指しています。この指し方だけで、Terraform は「これらを先に作ってからサーバーを立てる」と決めます。
無料プランで選べるサーバーの種類は6つだけです
t3.large を指定して弾かれたことがあります。選べるものの調べ方は3本目に書きました。
6-4. できた IP を画面に出す
outputs.tf という名前で新しいファイルを作ります。
# apply が終わったあと画面に出す値
output "ec2_ip" {
description = "EC2 のグローバル IP"
value = aws_instance.study.public_ip
}
output "ssh_command" {
description = "そのまま貼れる ssh のコマンド"
value = "ssh -i ~/.ssh/aws-study-key.pem ec2-user@${aws_instance.study.public_ip}"
}
output "bucket_name" {
description = "benchmark.py を置くバケット"
value = aws_s3_bucket.study.bucket
}
apply が終わると、この3つが画面に出ます。画面から作っていたときは、EC2 のページを開いて「パブリック IPv4 アドレス」を目で読んでコピーしていました。それが要らなくなります。
6-5. 🔴 ここから料金が発生します
terraform apply
aws_key_pair.study: Creation complete after 0s
aws_instance.study: Creation complete after 13s [id=i-081d50da6378be9ba]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
13秒でサーバーが立ちました。
| かかるもの | いくら |
|---|---|
サーバー本体(m7i-flex.large) |
1時間 約19円 |
| ディスク 30GB | 1日 約14円 |
6-6. IP は勝手に出てきます
bucket_name = "mitsuhashi-tf-2026"
ec2_ip = "13.158.50.13"
ssh_command = "ssh -i ~/.ssh/aws-study-key.pem ec2-user@13.158.50.13"
7章 中で動かす
7-1. 入る
ssh -i ~/.ssh/aws-study-key.pem ec2-user@13.158.50.13
中で確かめます。
grep PRETTY_NAME /etc/os-release
nproc
free -g | grep Mem
df -h /
PRETTY_NAME="Amazon Linux 2023.12.20260914"
2
Mem: 7 0 7 0 0 7
/dev/nvme0n1p1 30G 1.8G 29G 6% /
⭐ main.tf に書いた数字が、そのまま出てきます。volume_size = 30 が 30G、m7i-flex.large が CPU 2個・メモリ 7GB。画面から作ったときは「本当にこの設定で立ったか」を EC2 のページで探して確かめていました。
入れないときは、まず自分の IP を疑ってください
variables.tf に書いた番号と、いまの番号が違うと、通行許可証に弾かれます。エラーも出ず、ただ黙って待たされます。curl -s https://checkip.amazonaws.com で確かめて、変わっていたら variables.tf を直して terraform apply です。
7-2. ソフトを入れる
サーバーの中で打ちます。
curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3.2:3b
ollama list
NAME ID SIZE MODIFIED
llama3.2:3b a80c4f17acd5 2.0 GB Less than a second ago
⚠ ここは Terraform の外側の作業です。main.tf には「サーバーを1台立てる」としか書いていないので、Terraform が作るのは空のサーバーまで。いま入れた 2.0GB は Terraform が知らないものです。9章で効いてきます。
7-3. 🔴 アクセスキーなしで S3 から取れます
まず手元から S3 にファイルを置きます。
# iMac で
aws s3 cp benchmark.py s3://mitsuhashi-tf-2026/
サーバーの中で、自分が誰として動いているかを確かめます。
# サーバーで
aws sts get-caller-identity --query Arn --output text
arn:aws:sts::(アカウント番号):assumed-role/aws-study-tf-role/i-081d50da6378be9ba
~/.aws/credentials も環境変数も、何も置いていません。それでも 5-1〜5-3 で書いた役を名乗った状態で立ち上がっています。
aws s3 cp s3://mitsuhashi-tf-2026/benchmark.py .
download: s3://mitsuhashi-tf-2026/benchmark.py to ./benchmark.py
取れました。5-2 で書いた権限(このバケットを読むことだけ)がそのまま効いています。
動かした結果です。
accuracy: 5/6
mean 4.4s / min 4.1s / max 4.9s
キーがどこにも無いのに届く道すじです。
8章 消す
8-1. 何が消えるか先に見る
terraform plan -destroy
# aws_iam_instance_profile.study will be destroyed
# aws_iam_role.study will be destroyed
# aws_iam_role_policy.study will be destroyed
# aws_instance.study will be destroyed
# aws_key_pair.study will be destroyed
# aws_s3_bucket.study will be destroyed
# aws_security_group.study will be destroyed
Plan: 0 to add, 0 to change, 7 to destroy.
8-2. 🔴 途中で止まりました
terraform destroy
aws_instance.study: Destruction complete after 30s
aws_iam_role_policy.study: Destruction complete after 1s
aws_key_pair.study: Destruction complete after 0s
aws_iam_instance_profile.study: Destruction complete after 0s
aws_security_group.study: Destruction complete after 0s
aws_iam_role.study: Destruction complete after 1s
Error: deleting S3 Bucket (mitsuhashi-tf-2026):
api error BucketNotEmpty: The bucket you tried to delete is not empty
中身の入った S3 バケットは消せません。7-3 でファイルを置いたからです。
ここで大事なのは、エラーが出ても、そこまでに消した6つは戻らないことです。terraform.tfstate にはバケット1件だけが残り、途中まで進んだ状態が正しく記録されます。
画面から消すときは気づきません
AWS の画面でバケットを消そうとすると「先に空にしてください」と案内が出ます。コマンドからだと、このエラーが出るだけです。
8-3. 1行足すと直ります
resource "aws_s3_bucket" "study" {
bucket = "mitsuhashi-tf-2026"
force_destroy = true
}
force_destroy = true は「中身ごと消してよい」という指定です。
⚠ ここで私は失敗しました
この1行を反映させるつもりで terraform apply と打ちました。
ec2_ip = "3.112.24.96"
サーバーが立ってしまいました。8-2 で消えた6つが全部作り直されたのです。
terraform apply は 「1つだけ直す」ができません。main.tf に書いてあるものと AWS の現状の差を、全部埋めにいきます。すぐ気づいて消しましたが、43秒ぶんの料金は発生しました(0.2円)。
正しくはこう打ちます。
terraform apply -target=aws_s3_bucket.study
-target を付けると、指定したものだけを揃えます。
8-4. 残っていないことを数える
terraform destroy
Destroy complete! Resources: 7 destroyed.
⭐ 今度はバケットも消えました。同じコマンドが、1行足しただけで通ります。
念のため aws コマンドでも数えます。
aws ec2 describe-instances --filters "Name=instance-state-name,Values=running,stopped" --query 'Reservations[].Instances[].[InstanceId,State.Name]' --output text
aws ec2 describe-volumes --query 'Volumes[].[VolumeId,Size,State]' --output text
両方とも何も返ってこなければ、料金は止まっています。
サーバーは「停止」だけでは料金が止まりません
止めた状態でもディスク代が1日14円かかり続けます。terraform destroy はディスクごと消すので、ここは心配要りません。画面から止めているときは、ディスクが残っていないかを必ず見てください。私は2日ぶん払いました。
9章 もう一度作る
ここが、この記事でいちばん見せたかったところです。
9-1. もう一度立てる
terraform apply
aws_key_pair.study: Creation complete after 1s
aws_s3_bucket.study: Creation complete after 2s
aws_iam_role.study: Creation complete after 2s
aws_iam_role_policy.study: Creation complete after 1s
aws_security_group.study: Creation complete after 3s
aws_iam_instance_profile.study: Creation complete after 7s
aws_instance.study: Creation complete after 14s
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
31秒です。
作る順番はどこにも書いていません
上の並びをよく見ると、鍵・バケット・役・通行許可証の4つが同時に始まり、そのあと権限、取り付けの入れ物、最後にサーバーです。
main.tf に「この順で作れ」とは一行も書いていません。aws_iam_role.study.id や aws_security_group.study.id という指し方だけから、Terraform が順番を組み立てています。互いに関係のない4つは同時に走りました。
指し方だけで決まった順番を図にするとこうです。
⭐ 矢印が刺さっていない4つ(鍵・バケット・役・通行許可証)が、最初に同時に走った4つです。刺さる矢印がいちばん多いサーバーが最後になります。
9-2. 中身は同じか
ssh -i ~/.ssh/aws-study-key.pem ec2-user@43.206.149.43
7-1 と同じコマンドを打ちました。
| 見たもの | 1回目 | 作り直し |
|---|---|---|
| OS | Amazon Linux 2023.12.20260914 |
同じ |
| CPU | 2個 | 同じ |
| メモリ | 7GB | 同じ |
| ディスク | 30G 中 1.8G | 同じ |
| 身分証 | aws-study-tf-role |
同じ |
| ollama | 入っていた | ⛔ 入っていない |
which ollama
/usr/bin/which: no ollama in (/home/ec2-user/.local/bin:...)
main.tf に書いた7つは31秒で戻り、7-2 で手で入れた 2.0GB は戻りませんでした。
ここが Terraform の守備範囲の境目です。
-
main.tfに書いたもの(ディスクの大きさ・CPU の数・身分証)→ 戻る - ssh で入って手で入れたもの(ollama とモデル)→ 戻らない
⭐ 戻したいなら main.tf の aws_instance に user_data を書きます。起動したときに実行するスクリプトを書いておく場所で、そこに ollama を入れる手順を書けば、7つと同じ扱いになります。
9-3. 片付ける
terraform destroy
Destroy complete! Resources: 7 destroyed.
43秒でした。
つまずいた5か所
| 何が起きたか | どうしたか | |
|---|---|---|
| 1 |
brew install terraform が通らない |
公式サイトの zip を落として /usr/local/bin に置いた |
| 2 | サーバーに IAM ロールが直接付かない | 間に aws_iam_instance_profile を1つ挟んだ |
| 3 |
file("~/.ssh/...") が動かない |
file(pathexpand("~/...")) にした |
| 4 | 中身の入った S3 バケットが消せない |
force_destroy = true を1行足した |
| 5 |
apply で1つだけ直したつもりが全部作り直された |
-target=<名前> を付ける |
まとめ
同じ7つを作るのに、かかった時間です。
| 時間 | |
|---|---|
| 画面から手で(前の3本) | 1〜2時間 |
main.tf を書く(今回・1回目) |
3日 |
terraform apply(2回目以降) |
31秒 |
terraform destroy |
43秒 |
1回目は明らかに遅いです。画面でボタンを押せば終わることを、調べながら107行に書き直したので3日かかりました。
それでも書いてよかったと思ったのは、手元に107行のファイルが残ったことです。前の3本で画面から作ったときに残ったのは、記憶と画面写真だけでした。どのボタンをどの順で押したかは、1か月後には思い出せません。
107行のほうは、来月も同じものを31秒で立てます。何をどう設定したかも、ファイルを読めば全部書いてあります。
今日かかったお金は 4〜5円でした。サーバーが立っていた時間の合計が15分だったからです。作って、使って、消して、また作る——それを何度やっても、消し忘れさえしなければこの程度で済みます。