1. はじめに
本記事の前提条件
本記事は、個人の検証環境や小規模開発向けなど、スタンドアロン(単一)のAWSアカウントのセットアップを対象としています。
企業システムなどで複数アカウントを統合管理(Landing Zoneの構築など)する場合は、AWS Control TowerやIAM Identity Centerの利用を推奨します。
AWSアカウントのセットアップはAWSを安全に利用する上で重要な作業ですが、AWSマネジメントコンソールでの手動構築は必要な手順が多く、設定漏れや手順の前後によるミスを引き起こす可能性があります。
そこで、AWSアカウント作成後に必要なセットアップを可能な限りAWS CloudFormationで作成してみました。
本記事では、AWS CloudFormationでの設定ができないもしくは推奨されない、AWSマネジメントコンソールでの操作が必要なセットアップについても掲載しているため、
- 「AWSを使ってみたいけど、アカウントを作成していない」
- 「AWSアカウントを作成したけど、何も設定していない」
という方は、ぜひ本記事を参考にAWSアカウントをセットアップしてみてください!
本記事では、AWSアカウントのセットアップとして、
- マネジメントコンソール: ルートユーザーのMFA有効化
- マネジメントコンソール: IAMユーザーの請求情報へのアクセス有効化
- マネジメントコンソール: 管理者権限を持ったIAMユーザーの作成
- マネジメントコンソール: 管理者ユーザーのMFA有効化
- マネジメントコンソール: AWS Cost Explorerの有効化
- マネジメントコンソール: ローカリゼーションとデフォルトのリージョンを編集
- CloudFormation: AWS Budgetsの予算作成
- CloudFormation: Amazon GuardDutyの有効化
- CloudFormation: AWS CloudTrailの証跡作成
- CloudFormation: AWS Configの有効化
についてまとめています。
セットアップの種別について
セクションのタイトルに
-
「マネジメントコンソール」と書かれているセットアップ
→ AWSマネジメントコンソールでの操作が必要なセットアップ -
「CloudFormation」と書かれているセットアップ
→ AWS CloudFormationでの設定が可能なセットアップ
本記事に掲載されているセットアップを全て完了させることで、AWSのベストプラクティスに則った安全な環境を構築することができます。安全で快適なAWSライフの最初の一歩を爆速で踏み出しましょう!
発言は個人の見解に基づくものであり、所属組織を代表するものではありません。
2. AWSマネジメントコンソールでのセットアップ
本セクションでは、
- AWS CloudFormationでは設定できないセットアップ
- AWS CloudFormationでの設定が推奨されないセットアップ
についてまとめています。
2.1 マネジメントコンソール: ルートユーザーのMFA有効化
2.1.1 目的
AWSアカウントのルートユーザーは、全ての操作が可能な最強の権限を保有しています。もし、ルートユーザーが乗っ取られた場合、アカウント内の全リソースが危険に晒され、多額の請求やデータ損失を引き起こす可能性があります。アカウント全体の乗っ取りリスクを最小化するためにも、必ずMFAを有効化しましょう。
MFA (Multi Factor Authentication) とは
Webサイトやアプリケーションなどにログインする時に、2つ以上の認証要素の提示を求める認証方法
2.1.2 手順
- ルートユーザーでAWSマネジメントコンソールにサインイン
- 右上のアカウント名を展開し、「セキュリティ認証情報」をクリック
- 「多要素認証 (MFA)」の「MFAデバイスの割り当て」をクリック
- 案内に沿ってMFAデバイスを設定
MFAデバイスについては、個人の環境に合わせてお好きなものをご利用ください。
ちなみに、私はGoogle Authenticatorを利用しています。
2.1.3 確認
「多要素認証 (MFA)」に設定したMFAデバイスが登録されていれば、本設定は完了です。
2.2 マネジメントコンソール: IAMユーザーの請求情報へのアクセス有効化
2.2.1 目的
デフォルトの設定では、AWSの請求情報はルートユーザーしか閲覧することができません。しかし、ルートユーザーを日常的に使用することは、セキュリティ上のリスクが懸念されるため、AWSでは推奨されていません。
そこで、IAMユーザーの請求情報へのアクセスを有効化することで、ルートユーザーを封印したまま、IAMユーザーで請求情報を確認するという安全な運用が可能になります。
2.2.2 手順
- ルートユーザーでAWSマネジメントコンソールにサインイン
- 右上のアカウント名を展開し、「アカウント」をクリック
- 「IAMユーザーおよびロールによる請求情報へのアクセス」の「編集」をクリック
- 「IAMアクセスをアクティブ化」を有効化し、「更新」をクリック
2.2.3 確認
「IAMユーザーおよびロールによる請求情報へのアクセス」が有効化されていれば、本設定は完了です。
2.3 マネジメントコンソール: 管理者権限を持ったIAMユーザーの作成
2.3.1 目的
AWSのベストプラクティスでは、ルートユーザーの認証情報が必要なタスクを除き、AWSアカウントのルートユーザーにはアクセスしないことが強く推奨されています。
そのため、管理者権限を持ったIAMユーザーを作成し、ルートユーザーは封印した状態で管理者ユーザーを軸として運用していきましょう。
2.3.2 手順
ログイン
- ルートユーザーでAWSマネジメントコンソールにサインイン
- 上部の検索ボックスに
IAMと入力し、IAMをクリック
ユーザーグループの作成
- 左のメニューから「ユーザーグループ」をクリック
- 「グループを作成」をクリック
- 任意のユーザーグループ名を入力
- 「許可ポリシーを添付」の検索ボックスに
AdministratorAccessと入力し、AdministratorAccessを選択 - 「ユーザーグループを作成」をクリック
AdministratorAccess とは
AWSサービスとリソースへのフルアクセスを提供するAWSマネージドポリシー
ユーザーの作成
- 左のメニューから「ユーザー」をクリック
- 「ユーザーの作成」をクリック
- 任意のユーザー名を入力
- 「AWSマネジメントコンソールへのユーザーアクセスを提供する」を有効化
- 「人にコンソールアクセスを提供していますか?」の「IAMユーザーを作成します」を有効化
- 「コンソールパスワード」の「カスタムパスワード」を有効化し、任意のパスワードを入力
- 「ユーザーは次回のサインイン時に新しいパスワードを作成する必要があります」を無効化し、「次へ」をクリック
- 「許可のオプション」の「ユーザーをグループに追加」を選択
- 先ほど作成したユーザーグループを選択し、「次へ」をクリック
- 作成するユーザーの詳細を確認し、「ユーザーの作成」をクリック
コンソールパスワードについて
「自動生成されたパスワード」を利用する場合は、ユーザー作成後に表示されるパスワードを記録するか、「ユーザーは次回のサインイン時に新しいパスワードを作成する必要があります」を有効化し、サインイン時に任意のパスワードを設定するようにしてください。
2.3.3 確認
「ユーザー」に作成した管理者ユーザーが表示されているかつ、ユーザー名をクリックした先で許可ポリシーにAdministratorAccessが表示されていれば、本設定は完了です。
2.4 マネジメントコンソール: 管理者ユーザーのMFA有効化
2.4.1 目的
管理者ユーザーは、ルートユーザーに次ぐ強力な権限を持ちます。そのため、管理者ユーザーにもMFAを有効化し、不正ログインを防止しましょう。
2.4.2 手順
- ルートユーザーからサインアウトする
- 先ほど作成した管理者ユーザーでAWSマネジメントコンソールにサインイン
- 2.1 マネジメントコンソール: ルートユーザーのMFA有効化と同じ手順で、管理者ユーザーのMFAを有効化
2.4.3 確認
「多要素認証 (MFA) 」に設定したMFAデバイスが登録されていれば、本設定は完了です。
2.5 マネジメントコンソール: AWS Cost Explorerの有効化
2.5.1 目的
AWS Cost Explorerを有効化することで、AWSのコストと使用料を可視化し、効率的に管理することができるようになります。AWS Cost Explorerは初回アクセスから24時間後に有効化されるため、セットアップ後すぐにアクセスし、できるだけ早く有効化しましょう。
2.5.2 手順
- 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 右上のアカウント名を展開し、「Billing and Cost Management」をクリック
- 左のメニューから「Cost Explorer」をクリック
- 「24時間後にもう一度確認してください。」というメッセージが表示されるため、案内に従って24時間後に再度AWS Cost Explorerを確認
2.5.3 確認
初回アクセスから24時間後にAWS Cost Explorerを確認し、AWS Cost Explorerが有効化されていれば、本設定は完了です。
2.6 マネジメントコンソール: ローカリゼーションとデフォルトのリージョンを編集
2.6.1 目的
AWSはリージョン単位でリソースが分離されているため、意図しないリージョンでのリソース作成は、管理漏れや不要な課金を招く要因となります。
そのため、デフォルトを物理的に近い東京リージョン (ap-northeast-1)に固定することは、設定ミスの防止だけでなく、マネジメントコンソールやCLIのレスポンス性能を最大化し、ストレスのない運用環境を構築する上で非常に有効です。
2.6.2 手順
- 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 右上の歯車マークをクリック
- 「ローカリゼーションとデフォルトのリージョン」の「編集」をクリック
- 「言語」に「日本語」、「デフォルトのリージョン」に「アジアパシフィック (東京) ap-northeast-1」を選択
- 「設定を保存」をクリック
2.6.3 確認
右上の歯車マークをクリックし、「ローカリゼーションとデフォルトのリージョン」の「言語」が「日本語」、「デフォルトのリージョン」が「アジアパシフィック (東京) ap-northeast-1」と表示されていれば、本設定は完了です。
3. AWS CloudFormationでのセットアップ
本セクションでは、
- AWS CloudFormationでの作成が可能なセットアップ
についてまとめています。
AWSマネジメントコンソールでのセットアップについても記載しておりますので、コンソール画面での設定がお好みの方はぜひご活用ください。
3.1 AWS CloudFormationとは
yamlやjsonのテンプレートファイルを利用して、AWSのリソースをプロビジョニングし、管理することができるIaC (Infrastructure as Code) サービス
IaC (Infrastructure as Code) とは
コードを使用してインフラストラクチャの構築・管理を行うこと
3.1.1 メリット
構築の再現性と一貫性を担保できる
- 手動構築では避けられない設定漏れや手順の前後によるミスを排除できる
- 開発・テスト・本番など、同一構成の環境を即座かつ寸分違わず複製できる
リソースのライフサイクルを一括管理できる
- 複数のリソースをスタックという一つの単位で扱うため、プロジェクトの終了時に、不要になったリソースを一つずつ消して回る必要がない
- スタックを削除するだけで、依存関係を考慮しながら安全に全リソースをクリーンアップできる
ドリフト検出により設定の乖離を把握できる
- マネジメントコンソールから変えてしまった設定をドリフト検出で検出できる
- コードで定義したあるべき姿と現在の実際の設定の差分を瞬時に特定できる
3.1.2 デメリット
初期の学習コストと独自記法への慣れが必要
- プログラミング言語とは異なる独自の関数や、リソースごとの詳細なプロパティ定義を覚える必要がある
- 最初はドキュメントの理解に時間を要するため、手動構築よりも時間がかかる
エラー発生時のロールバックとデバッグの難しさ
- スタックの作成や更新に失敗した際のロールバックの待ち時間が長い
- エラーログからエラー箇所を特定するのが難しい
最新機能や特定リソースへの対応にラグがある
- AWSの全リソースが、発表と同時にAWS CloudFormationでサポートされるわけではない
- 最新の機能や設定項目をすぐに使いたい場合は、AWS CloudFormation側が対応するまで待つか、カスタムリソースを自作して対応するといった回避策が必要になる
3.1.3 本記事での使い方
- 各セクションに掲載されているCloudFormationテンプレートを
任意のファイル名.yamlとして自身の環境に保存 - 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 上部の検索ボックスに
CloudFormationと入力し、CloudFormationをクリック - 左のメニューから「スタック」をクリック
- 「スタックの作成」を選択し、「新しいリソースを使用 (標準)」をクリック
- 「テンプレートの準備」の「既存のテンプレートを選択」を選択
- 「テンプレートの指定」の「テンプレートファイルのアップロード」を選択
- 「テンプレートファイルのアップロード」の「ファイルの選択」をクリック
- 先ほど保存した
任意のファイル名.yamlをアップロードし、「次へ」をクリック - 任意のスタック名とパラメータを入力し、「次へ」をクリック
- そのまま「次へ」をクリック
- 詳細を確認し、「送信」をクリック
3.2 CloudFormation: AWS Budgetsの予算作成
3.2.1 目的
AWSは従量課金制のため、気づかないうちに請求が膨れ上がってしまったというケースがあります。そういった場合に備えて、AWS Budgetsを設定しておくことで、現在の利用料と月額の予測料金を把握し、予算を超えそうもしくは超えたときに通知を受け取ることができます。
本記事で掲載しているテンプレートでは、月末に予算の超過が予測された時と、実際に予算を超過した時にメールとしてユーザーに通知するよう、リソースを定義しています。
![]() |
| CloudFormationテンプレートで作成されるアーキテクチャ |
3.2.2 CloudFormationで実施する場合
- 下記のCloudFormationテンプレートを
任意のファイル名.yaml(例:Budget.yaml)として自身の環境に保存 - 3.1.3 使い方の2から8まで実施
- 先ほど保存した
Budget.yamlをアップロードし、「次へ」をクリック - 任意のスタック名 (例:
Budget)と、MonthlyBudgetLimitとNotificationEmailのパラメータを入力 - 3.1.3 使い方の11から12まで実施
- スタックのステータスが
CREATE_COMPLETEとなれば、スタックの作成は成功
各パラメータの説明
MonthlyBudgetLimit
- 月の予算 (注意: JPYではなくUSD)
- 本テンプレートでは、月末に予算の超過が予測された時と、実際に予算を超過した時に通知
NotificationEmail
- 通知を受け取るメールアドレス
# Template description
AWSTemplateFormatVersion: "2010-09-09"
Description: Budget with email notification
# Parameters
Parameters:
# Monthly budget limit (USD)
MonthlyBudgetLimit:
Type: Number
Description: Monthly budget limit (USD)
MinValue: 1
# Email address to receive budget alerts
NotificationEmail:
Type: String
Description: Email address to receive budget alerts
AllowedPattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
# Resources
Resources:
# Monthly cost budget
MonthlyCostBudget:
Type: AWS::Budgets::Budget
Properties:
Budget:
BudgetName: MonthlyCostBudget
BudgetLimit:
Amount: !Ref MonthlyBudgetLimit
Unit: USD
BudgetType: COST
TimeUnit: MONTHLY
NotificationsWithSubscribers:
- Notification:
NotificationType: FORECASTED
ComparisonOperator: GREATER_THAN
Threshold: 100
ThresholdType: PERCENTAGE
Subscribers:
- SubscriptionType: EMAIL
Address: !Ref NotificationEmail
- Notification:
NotificationType: ACTUAL
ComparisonOperator: GREATER_THAN
Threshold: 100
ThresholdType: PERCENTAGE
Subscribers:
- SubscriptionType: EMAIL
Address: !Ref NotificationEmail
# Outputs
Outputs:
# Monthly cost budget
MonthlyCostBudgetOutput:
Description: Budget name
Value: !Ref MonthlyCostBudget
# Monthly budget limit (USD)
MonthlyBudgetLimitOutput:
Description: Monthly budget limit (USD)
Value: !Ref MonthlyBudgetLimit
# Email address to receive budget alerts
NotificationEmailOutput:
Description: Email address to receive budget alerts
Value: !Ref NotificationEmail
3.2.3 マネジメントコンソールで実施する場合
手順 (クリックすると展開されます)
- 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 右上のアカウント名を展開し、「Billing and Cost Management」をクリック
- 左のメニューから「予算」をクリック
- 「予算を作成」をクリック
- 「予算の設定」の「カスタマイズ (アドバンスド)」を選択
- 「予算タイプ」の「コスト予算 - 推奨」を選択し、「次へ」をクリック
- 「詳細」の「予算名」に任意の予算名を入力
- 「予算額を設定」の「予算額 ($) を入力してください」に任意の予算額 (例:
MonthlyCostBudget)を入力し、「次へ」をクリック - 「アラートのしきい値を追加」をクリック
- 「しきい値」を
100と予算額の%とし、「トリガー」を実際、「Eメールの受信者」に通知を受け取るメールアドレスを入力 - 「アラートのしきい値を追加」を再度クリック
- 「しきい値」を
100と予算額の%とし、「トリガー」を予測、「Eメールの受信者」に通知を受け取るメールアドレスを入力し、「次へ」をクリック - 再度「次へ」をクリック
- 詳細を確認し、「予算の作成」をクリック
3.2.4 確認
予算の一覧に作成した予算が表示されていれば、本設定は完了です。
3.3 CloudFormation: Amazon GuardDutyの有効化
3.3.1 目的
Amazon GuardDutyは、AWSの脅威検知サービスであり、Amazon GuardDutyを有効にしておくことで、アカウント侵害の早期発見が可能になります。
本記事で掲載しているテンプレートでは、Amazon GuardDutyの有効化に加えて、
- Amazon Simple Notification Serviceトピックとサブスクリプション
- Amazon EventBridgeルール
についても定義しています。
そのため、Amazon GuardDutyのイベントをAmazon EventBridgeで検知し、Amazon Simple Notification Serviceのメールとしてユーザーに通知することができます。
![]() |
| CloudFormationテンプレートで作成されるアーキテクチャ |
3.3.2 CloudFormationで実施する場合
- 下記のCloudFormationテンプレートを
任意のファイル名.yaml(例:GuardDuty.yaml)として自身の環境に保存 - 3.1.3 使い方の2から8まで実施
- 先ほど保存した
GuardDuty.yamlをアップロードし、「次へ」をクリック - 任意のスタック名 (例:
GuardDuty)と、NotificationEmailのパラメータを入力 - 3.1.3 使い方の11から12まで実施
- スタックのステータスが
CREATE_COMPLETEとなれば、スタックの作成は成功 -
NotificationEmailとして設定したメールアドレスに「AWS Notification - Subscription Confirmation」というタイトルのメールが届くため、メール本文にある「Confirm subscription」のリンクをクリック - リンク先で「Subscription confirmed!」と表示されることを確認
既にAmazon GuardDutyを有効化している場合、スタックの作成前にAmazon GuardDutyを無効化する必要があります。
手順と確認 (クリックすると展開されます)
手順
- 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 上部の検索ボックスに
GuardDutyと入力し、GuardDutyをクリック - 左のメニューから「設定」をクリック
- 「GuardDutyを無効にする」の「無効にする」をクリック
- 確認画面にて再度「無効にする」をクリック
確認
Amazon GuardDutyの設定画面を表示した時に、初期状態の画面が表示されれば、Amazon GuardDutyの無効化は完了です。
各パラメータの説明
NotificationEmail
- 通知を受け取るメールアドレス
# Template description
AWSTemplateFormatVersion: "2010-09-09"
Description: Enable GuardDuty and notify via EventBridge and SNS
# Parameters
Parameters:
NotificationEmail:
Type: String
Description: Email address to receive GuardDuty alerts
AllowedPattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
# Resources
Resources:
# GuardDuty Detector
GuardDutyDetector:
Type: AWS::GuardDuty::Detector
Properties:
Enable: true
# GuardDuty SNS Topic
GuardDutySNSTopic:
Type: AWS::SNS::Topic
DependsOn: GuardDutyDetector
Properties:
DisplayName: GuardDuty alerts SNS Topic
# GuardDuty SNS Subscription
GuardDutySNSSubscription:
Type: AWS::SNS::Subscription
DependsOn: GuardDutySNSTopic
Properties:
Protocol: email
Endpoint: !Ref NotificationEmail
TopicArn: !Ref GuardDutySNSTopic
# GuardDuty Event Rule
GuardDutyEventRule:
Type: AWS::Events::Rule
DependsOn: GuardDutySNSSubscription
Properties:
Description: Trigger SNS when GuardDuty finds a threat
EventPattern:
source:
- aws.guardduty
detail-type:
- GuardDuty Finding
State: ENABLED
Targets:
- Arn: !Ref GuardDutySNSTopic
Id: GuardDutySNSTarget
InputTransformer:
InputPathsMap:
severity: "$.detail.severity"
title: "$.detail.title"
type: "$.detail.type"
time: "$.time"
region: "$.region"
accountId: "$.detail.accountId"
description: "$.detail.description"
id: "$.detail.id"
InputTemplate: |
"【GuardDuty alert detected】"
"--------------------------------------------------"
"【Overviews】"
"Severity: <severity>"
"Title: <title>"
"Type: <type>"
"--------------------------------------------------"
"【Details】"
"Time: <time>"
"Region: <region>"
"Account ID: <accountId>"
"Description: <description>"
"--------------------------------------------------"
"Please check the details in the Management Console."
"https://console.aws.amazon.com/guardduty/home?region=<region>#/findings?macros=current&fId=<id>"
# Allow EventBridge to publish to SNS
AllowEventBridgeToPublishToSNS:
Type: AWS::SNS::TopicPolicy
DependsOn: GuardDutyEventRule
Properties:
Topics:
- !Ref GuardDutySNSTopic
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowEventBridgePublish
Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sns:Publish
Resource: !Ref GuardDutySNSTopic
# Outputs
Outputs:
# GuardDuty Detector
GuardDutyDetectorOutput:
Description: GuardDuty Detector
Value: !Ref GuardDutyDetector
# SNS Topic
GuardDutySNSTopicOutput:
Description: SNS topic for GuardDuty alerts
Value: !Ref GuardDutySNSTopic
3.3.3 マネジメントコンソールで実施する場合
手順 (クリックすると展開されます)
Amazon GuardDutyの有効化
- 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 上部の検索ボックスに
GuardDutyと入力し、GuardDutyをクリック - 「Try GuardDuty for free」の「Enable all GuardDuty features」を選択し、「今すぐ始める」をクリック
- 「GuardDutyを有効にする」をクリック
Amazon Simple Notification Serviceトピックの作成
- 上部の検索ボックスに
SNSもしくはSimple Notification Serviceと入力し、Simple Notification Serviceをクリック - 左のメニューから「トピック」をクリック
- 「トピックの作成」をクリック
- 「詳細」の「タイプ」は「スタンダード」を選択し、任意の名前 (例:
GuardDutySNSTopic)と表示名 (例:GuardDuty alerts SNS Topic)を入力 - 「アクセスポリシー」を展開し、「アドバンスド」を選択
- JSONエディタ内の
Statement配下に下記のJSONを追加 - 「トピックの作成」をクリック
{
"Sid": "AllowEventBridgePublish",
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "sns:Publish",
"Resource": "YOUR_SNS_TOPIC_ARN"
}
Amazon Simple Notification Serviceサブスクリプションの作成
- 左のメニューから「サブスクリプション」をクリック
- 「サブスクリプションの作成」をクリック
- 「トピックARN」に先ほど作成したトピックを選択し、「プロトコル」にEメールを選択
- 「エンドポイント」に通知を受け取るメールアドレスを入力し、「サブスクリプションの作成」をクリック
- 「エンドポイント」に入力したメールアドレスに「AWS Notification - Subscription Confirmation」というタイトルのメールが届くため、メール本文にある「Confirm subscription」のリンクをクリック
- リンク先で「Subscription confirmed!」と表示されることを確認
Amazon EventBridgeルールの作成
- 上部の検索ボックスに
EventBridgeもしくはAmazon EventBridgeと入力し、Amazon EventBridgeをクリック - 左のメニューから「ルール」をクリック
- 「ルールを作成」をクリック
- 「名前」に任意の名前 (例:
GuardDutyEventRule)、「説明」に任意の説明 (例:Trigger SNS when GuardDuty finds a threat)を入力 - 「イベントバス」は
defaultを選択し、「選択したイベントバスでルールを有効にする」を有効化した状態で「次へ」をクリック - 「イベントパターン」の「AWSのサービス」に
GuardDutyを選択 - 「イベントタイプ」に
GuardDuty Findingを選択し、「次へ」をクリック - 「ターゲット1」の「ターゲットを選択」でSNSトピックを選択
- 「トピック」は先ほど作成したトピックを選択
- 「追加設定」を開き、「ターゲット入力を設定」で入力トランスフォーマーを選択
- 「入力トランスフォーマーを設定」をクリック
- 「ターゲット入力トランスフォーマー」の入力パスとテンプレートにそれぞれ下記の
InputPathsMap.jsonとInputTemplate.txtを入力し、確認をクリック - 「次へ」をクリック
- 再度「次へ」をクリック
- 詳細を確認し、「ルールの作成」をクリック
{
"severity": "$.detail.severity",
"title": "$.detail.title",
"type": "$.detail.type",
"time": "$.time",
"region": "$.region",
"accountId": "$.detail.accountId",
"description": "$.detail.description",
"id": "$.detail.id"
}
"【GuardDuty alert detected】"
"--------------------------------------------------"
"【Overviews】"
"Severity: <severity>"
"Title: <title>"
"Type: <type>"
"--------------------------------------------------"
"【Details】"
"Time: <time>"
"Region: <region>"
"Account ID: <accountId>"
"Description: <description>"
"--------------------------------------------------"
"Please check the details in the Management Console."
"https://console.aws.amazon.com/guardduty/home?region=<region>#/findings?search=id=<id>"
3.3.4 確認
AWSマネジメントコンソールの左下にあるAWS CloudShellにて、Amazon GuardDutyのサンプルイベント発生用の下記コマンドを実行し、設定したメールアドレスにメールが届いていれば、本設定は完了です。
aws guardduty create-sample-findings \
--detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \
--finding-types "Backdoor:EC2/DenialOfService.Dns"
引用: https://dev.classmethod.jp/articles/create-a-single-sample-findings-in-guardduty/
3.4 CloudFormation: AWS CloudTrailの証跡作成
3.4.1 目的
AWS CloudTrailは、AWSのAPI操作ログを記録するサービスです。AWS CloudTrailを有効化することによって、「誰が」「いつ」「どのリソースに」「どんな操作をしたか」を把握することができます。セキュリティ事故や誤操作の調査に必須であり、AWSのセキュリティ基盤の中核となるサービスです。
本記事で掲載しているテンプレートでは、AWS CloudTrailの証跡作成とログ保存用のAmazon S3バケットの作成について定義しています。なお、Amazon S3バケットには、1年でオブジェクトを削除するライフサイクルルールを設定しています。
スタック削除時にAmazon S3バケットが空でないことによるDELETE_FAILEDを防止するために、バケットを空にするAWS Lambdaをカスタムリソースとして作成しています。
![]() |
| CloudFormationテンプレートで作成されるアーキテクチャ |
3.4.2 CloudFormationで実施する場合
- 下記のCloudFormationテンプレートを
任意のファイル名.yaml(例:CloudTrail.yaml)として自身の環境に保存 - 3.1.3 使い方の2から8まで実施
- 先ほど保存した
CloudTrail.yamlをアップロードし、「次へ」をクリック - 任意のスタック名 (例:
CloudTrail)を入力 - 3.1.3 使い方の11から12まで実施
- スタックのステータスが
CREATE_COMPLETEとなれば、スタックの作成は成功
# Template description
AWSTemplateFormatVersion: "2010-09-09"
Description: Create S3 bucket for CloudTrail and enable CloudTrail
# Resources
Resources:
# S3 bucket for CloudTrail
CloudTrailBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "cloudtrail-bucket-${AWS::AccountId}-${AWS::Region}"
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: DeleteCloudTrailLogs
Status: Enabled
ExpirationInDays: 365
Prefix: "AWSLogs/"
# S3 bucket policy for CloudTrail
CloudTrailBucketPolicy:
Type: AWS::S3::BucketPolicy
DependsOn: CloudTrailBucket
Properties:
Bucket: !Ref CloudTrailBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: CloudTrailAclCheck
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:GetBucketAcl
Resource: !Sub arn:aws:s3:::${CloudTrailBucket}
- Sid: CloudTrailWrite
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:PutObject
Resource: !Sub arn:aws:s3:::${CloudTrailBucket}/AWSLogs/${AWS::AccountId}/*
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
# CloudTrail
CloudTrail:
Type: AWS::CloudTrail::Trail
DependsOn: CloudTrailBucketPolicy
Properties:
TrailName: ManagementEvent
S3BucketName: !Sub "cloudtrail-bucket-${AWS::AccountId}-${AWS::Region}"
IsLogging: true
IsMultiRegionTrail: false
IncludeGlobalServiceEvents: false
EnableLogFileValidation: true
EventSelectors:
- ReadWriteType: All
IncludeManagementEvents: true
DataResources: []
# Custom resource for Lambda
S3BucketCleaner:
Type: Custom::S3BucketCleaner
Properties:
ServiceToken: !GetAtt S3CleanerFunction.Arn
BucketName: !Ref CloudTrailBucket
# Lambda to delete to clear S3 bucket
S3CleanerFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt S3CleanerRole.Arn
Runtime: python3.11
Timeout: 300
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
s3 = boto3.resource('s3')
bucket_name = event['ResourceProperties']['BucketName']
try:
if event['RequestType'] == 'Delete':
bucket = s3.Bucket(bucket_name)
bucket.object_versions.delete()
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, {"Message": str(e)})
# IAM role for Lambda
S3CleanerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: S3DeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:ListBucket
- s3:ListBucketVersions
- s3:DeleteObject
- s3:DeleteObjectVersion
Resource:
- !Sub "arn:aws:s3:::${CloudTrailBucket}"
- !Sub "arn:aws:s3:::${CloudTrailBucket}/*"
# Outputs
Outputs:
# S3 bucket for CloudTrail
CloudTrailBucketOutput:
Description: S3 bucket for CloudTrail
Value: !Ref CloudTrailBucket
# CloudTrail
CloudTrailOutput:
Description: CloudTrail trail name
Value: !Ref CloudTrail
3.4.3 マネジメントコンソールで実施する場合
手順 (クリックすると展開されます)
Amazon S3バケットの作成
- 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 上部の検索ボックスに
S3と入力し、S3をクリック - 左のメニューから「汎用バケット」をクリック
- 「バケットタイプ」は「汎用」を選択
- 「バケット名前空間」は「グローバル名前空間」を選択した場合、グローバル名前空間内で一意な名称 (例:
cloudtrail-bucket-アカウントID-リージョン名)を入力し、「アカウントのリージョナル名前空間」を選択した場合は任意の名称を入力 - 「バケットを作成」をクリック
ライフサイクルルールの作成
- 「管理」タブを表示し、「ライフサイクルルールを作成する」をクリック
- 「ライフサイクルルール名」に任意のルール名 (例:
DeleteCloudTrailLogs)を入力 - 「プレフィックス」に
AWSLogs/を入力 - 「ライフサイクルルールのアクション」は「オブジェクトの現行バージョンを有効期限切れにする」を選択
- 「ルールの作成」をクリック
バケットポリシーの作成
- 「アクセス許可」タブを表示し、「バケットポリシー」の「編集」をクリック
- 下記のバケットポリシーの「作成したバケット名」と「アカウントID」を自身の環境に合わせて変更し、「ポリシー」に入力
- 「変更の保存」をクリック
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::作成したバケット名"
},
{
"Sid": "CloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::作成したバケット名/AWSLogs/アカウントID/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
CloudTrailの有効化
- 上部の検索ボックスに
CloudTrailと入力し、CloudTrailをクリック - 左のメニューから「証跡」をクリック
- 「証跡の作成」をクリック
- 「証跡名」に任意の証跡名 (例:
default)を入力 - 「既存のS3バケットを使用する」を選択し、「証跡ログバケット名」に先ほど作成したAmazon S3バケット名を選択
- 「ログファイルのSSE-KMS暗号化」を無効化
- 「次へ」をクリック
- 再度「次へ」をクリック
- 詳細を確認し、「証跡の作成」をクリック
3.4.4 確認
AWSマネジメントコンソールの左下にあるAWS CloudShellにて、AWS CloudTrailのログを確認する下記コマンドを実行し、ログファイルが表示されれば、本設定は完了です。
aws s3 ls s3://作成したバケット名 --recursive | sort | tail -n 1
AWS CloudTrailはイベント発生からAmazon S3出力までタイムラグがあります。
3.5 CloudFormation: AWS Configの有効化
3.5.1 目的
AWS Configは、AWSリソースの構成変更を記録するサービスです。AWS CloudTrailがAPI操作ログを記録するのに対し、AWS Configは構成変更を記録します。AWS Configを有効化することによって、AWSリソースの構成を容易に管理することができるようになります。
本記事で掲載しているテンプレートでは、AWS Configの有効化とログ保存用のAmazon S3バケットの作成について定義しています。なお、Amazon S3バケットには、1年でオブジェクトを削除するライフサイクルルールを設定しています。
スタック削除時にAmazon S3バケットが空でないことによるDELETE_FAILEDを防止するために、バケットを空にするAWS Lambdaをカスタムリソースとして作成しています。
![]() |
| CloudFormationテンプレートで作成されるアーキテクチャ |
3.5.2 CloudFormationで実施する場合
- 下記のCloudFormationテンプレートを
任意のファイル名.yaml(例:Config.yaml)として自身の環境に保存 - 3.1.3 使い方の2から8まで実施
- 先ほど保存した
Config.yamlをアップロードし、「次へ」をクリック - 任意のスタック名 (例:
Config)を入力 - 3.1.3 使い方の11から12まで実施
- スタックのステータスが
CREATE_COMPLETEとなれば、スタックの作成は成功
既にAWS Configを有効化している場合、スタックの作成前にAWS Configを無効化する必要があります。
手順と確認 (クリックすると展開されます)
手順
AWSマネジメントコンソールの左下にあるAWS CloudShellにて、AWS Configを無効化する下記コマンドを実行してください。
for r in `aws ec2 describe-regions --query Regions[*].RegionName --output text`
do
for channel_name in `aws configservice describe-delivery-channels --region $r --query DeliveryChannels[*].name --output text`
do
recorder_name=`aws configservice describe-configuration-recorders --region $r --query ConfigurationRecorders[*].name --output text`
aws configservice delete-configuration-recorder --configuration-recorder-name $recorder_name --region $r
aws configservice delete-delivery-channel --delivery-channel-name $channel_name --region $r
done
done
引用: https://dev.classmethod.jp/articles/disable-config-all-region-cli/
確認
下記コマンドを実行し、DeliveryChannelsが空であることが表示されれば、AWS Configの無効化は完了です。
aws configservice describe-delivery-channels
# Template description
AWSTemplateFormatVersion: "2010-09-09"
Description: Create S3 bucket for Config and enable Config
# Resources
Resources:
# S3 bucket for Config
ConfigBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "config-bucket-${AWS::AccountId}-${AWS::Region}"
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: DeleteConfigLogs
Status: Enabled
ExpirationInDays: 365
Prefix: "AWSLogs/"
# S3 bucket policy for Config
ConfigBucketPolicy:
Type: AWS::S3::BucketPolicy
DependsOn: ConfigBucket
Properties:
Bucket: !Ref ConfigBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ConfigBucketAclCheck
Effect: Allow
Principal:
Service: config.amazonaws.com
Action: s3:GetBucketAcl
Resource: !Sub arn:aws:s3:::${ConfigBucket}
- Sid: ConfigBucketWrite
Effect: Allow
Principal:
Service: config.amazonaws.com
Action: s3:PutObject
Resource: !Sub arn:aws:s3:::${ConfigBucket}/AWSLogs/${AWS::AccountId}/Config/*
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
# AWSServiceRoleForConfig
ConfigServiceLinkedRole:
Type: AWS::IAM::ServiceLinkedRole
DependsOn: ConfigBucketPolicy
Properties:
AWSServiceName: config.amazonaws.com
# Config Recorder
ConfigRecorder:
Type: AWS::Config::ConfigurationRecorder
DependsOn: ConfigServiceLinkedRole
Properties:
Name: default
RoleARN: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig"
RecordingGroup:
AllSupported: true
IncludeGlobalResourceTypes: false
# Config Delivery Channel
ConfigDeliveryChannel:
Type: AWS::Config::DeliveryChannel
DependsOn: ConfigServiceLinkedRole
Properties:
S3BucketName: !Ref ConfigBucket
# Custom resource for Lambda
S3BucketCleaner:
Type: Custom::S3BucketCleaner
Properties:
ServiceToken: !GetAtt S3CleanerFunction.Arn
BucketName: !Ref ConfigBucket
# Lambda to delete to clear S3 bucket
S3CleanerFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt S3CleanerRole.Arn
Runtime: python3.11
Timeout: 300
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
s3 = boto3.resource('s3')
bucket_name = event['ResourceProperties']['BucketName']
try:
if event['RequestType'] == 'Delete':
bucket = s3.Bucket(bucket_name)
bucket.object_versions.delete()
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, {"Message": str(e)})
# IAM role for Lambda
S3CleanerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: S3DeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:ListBucket
- s3:ListBucketVersions
- s3:DeleteObject
- s3:DeleteObjectVersion
Resource:
- !Sub "arn:aws:s3:::${ConfigBucket}"
- !Sub "arn:aws:s3:::${ConfigBucket}/*"
# Outputs
Outputs:
# Config Recorder
ConfigRecorderOutput:
Description: Config Recorder name
Value: !Ref ConfigRecorder
# S3 bucket for Config
ConfigBucketOutput:
Description: S3 bucket for Config
Value: !Ref ConfigBucket
3.5.3 マネジメントコンソールで実施する場合
手順 (クリックすると展開されます)
Amazon S3バケットの作成
- 管理者ユーザーでAWSマネジメントコンソールにサインイン
- 上部の検索ボックスに
S3と入力し、S3をクリック - 左のメニューから「汎用バケット」をクリック
- 「バケットタイプ」は「汎用」を選択
- 「バケット名前空間」は「グローバル名前空間」を選択した場合、グローバル名前空間内で一意な名称 (例:
config-bucket-アカウントID-リージョン名)を入力し、「アカウントのリージョナル名前空間」を選択した場合は任意の名称を入力 - 「バケットを作成」をクリック
ライフサイクルルールの作成
- 「管理」タブを表示し、「ライフサイクルルールを作成する」をクリック
- 「ライフサイクルルール名」に任意のルール名 (例:
DeleteConfigLogs)を入力 - 「プレフィックス」に
AWSLogs/を入力 - 「ライフサイクルルールのアクション」は「オブジェクトの現行バージョンを有効期限切れにする」を選択
- 「ルールの作成」をクリック
バケットポリシーの作成
- 「アクセス許可」タブを表示し、「バケットポリシー」の「編集」をクリック
- 下記のバケットポリシーの「作成したバケット名」と「アカウントID」を自身の環境に合わせて変更し、「ポリシー」に入力
- 「変更の保存」をクリック
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ConfigBucketAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "config.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::作成したバケット名"
},
{
"Sid": "ConfigBucketWrite",
"Effect": "Allow",
"Principal": {
"Service": "config.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::作成したバケット名/AWSLogs/アカウントID/Config/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
AWS Configの有効化
- 上部の検索ボックスに
ConfigもしくはAWS Configと入力し、AWS Configをクリック - 「AWS Configのセットアップ」の「今すぐ始める」をクリック
- 「配信チャネル」の「Amazon S3バケット」にて、「アカウントからバケットを選択」を選択し、先ほど作成したAmazon S3バケットを選択
- 「次へ」をクリック
- 再度「次へ」をクリック
- 詳細を確認し、「確認」をクリック
3.5.4 確認
AWSマネジメントコンソールの左下にあるAWS CloudShellにて、AWS Configのログを確認する下記コマンドを実行し、ログファイルが表示されれば、本設定は完了です。
aws s3 ls s3://作成したバケット名 --recursive | sort | tail -n 1
AWS Configはリソースに変更がない限り、ログが出ない設定もあります。
4. おわりに
これにてAWSのベストプラクティスに則った安全な環境の構築は完了です。最後まで読んでいただき、ありがとうございました。
3.1.1 メリットで述べたように、AWS CloudFormationを利用することで、設定漏れや手順の前後によるミスなく、AWSアカウントのセットアップを迅速に実施することができます。
本記事では、マネジメントコンソールで実施する場合についても記載していますが、CloudFormationで実施する場合と比較して、必要な手順が多く感じたと思います。必要な手順が多くなると、それに伴って操作ミスの可能性も増加するため、AWS CloudFormationの恩恵をより多く享受することができます。
一方、3.1.2 デメリットで述べたように、AWS CloudFormationには初期の学習コストと独自記法への慣れという壁があります。しかし、一度テンプレート化してしまえば、そのテンプレートは必ず大きな資産になると私は考えています。今はまだコードの内容が完全に理解できなくても、まずは「動かしてみる」ことから始め、徐々に中身の理解を深めていきましょう!
本記事が、安全で快適なAWSライフの一助となるだけでなく、AWS CloudFormationでのインフラ構築の第一歩を後押しするきっかけになれば幸いです。
なお、本記事に掲載しているCloudFormationテンプレートについては全て検証し、スタックの作成まで確認していますが、スタックの作成に失敗した場合は別途お知らせください。
AWS は、米国その他の諸国における Amazon.com, Inc. またはその関連会社の商標です。
その他、記載されている会社名および商品・製品・サービス名は、各社の商標または登録商標です。




