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?

運用エンジニアが Cloud Resume Challenge(AWS)で職務経歴サイトを作り、Terraform で既存リソースをコード化するまで

0
Last updated at Posted at 2026-11-10

運用エンジニアが Cloud Resume Challenge(AWS)で職務経歴サイトを作り、Terraform で既存リソースをコード化するまで

はじめに

運用保守の仕事(Windows Server/VMware、Linux・AWS 環境の障害一次対応)を約 2 年半続けてきましたが、AWS を「自分で構築した」経験がありませんでした。そこで Cloud Resume Challenge の AWS 版に取り組み、日英バイリンガルの職務経歴サイトを作りました。

この記事では、構成と工夫した点に加えて、新しい形態の AWS アカウントでつまずいたことと、コンソールで作ったリソースを Terraform に取り込んだら過剰権限が見つかった話をまとめます。

生成 AI を学習の補助に使いましたが、設定・実装・デプロイは自分で行っています。

構成

ブラウザ ──HTTPS──▶ CloudFront ──OAC──▶ S3(非公開バケット)
   │
   └─ counter.js ──▶ API Gateway (HTTP API, GET /count)
                         ▼
                      Lambda (Python 3.14) ──▶ DynamoDB(アクセス数)
サービス 役割
S3 + CloudFront(OAC) 静的サイト配信。バケットは公開せず、CloudFront 経由のみ
API Gateway(HTTP API) GET /count。CORS はサイトのオリジンだけ許可
Lambda + DynamoDB アクセス数を ADD で原子的に 1 増やして返す
GitHub Actions frontend/ は S3 同期 + キャッシュ無効化、backend/ はテスト成功後に Lambda 更新
Terraform バックエンドの既存リソースを import してコード管理

工夫した点

S3 を公開しない

静的ウェブサイトホスティングは使わず、CloudFront の Origin Access Control(OAC)経由でのみ配信しています。
※ 推奨設定で作ると「デフォルトルートオブジェクト」が空のままで、/ にアクセスすると AccessDenied になりました。index.html を設定して解決。

アトミックカウンター

「読む → +1 → 書く」だと同時アクセスで値が飛ぶので、DynamoDB の更新式 ADD を使っています。count は予約語なので ExpressionAttributeNames で回避しました。

response = table.update_item(
    Key={"id": "visitors"},
    UpdateExpression="ADD #c :inc",
    ExpressionAttributeNames={"#c": "count"},
    ExpressionAttributeValues={":inc": 1},
    ReturnValues="UPDATED_NEW",
)

テストが通ったときだけデプロイ

pytest + moto で DynamoDB をモックし、4 項目(応答形式、初回値、連続加算、保存結果)を確認。全部成功したときだけ aws lambda update-function-code を実行します。

つまずいたこと:新しい形態の AWS アカウント

今回使ったのは、新しいサインアップ方式で作ったアカウント(無料プラン、プロジェクト単位で管理される形態)です。通常のアカウントと違う制約があり、古い記事の手順がそのままでは通りませんでした。

制約 影響と対応
ルートユーザーがない/IAM ユーザーでコンソールにログインできない IAM ユーザーはプログラム用(アクセスキー)のみ
リージョンが固定(シドニー) 東京リージョンは使えない
SCP で iam:*Provider* が拒否される GitHub Actions の OIDC 連携が作れない → 最小権限の IAM ユーザー+アクセスキーを GitHub Secrets に保存(定期ローテーションが課題)
CloudShell が「アカウント確認中」で起動しない Terraform は GitHub Codespaces で実行

OIDC が使えないのは悔しいですが、「なぜアクセスキーを選んだか」「そのリスクにどう対処しているか」を説明できるようにしました。

Terraform:作り直さずに import する

バックエンドはコンソールで手作業で作っていたので、Terraform 1.5 以降の import ブロックと -generate-config-out で既存リソースをコードに起こしました。

環境と権限

  • 実行場所:GitHub Codespaces(ブラウザ上の Linux)
  • 認証:ReadOnlyAccess + state バケットへの書き込みだけを許可した専用 IAM ユーザー。import と plan は読み取りだけで済むため
  • state:専用の S3 バケット(バージョニング有効、use_lockfile = true で S3 ネイティブロック)

state をサイト用バケットに置かなかったのは、デプロイで aws s3 sync --delete を使っているからです。同じバケットに置くと、次のデプロイで state が消えます。

手順

# imports.tf(抜粋)
import {
  to = aws_dynamodb_table.visitor_count
  id = "visitor-count"
}
import {
  to = aws_apigatewayv2_stage.default
  id = "<api-id>/$default"
}
terraform init
terraform plan -generate-config-out=generated.tf

自動生成されたコードでは 2 つのエラーが出ました。

  1. Lambda に filename がない:AWS からコードの zip 自体は取得できないため。コードは GitHub Actions がデプロイするので、Terraform は設定だけを管理するよう override ファイルで変更を無視しました。
# lambda_override.tf
resource "aws_lambda_function" "visitor_counter" {
  filename = "lambda.zip"
  lifecycle {
    ignore_changes = [filename, source_code_hash]
  }
}
  1. DynamoDB の recovery_period_in_days = 0:PITR を無効にしているテーブルで 0 が出力されるが、Terraform 側の許容範囲は 1〜35。該当行を削除して解決。

その後 terraform plan で

Plan: 9 to import, 0 to add, 0 to change, 0 to destroy.

となり、コードと実環境の差分ゼロを確認してから apply しました。

import で見つかった過剰権限

生成されたコードを読んで気づいたのが、Lambda の実行ロールに PowerUserAccess(IAM 以外のほぼ全操作)が付いていたことです。カウンターに必要なのは DynamoDB の UpdateItem とログ出力だけです。

そこで Terraform で専用ロールを作って置き換えました。

resource "aws_iam_role_policy" "visitor_counter_dynamodb" {
  name = "visitor-count-update"
  role = aws_iam_role.visitor_counter.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = "dynamodb:UpdateItem"
      Resource = aws_dynamodb_table.visitor_count.arn
    }]
  })
}

# 元のロールは削除せず、Terraform の管理対象から外すだけ
removed {
  from = aws_iam_role.lambda_role
  lifecycle {
    destroy = false
  }
}
  • 変更は PR で出し、plan(3 to add, 1 to change, 0 to destroy)を確認してから apply
  • apply のときだけ、対象のロールと Lambda に絞った書き込み権限を一時的に付与し、終わったら外しました
  • apply 後に API を叩いてカウンターが動くことを確認

コンソールで作ったものは「何が設定されているか」が見えにくく、コード化して初めて気づけた問題でした。

まとめ・今後

  • 運用で見ていた CloudWatch Logs、Lambda、API Gateway を自分で作ることで、「どこを見れば何が分かるか」の解像度が上がりました
  • Terraform は新規構築より、既存環境の import → 差分ゼロ確認 → 安全に修正の流れが実務に近いと感じました

今後は CloudWatch アラームによる監視、S3・CloudFront の Terraform 化、AWS SAA の取得に取り組みます。

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?