本記事は 春のスキルアップ応援フェア2026 4/21付の記事です。 |
こんにちは。SCSK渡辺(大)です。
「動くけどAWS Well-Architected じゃない」環境をAIに作らせ、直すことでベストプラクティスを学んでみました。
個人的には非常に勉強になりました。
全体の流れ
- デプロイ: アンチパターン入りCFNテンプレートを特定のリージョンでデプロイ
- 修正: AWSマネジメントコンソールでアンチパターンを見つけて修正
- クリーンアップ: スタック削除(必要に応じてスタック外リソース削除)
問題用に作るもの
YAMLのテンプレート1つで(AWS CloudFormationスタック1つで)以下のリソースを作ります。
これらにAWS Well-Architectedのベストプラクティスに反するアンチパターンが仕込まれています。
| 論理ID | リソース種類 | 関連する問題 |
|---|---|---|
| ChallengeVPC | VPC | A1, B2 |
| PrivateSubnetA / B | サブネット × 2 | B2 |
| PrivateRouteTable | ルートテーブル | B2 |
| EC2SecurityGroup | セキュリティグループ | B2 |
| RDSSecurityGroup | セキュリティグループ | B2 |
| ChallengeBucket | S3バケット | B1, M3, B2 |
| AccessLogBucket | S3バケット | B1, B2 |
| EC2Role | IAMロール | M1, B2 |
| EC2InstanceProfile | インスタンスプロファイル | — |
| ChallengeInstance | EC2インスタンス | A2, B2 |
| RDSSubnetGroup | DBサブネットグループ | B2 |
| ChallengeDB | RDSインスタンス | B3, M2, B2 |
| ChallengeTable | DynamoDBテーブル | A3, B2 |
| ChallengeLogGroup | CloudWatch Logsロググループ | B2 |
| FlowLogsLogGroup | CloudWatch Logsロググループ | A1, B2 |
⚠ 各リソースの備考を見る(ネタバレ注意)
| 論理ID | 備考 |
|---|---|
| ChallengeVPC | フローログ無効 |
| PrivateSubnetA / B | Multi-AZ用プライベートサブネット |
| PrivateRouteTable | — |
| EC2SecurityGroup | EC2用、インバウンドなし |
| RDSSecurityGroup | RDS用、VPC内部のみ |
| ChallengeBucket | アクセスログ無効、バージョニング無効 |
| AccessLogBucket | アクセスログの保管先として使用 |
| EC2Role | Action/Resource全開 |
| EC2InstanceProfile | EC2Roleに紐づく(タグ非対応) |
| ChallengeInstance | EBS gp2、暗号化なし |
| RDSSubnetGroup | タグエディタからは見つけにくい |
| ChallengeDB | Single-AZ、バックアップ無効 |
| ChallengeTable | 過剰キャパシティ、Auto Scalingなし |
| ChallengeLogGroup | 保持期間未設定(無期限) |
| FlowLogsLogGroup | VPCフローログの送信先として使用 |
問題構成
9問構成(初級3問・中級3問・上級3問)です。
ヒントは3段階で用意しています。
| 難易度 | 問題の頭文字 | 主な柱 |
|---|---|---|
| 初級 | B (Beginner) | セキュリティ、運用上の優秀性、信頼性 |
| 中級 | M (Middle) | セキュリティ、信頼性 |
| 上級 | A (Advanced) | セキュリティ、コスト最適化、パフォーマンス効率、運用上の優秀性 |
デプロイ
以下のテンプレートをAWS
CloudFormationで特定のリージョンにデプロイしてください。
デプロイは10分以内で完了します(RDSの作成に時間がかかります)。
リソースが作成される場合があることを承認します。」のチェックボックスにチェックを入れてください。
テンプレートにIAMロールが含まれているため必要です。
▶ wa-challenge.yaml(クリックで展開)
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Well-Architected Anti-Pattern Challenge.
This template contains anti-patterns that violate Well-Architected best practices.
Find and fix them using the AWS Management Console.
Parameters:
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
Resources:
ChallengeVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref ChallengeVPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs '']
PrivateSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref ChallengeVPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [1, !GetAZs '']
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref ChallengeVPC
PrivateSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetA
RouteTableId: !Ref PrivateRouteTable
PrivateSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetB
RouteTableId: !Ref PrivateRouteTable
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: EC2 Security Group - No inbound access
VpcId: !Ref ChallengeVPC
SecurityGroupIngress: []
SecurityGroupEgress:
- IpProtocol: '-1'
CidrIp: 10.0.0.0/16
RDSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: RDS Security Group - VPC internal only
VpcId: !Ref ChallengeVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref EC2SecurityGroup
SecurityGroupEgress:
- IpProtocol: '-1'
CidrIp: 10.0.0.0/16
ChallengeBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
AccessLogBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: OverlyPermissivePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
ChallengeInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3.micro
ImageId: !Ref LatestAmiId
SubnetId: !Ref PrivateSubnetA
SecurityGroupIds:
- !Ref EC2SecurityGroup
IamInstanceProfile: !Ref EC2InstanceProfile
PropagateTagsToVolumeOnCreation: true
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 20
VolumeType: gp2
Encrypted: false
DeleteOnTermination: true
RDSSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Challenge RDS Subnet Group
SubnetIds:
- !Ref PrivateSubnetA
- !Ref PrivateSubnetB
ChallengeDB:
Type: AWS::RDS::DBInstance
DeletionPolicy: Delete
Properties:
DBInstanceClass: db.t3.micro
Engine: mysql
EngineVersion: '8.0'
MasterUsername: admin
MasterUserPassword: ChallengePw123!
AllocatedStorage: '20'
DBSubnetGroupName: !Ref RDSSubnetGroup
VPCSecurityGroups:
- !Ref RDSSecurityGroup
PubliclyAccessible: false
MultiAZ: false
BackupRetentionPeriod: 0
StorageEncrypted: false
ChallengeTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Delete
Properties:
TableName: ChallengeTable
AttributeDefinitions:
- AttributeName: PK
AttributeType: S
KeySchema:
- AttributeName: PK
KeyType: HASH
BillingMode: PROVISIONED
ProvisionedThroughput:
ReadCapacityUnits: 100
WriteCapacityUnits: 100
ChallengeLogGroup:
Type: AWS::Logs::LogGroup
DeletionPolicy: Delete
Properties:
LogGroupName: /challenge/application
FlowLogsLogGroup:
Type: AWS::Logs::LogGroup
DeletionPolicy: Delete
Properties:
LogGroupName: /vpc/flow-logs
Outputs:
VPCId:
Value: !Ref ChallengeVPC
FlowLogsLogGroupName:
Value: !Ref FlowLogsLogGroup
S3BucketName:
Value: !Ref ChallengeBucket
AccessLogBucketName:
Value: !Ref AccessLogBucket
EC2InstanceId:
Value: !Ref ChallengeInstance
RDSEndpoint:
Value: !GetAtt ChallengeDB.Endpoint.Address
DynamoDBTableName:
Value: !Ref ChallengeTable
問題に取り組む
デプロイが完了したら、AWSマネジメントコンソールで東京リージョンを選択し、以下の問題を見ながらアンチパターンを見つけて修正してください。
スタックの「リソース」タブから各種リソースを参照しにいくとスムーズに進められます。
初級問題
B1: S3バケットのアクセスログ
柱: セキュリティ
問題: デプロイされたS3バケットにはWell-Architectedセキュリティ柱の検出に関するベストプラクティスに反する設定があります。見つけて修正してください。
関連するWell-Architectedガイド: SEC04-BP02 Capture logs, findings, and metrics in standardized locations
💡 ヒント1(方向性)
誰がいつバケットにアクセスしたかを追跡するための設定を確認してください。S3バケットのプロパティを見てみましょう。
💡 ヒント3(ほぼ答え)
S3コンソール → バケットを選択 → 「プロパティ」→ 「サーバーアクセスのログ記録」を編集 → 「有効にする」を選択し、ログの送信先にスタックで作成済みのアクセスログ用バケット(AccessLogBucket)を指定してください。
B2: リソースのタグ付け
柱: 運用上の優秀性 + コスト最適化
問題: デプロイされたリソース群にはWell-Architected運用上の優秀性の柱に反する共通の問題があります。見つけて修正してください。
注意:
APIレート制限にご注意ください。
関連するWell-Architectedガイド: COST04-BP01 Track resources over their lifetime
💡 ヒント1(方向性)
リソースの管理・追跡・コスト配分に必要な基本的な設定が欠けています。複数のリソースに共通する問題です。
💡 ヒント3(ほぼ答え)
全リソースに Environment, Project, Owner 等のタグを追加してください。各リソースのコンソール画面 → 「タグ」タブから追加できます。
注意:タグエディタで一括タグ付けする場合、リソースタイプでフィルタしてチャレンジのリソースだけを選択してください。アカウント内の全リソースを対象にするとAPIレート制限に抵触したり、マネージドルールなどタグ変更が禁止されているリソースでエラーになります。
B3: RDSの自動バックアップ
柱: 信頼性
問題: デプロイされたRDSインスタンスにはWell-Architected信頼性の柱のバックアップベストプラクティスに反する設定があります。見つけて修正してください。
関連するWell-Architectedガイド: REL09-BP03 Perform data backup automatically
💡 ヒント1(方向性)
障害時のデータ復旧に関するベストプラクティスを確認してください。RDSの設定を見てみましょう。
中級問題
M1: IAMの最小権限
柱: セキュリティ
問題: デプロイされたIAMロールにはWell-Architectedセキュリティ柱のアクセス管理ベストプラクティスに反する設定があります。見つけて修正してください。
関連するWell-Architectedガイド: SEC03-BP02 Grant least privilege access
💡 ヒント1(方向性)
IAMのアクセス権限に関するベストプラクティスを確認してください。EC2に割り当てられたロールのポリシーを見てみましょう。
💡 ヒント3(ほぼ答え)
IAMコンソール → ロール → EC2ロールを選択 → OverlyPermissivePolicy を編集し、必要なアクション/リソースのみに絞ってください。当チャレンジではEC2に特定の用途がないため、インラインポリシー OverlyPermissivePolicy を削除するか、AWS管理ポリシーAWSDenyAll をアタッチして全アクションを拒否する方法でも正解です。
M2: RDSの可用性
柱: 信頼性
問題: デプロイされたRDSインスタンスにはWell-Architected信頼性の柱の障害管理ベストプラクティスに反する設定があります。見つけて修正してください。(B3とは別の問題です)
関連するWell-Architectedガイド: Failure management – Reliability Pillar
💡 ヒント1(方向性)
単一障害点の排除に関するベストプラクティスを確認してください。
💡 ヒント3(ほぼ答え)
RDSコンソール → インスタンスを選択 → 「変更」→ 「可用性と耐久性」セクションで「マルチAZデプロイメント」を「スタンバイインスタンスを作成する」に変更してください。
M3: S3のデータ保護
柱: 信頼性 + セキュリティ
問題: デプロイされたS3バケットにはWell-Architectedのデータ保護ベストプラクティスに反する設定があります。見つけて修正してください。(B1とは別の問題です)
関連するWell-Architectedガイド: REL09-BP02 Secure and encrypt backups
💡 ヒント1(方向性)
データの誤削除からの保護に関するベストプラクティスを確認してください。
上級問題
A1: ネットワーク監査ログ
柱: セキュリティ + 運用上の優秀性
問題: デプロイされたVPCにはWell-Architectedセキュリティ柱の検出に関するベストプラクティスに反する設定があります。見つけて修正してください。
関連するWell-Architectedガイド: Security Pillar – Detection
💡 ヒント1(方向性)
ネットワークトラフィックの監査・可視化に関するベストプラクティスを確認してください。
💡 ヒント2(具体的なガイドリンク)
VPCフローログが無効で、ネットワークトラフィックの監査ができません。
💡 ヒント3(ほぼ答え)
VPCコンソール → VPCを選択 → 「フローログ」タブ → 「フローログを作成」で以下を設定してください:
- 名前 – オプション: 任意(空欄でもOK)
- フィルタ: すべて
- 最大集約間隔: 10分(デフォルトのまま)
- 送信先: CloudWatch Logsに送信
- 送信先ロググループ: スタックで作成済みの
/vpc/flow-logsを選択 - IAMロール: 「Set Up Permissions」から新規作成、または既存のフローログ用ロールを選択
- ログレコードの形式: AWSのデフォルト形式(デフォルトのまま)
- 追加のメタデータ: なし(デフォルトのまま)
A2: EBSストレージの最適化
柱: コスト最適化 + セキュリティ
問題: デプロイされたEC2インスタンスのEBSボリュームにはWell-Architectedのコスト最適化とセキュリティのベストプラクティスに反する設定があります。見つけて修正してください。
関連するWell-Architectedガイド:
💡 ヒント1(方向性)
ストレージの世代選択とデータ保護の2つの観点で確認してください。
💡 ヒント3(ほぼ答え)
gp2→gp3の変更: EC2コンソール → ボリューム → ボリュームを選択 → 「変更」→ タイプをgp3に変更 → 「変更」をクリック。
暗号化の対応: 既存ボリュームでは暗号化を直接有効にできないため、以下の手順が必要です:
- EC2コンソール → インスタンス → 対象インスタンスを「停止」する
- ボリューム → 対象ボリュームを選択 → 「スナップショットを作成」
- スナップショット → 作成したスナップショットを選択 → 「スナップショットをコピー」→ 「暗号化」にチェック → コピー
- 暗号化済みスナップショットを選択 → 「スナップショットからボリュームを作成」→ AZを元のボリュームと同じにする
- 元のボリュームをインスタンスからデタッチ
- 暗号化済みボリュームをインスタンスにアタッチ(デバイス名:
/dev/xvda) - インスタンスを起動
暗号化の手順は複雑なため、gp2→gp3の変更だけでも正解とします。
A3: DynamoDBのキャパシティ管理
柱: コスト最適化 + パフォーマンス効率
問題: デプロイされたDynamoDBテーブルにはWell-Architectedのコスト最適化とパフォーマンス効率のベストプラクティスに反する設定があります。見つけて修正してください。
関連するWell-Architectedガイド:
💡 ヒント1(方向性)
データベースのキャパシティ管理とコスト効率に関するベストプラクティスを確認してください。
💡 ヒント2(具体的なガイドリンク)
DynamoDBがプロビジョンドモードで過剰なRCU/WCU(各100)が設定されており、Auto Scalingも無効です。
💡 ヒント3(ほぼ答え)
DynamoDBコンソール → テーブルを選択 → 「追加の設定」→ 「読み込み/書き込みキャパシティ」セクションの「編集」をクリック。
以下のいずれかを実施してください:
方法1: オンデマンドモードに変更(推奨)
- キャパシティモードを「オンデマンド」に変更
- 「変更を保存」をクリック
方法2: Auto Scalingを有効化
- キャパシティモードは「プロビジョンド」のまま
- 読み込みキャパシティ: 「Auto Scaling」をオン → 最小キャパシティを1〜5程度に設定
- 書き込みキャパシティ: 同様に「Auto Scaling」をオン → 最小キャパシティを1〜5程度に設定
- 「変更を保存」をクリック
クリーンアップ
CloudFormationコンソールからスタックを削除してください。
AccessLogBucket と ChallengeBucketの中身を先に空にしてからスタックを削除してください。S3コンソール → バケットを選択 → 「空にする」で削除できます。
スタック外リソースの手動削除
問題を解く過程で、スタック管理外のリソースが作成されている場合があります。これらはスタック削除では消えないため、手動で削除してください。
⚠ スタック外リソースの一覧と削除手順(ネタバレ注意)
| 関連問題 | リソース | 削除手順 |
|---|---|---|
| A1 | VPCフローログ | VPCコンソール → VPCを選択 → 「フローログ」タブ → フローログを選択 → 「アクション」→「フローログの削除」 |
| A1 | VPCフローログ用IAMロール | IAMコンソール → ロール → VPCFlowLogs-Cloudwatch-* のようなロールを検索 → 削除 |
| A2 | EBSスナップショット(暗号化対応した場合) | EC2コンソール → スナップショット → 作成したスナップショットを選択 → 「アクション」→「スナップショットの削除」 |
| A2 | デタッチ済みEBSボリューム(暗号化対応した場合) | EC2コンソール → ボリューム → 状態が「available」のボリュームを選択 → 「アクション」→「ボリュームの削除」 |
まとめ
テンプレートや問題文はAIに作ってもらいましたが、実際に自分で修正を試してみると、S3のデフォルト暗号化が既に有効になっていて問題として成立しなかったり(この問題は削除してもらいました)、ヒントの手順が現在のコンソールUIと合っていなかったりと、そのままでは使えない箇所がいくつもありました。
AIが生成したものを鵜呑みにせず、自分で手を動かして検証することの大切さを改めて感じました。
Well-Architected Framework は読むだけでは実感が湧きにくいですが、「動くけどベストプラクティスじゃない環境」を自分の手で直すことで、各ベストプラクティスの意味が体感できました。
「M2: RDSの可用性」ではMulti-AZ化の過程で、定期メンテナンスウィンドウの設定や延期方法についても学ぶことができました。
また「B1: S3バケットのアクセスログ」では、ログの蓄積をきっかけにS3ライフサイクルポリシーやストレージクラス移行時の料金体系についても理解が深まりました。
このように、1つのアンチパターンを修正する過程で関連するベストプラクティスにも自然と触れることができ、Well-Architectedの学習が点ではなく面で広がっていく実感がありました。


EC2・RDSはプライベートサブネット配置、S3はパブリックアクセス完全ブロック、セキュリティグループはインバウンドなしです。
コストは東京リージョンで24時間以内に削除した場合、約200〜300円です。終わったら必ずスタック削除してください。