1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめてのTerraform

1
Last updated at Posted at 2026-09-16

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 = 3030Gm7i-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.idaws_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.tfaws_instanceuser_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分だったからです。作って、使って、消して、また作る——それを何度やっても、消し忘れさえしなければこの程度で済みます。

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?