複数アカウント下での「GuardDuty S3 MalwareProtection」の
導入を阻む課題とその解決策
投稿日: 2025/01/14
はじめに
弊社ブログでも取り上げた「GuardDuty S3 MalwareProtection」は、GuardDutyの管理コンソールからS3バケットごとに有効化を設定する必要があります。ただし、マルチアカウント管理環境では、GuardDutyの管理コンソールへの操作が制限される予防的統制が敷かれている場合があります。このような環境では、機能の利用推進が後述する課題によって阻まれることがあります。
本記事では、その課題を解決するためのソリューションをご紹介します。
なお、「GuardDuty S3 MalwareProtection」の詳細については、弊社ブログ「Amazon GuardDutyでAmazon S3バケットをマルウェアから保護する」をご参照ください。
https://www.ctc-g.co.jp/solutions/cloud/column/article/112.html
予防的統制下におけるGuardDuty利用の課題
GuardDutyの管理コンソールに対する操作をAWS OrganizationsのSCP(Service Control Policy)やBoundary Policyで制限している環境では、「GuardDuty S3 MalwareProtection」を利用するために、次のような施策が検討されることがあります。
- 予防的統制で設定したPolicyの見直し
- 申請ベースで、管理者が代行して構築
これらの施策は、GuardDutyの利用を推進する目的自体は達成できます。しかし、統制の緩和や構築代行に伴い、セキュリティポリシーの一貫性低下や管理者の負荷増加といった新たな課題を生み出します。
これらの課題を解決する方法として、「セルフサービス化」と「一時的な権限昇格」の両方を実現できる「AWS Service Catalog」の活用があります。
AWS Service Catalogは、AWS Organizationsが利用できる環境と利用できない環境で展開方法が異なります。本記事では、AWS Organizationsを利用しないマルチアカウント環境を想定し、AWS Service Catalogを活用した具体的な方法をご紹介します。
AWS Service Catalogがもたらす管理者と利用者の双方が得られるメリットとは
「GuardDuty S3 MalwareProtection」を例にAWS Service Catalogを活用することで、管理者と利用者の双方に以下のようなメリットを提供できます。
<管理者視点のメリット>
- マルチアカウント環境(ランディングゾーン)における「GuardDuty S3 MalwareProtection」の定義を一元管理できる
- メンバーアカウントへ「GuardDuty S3 MalwareProtection」を簡単に展開できる
<利用者視点のメリット>
- 利用者自身のセルフサービスで「GuardDuty S3 MalwareProtection」を有効化できる
- ウィザード形式で、適用対象のS3バケットを入力するだけで、「GuardDuty S3 MalwareProtection」を有効化できる
AWS Service Catalogを活用することで、運用効率を大幅に向上させるとともに、管理者の負荷軽減と利用者の利便性向上を両立できます。
AWS Organizationsを利用しない環境でのAWS Service Catalog構成
複雑な準備は必要ありません。AWS Organizationsを利用しない環境で、AWS Service Catalogを構築する手順を見ていきましょう。必要なステップは次の3つです。
- 1. ランディングゾーンのAWSアカウントでポートフォリオを登録
AWS Service Catalogにポートフォリオを作成し、共有するメンバーアカウントを指定します。 - 2. メンバーアカウントにIAM Roleを作成
AWS Service Catalogで登録した起動制約(Launch Constraint)に設定されているIAM Role名と同じ名前のロールをメンバーアカウントで作成します。 - 3. ポートフォリオをメンバーアカウントにインポート
メンバーアカウントのAWS Service Catalogに、ランディングゾーンで作成したポートフォリオをインポートします。
構成イメージ
これらの3ステップを実施することで、AWS Organizationsを利用しない環境でも、AWS Service Catalogを用いた効率的なセルフサービスの提供が可能になります。

AWS Organizationsを使わない環境でのAWS Service Catalogの設定手順
AWS Service Catalogを構築する際の手順を説明します。本ガイドでは、AWS Organizationsを利用せずに「GuardDuty S3 MalwareProtection」をAWS Service Catalogで構成する方法を詳しく解説します。以下のステップに従って進めてください。
1. ランディングゾーンアカウントでの設定
① IAMポリシーとロールの作成
ランディングゾーンのAWSアカウントにて、AWS Service Catalogで「GuardDuty S3 MalwareProtection」の登録に必要なIAMポリシーとロールを作成します。このロールはメンバーアカウントで直接利用されることはありませんが、AWS Service Catalogの製品作成時に必要です。
サンプルPolicyとRole
<Policy名:pl-cmn-s3mwprotection-tokyo>
ポリシーの内容は以下の通りです(詳細はAWSドキュメントを参照してください)。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"guardduty:*MalwareProtectionPlan"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:CreatePolicy",
"iam:PutRolePolicy",
"iam:AttachRolePolicy",
"iam:GetRole",
"iam:GetPolicy",
"iam:List*",
"iam:DetachRolePolicy",
"iam:DeleteRolePolicy",
"iam:DeleteRole",
"iam:DeletePolicy",
"iam:PassRole",
"iam:TagRole",
"iam:TagPolicy"
],
"Resource": [
"arn:aws:iam::{AWSアカウント番号}:policy/pl-s3mwprotection-*", この定義は任意
"arn:aws:iam:: {AWSアカウント番号}:role/rl-s3mwprotection-*" この定義は任意
]
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:PutBucketNotification",
"s3:PutBucketPolicy",
"s3:*tagging",
"s3:TagResource",
"s3:UntagResource"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/servicecatalog:provisioning": "true" この定義はおまじない。詳細はAWSドキュメントのを参照ください。
https://docs.aws.amazon.com/servicecatalog/latest/adminguide/constraints-launch.html
}
}
},
{
"Effect": "Allow",
"Action": [
"guardduty:TagResource"
],
"Resource": "*"
}
]
}
<Role名:rl-cmn-s3mwprotection-tokyo>
信頼関係と結び付けるPolicyを以下のように設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "servicecatalog.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
結び付けるPolicy
- AWS Service CatalogEndUserFullAccess(AWS管理ポリシー)
- pl-cmn-s3mwprotection-tokyo(カスタマー管理ポリシー)
② ポートフォリオの作成
AWS Service Catalogで新しいポートフォリオを作成します。
③ 製品の登録
作成したポートフォリオを選択し、「GuardDuty S3 MalwareProtection」を構築するためのCloudFormationテンプレートを製品として登録します。
<「GuardDuty S3 MalwareProtection」を構築するCloudFormation定義>
template-sc-guardduty-s3mwprotection.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Enable S3 Malware Protection with IAM Role for an existing GuardDuty detector
Parameters:
S3BucketName:
Type: String
Description: 'The name of the S3 bucket to protect'
Resources:
GuardDutyIAMPolicy:
Type: AWS::IAM::ManagedPolicy
DeletionPolicy: Delete
Properties:
ManagedPolicyName: !Sub pl-s3mwprotection-${AWS::StackName}
Description: !Sub pl-s3mwprotection-${AWS::StackName}
PolicyDocument:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowManagedRuleToSendS3EventsToGuardDuty",
"Effect": "Allow",
"Action": [
"events:PutRule",
"events:DeleteRule",
"events:PutTargets",
"events:RemoveTargets"
],
"Resource": [
"arn:aws:events:*:*:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
],
"Condition": {
"StringLike": {
"events:ManagedBy": "malware-protection-plan.guardduty.amazonaws.com"
}
}
},
{
"Sid": "AllowGuardDutyToMonitorEventBridgeManagedRule",
"Effect": "Allow",
"Action": [
"events:DescribeRule",
"events:ListTargetsByRule"
],
"Resource": [
"arn:aws:events:*:*:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
]
},
{
"Sid": "AllowPostScanTag",
"Effect": "Allow",
"Action": [
"s3:PutObjectTagging",
"s3:GetObjectTagging",
"s3:PutObjectVersionTagging",
"s3:GetObjectVersionTagging"
],
"Resource": [
!Sub "arn:aws:s3:::${S3BucketName}/*"
]
},
{
"Sid": "AllowEnableS3EventBridgeEvents",
"Effect": "Allow",
"Action": [
"s3:PutBucketNotification",
"s3:GetBucketNotification"
],
"Resource": [
!Sub "arn:aws:s3:::${S3BucketName}"
]
},
{
"Sid": "AllowPutValidationObject",
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": [
!Sub "arn:aws:s3:::${S3BucketName}/malware-protection-resource-validation-object"
]
},
{
"Sid": "AllowCheckBucketOwnership",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
!Sub "arn:aws:s3:::${S3BucketName}"
]
},
{
"Sid": "AllowMalwareScan",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": [
!Sub "arn:aws:s3:::${S3BucketName}/*"
]
},
{
"Sid": "AllowDecryptForMalwareScan",
"Effect": "Allow",
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt"
],
"Resource": "arn:aws:kms:*:*:key/*",
"Condition": {
"StringLike": {
"kms:ViaService": "s3.*.amazonaws.com"
}
}
}
]
}
GuardDutyIAMRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
RoleName: !Sub rl-s3mwprotection-${AWS::StackName}
Description: !Sub rl-s3mwprotection-${AWS::StackName}
MaxSessionDuration: 3600
Tags:
- Key: "Owner"
Value: "CCoE"
AssumeRolePolicyDocument:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "malware-protection-plan.guardduty.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Path: /
ManagedPolicyArns:
- !Ref GuardDutyIAMPolicy
MalwareProtection:
Type: AWS::GuardDuty::MalwareProtectionPlan
DeletionPolicy: Delete
DependsOn: GuardDutyIAMRole
Properties:
Actions:
Tagging:
Status: ENABLED
ProtectedResource:
S3Bucket:
BucketName: !Ref S3BucketName
Role: !GetAtt GuardDutyIAMRole.Arn
④ IAMロールの指定(起動制約の設定)
製品に対して起動制約として利用するIAMロールを指定します。
この際、「ロール名を入力」を選択し、指定したロール名が存在しない場合にエラーが発生しないよう、事前にロールを作成してください。
ポイントは、「ロール名を入力」を選択することです。「IAMロール名を選択」すると、ランディングゾーンで指定したIAM RoleのARNが指定されるため、メンバーアカウントの実行でエラーになります。
なお、AWSドキュメントでは以下に記載されており誤解を生む表現となっていますが、共有する際は利用できないので注意が必要です。
<AWSドキュメント原文>
https://docs.aws.amazon.com/ja_jp/servicecatalog/latest/adminguide/constraints-launch.html
<誤解を生むこと>
- 「ロール名を入力」を選択すれば、アカウントに依存しないようにできるが、メンバーアカウントにIAMロールを作成しない手段「ARNを入力」でもアカウント依存を前提に利用ができるとも読み取れる。
- “共有アカウントごとに作成するリソースを減らすことができます。” ⇒減らすどうこうではなく、ポートフォリオの共有時は、起動制約に選択するものは「ローカルロール名」の指定一択になる。
⑤ 共有の設定
メンバーアカウントへの共有設定を行います。AWS Organizationsを利用しない環境なので、共有先のメンバーアカウントを個別に追加してください。
共有設定後の画面

⑥ ポートフォリオIDの控え
メンバーアカウント側の作業に進む前に、ポートフォリオIDを控えておきます(例: port-rp77had2msz2u)。以降は、メンバーアカウント側の作業になります。
2. メンバーアカウントに起動制約と同じIAM Role名を作成する
① IAM Roleの作成
メンバーアカウントに、ランディングゾーンで作成したIAMロールと同じ名前のロールを作成します。IAMポリシー、信頼関係の定義も含めて設定してください(例: IAMロール名はrl-cmn-s3mwprotection-tokyo)。
3. メンバーアカウントでポートフォリオをインポートする
① ポートフォリオのインポート
メンバーアカウントでは、共有されたポートフォリオIDを指定してインポートします。この作業は「共有されたポートフォリオを受け入れる」操作に該当します。AWSドキュメントには「共有とインポート」と記載されていますが、受け入れ側のアクションが必要である点に注意してください。
<Tips>
一般的には、ランディングゾーンで共有相手を指定した際、共有を受けた側に承認を求めるような表示がされるAWSサービスがあります。そのため、同じような動作を期待するかもしれません。しかし、AWS Service Catalogではそのような表示はありません。共有を受け入れる側は、能動的に共有されたポートフォリオIDを指定し、入力する必要があります。
② インポート完了後の確認
ランディングゾーンで作成した製品と制約が正しく登録されていることを確認します。
ランディングゾーンにて作成したポートフォリオから製品と制約が継承されます。制約の説明を見ることで、利用されるIAM Role名が確認できます。
③ アクセス権の付与
インポートしたポートフォリオを利用できるユーザーまたはロールを指定します。AWS Organizationsを利用している場合はランディングゾーンで一括設定できますが、今回のケースではメンバーアカウント側で登録する必要があります。
余談ですが、AWS Organizationsを利用していれば、ランディングゾーンにて「プリンシパル名」を指定した登録が利用できます。

今回は、AdministratorAccessを保持する「rl-user-admin」にアクセス権を付与します。

<リソース名を記載した構成イメージ>

AWS Service Catalogでのプロビジョニング
① アクセス権のあるロールへスイッチ
AWS Service Catalogで製品をプロビジョニングするために、事前にアクセス権を付与したロール「rl-user-admin」へスイッチします。
※ アクセス権がないロールを使用した場合、プロビジョニング可能な製品が表示されません。

② プロビジョニングの実行
AWS Service Catalogから「GuardDuty S3 MalwareProtection」を起動します。この際、以下の2つを入力します。入力後、CloudFormationが実行され、リソースが作成されます。
- プロビジョニングされた製品の名前: AWS Service Catalogで管理する一意の名前
- S3バケット名: 構築対象のバケット名
プロビジョニング結果の確認
プロビジョニングに成功すると、「プロビジョニングされた製品」の画面で状態を確認できます。また、作成されたリソース名やCloudFormationのスタック名も確認できます。

作成されたリソース名

CloudFormationのスタック名

AWS Service Catalog利用時の工夫ポイント
AWS Service Catalogの利点は、利用者が主体的にセルフサービス型で構築できる点です。そのため、複数回の構築や異なる利用者による利用を想定した設計が重要です。
今回使用したCloudFormationテンプレートでは以下を考慮しています。
- スタック単位の疎結合
IAMポリシーやロール名にスタック名を含めることで、テンプレートがバージニア北部リージョンに作成されるIAMリソースをスタックごとに独立して扱えるように設計しています。この工夫により、利用者が同一テンプレートを複数回実行してもエラーが発生しません。
メンバーアカウントへのポートフォリオを共有する運用課題と解決策
AWS Organizationsを利用しない環境では、メンバーアカウントへの展開が最大の課題です。
Organizationsを利用すれば、メンバーアカウントへの共有が手間なく実現できます。しかし、AWS Service Catalogをメンバーアカウントで利用するためには、ポートフォリオのインポートとアクセス許可の設定が必要となり、手作業が煩雑になりがちです。
この課題を効率的に解決するには、AWS CloudFormationのStackSetsを活用する方法があります。
解決策:AWS CloudFormationのStackSetsの活用
ポイント1:インポートの自動化
CloudFormationテンプレートで以下の定義を利用することで、メンバーアカウント側でポートフォリオのインポートを自動化できます。
- Type: AWS::ServiceCatalog::AcceptedPortfolioShare
ポイント2:アクセス許可の設定
同様に、以下の定義を使用することで、アクセス許可を効率的に設定できます。
- Type: AWS::ServiceCatalog::PortfolioPrincipalAssociation
この仕組みを活用することで、手動での操作を最小限に抑え、スムーズな展開が可能になります。
メンバーアカウントへのAWS Service Catalog展開を自動化する方法
メンバーアカウントにログインせずに、AWS Service Catalogを展開する方法を解説します。この手法では、AWS CloudFormationのStackSetsを利用します。今回はStackSetsの具体的な操作には触れませんが、CloudFormationテンプレートに含めるべき重要な要素を以下にまとめました。
- ① IAM Roleの作成
AWS Service Catalogのポートフォリオで定義した名前に基づき、IAM Roleを作成します。
このRoleは、ポートフォリオの利用に必要なアクセス権を持ちます。 - ② ポートフォリオのインポート
AWS Service Catalogで共有されたポートフォリオをメンバーアカウントにインポートします。
具体的には、以下のリソースタイプを使用します。
Type: AWS::ServiceCatalog::AcceptedPortfolioShare - ③ アクセス権の付与
インポートしたポートフォリオに対して、必要なアクセス権を設定します。
以下のリソースタイプを使用します。
Type: AWS::ServiceCatalog::PortfolioPrincipalAssociation
これらの要素をテンプレートに含めることで、メンバーアカウントにログインすることなくAWS Service Catalogの展開が可能になります。
定義サンプル
AWSTemplateFormatVersion: '2010-09-09'
Conditions:
TargetRegionTokyo:
!Equals [!Ref AWS::Region, ap-northeast-1]
TargetRegionVA:
!Equals [!Ref AWS::Region, us-east-1]
Mappings:
RegionMap:
ap-northeast-1:
RegionLabels: tokyo
Parameters:
AccessToThePortfolioGrantingAccessToUsers:
Description: Granting Access to Users Role Name
Type: String
Default: rl-user-admin
PortfolioIdTokyo:
Description: Portfolio Id
Type: String
CommonName:
Description: Common strings used as part of Roles and Policies
Type: String
Default: cmn-s3mwprotection
Resources:
#
# Policy used for executing ServiceCatalog (Launch Constraints)
#
SCGuardDutyIAMPolicyTokyo:
Type: AWS::IAM::ManagedPolicy
Condition: TargetRegionTokyo
DeletionPolicy: Delete
Properties:
ManagedPolicyName: !Join
- ""
- - 'pl-'
- !Ref CommonName
- '-'
- !FindInMap [ RegionMap, !Ref "AWS::Region", RegionLabels ]
Description: Policy used for executing ServiceCatalog
PolicyDocument:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"guardduty:*MalwareProtectionPlan"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:CreatePolicy",
"iam:PutRolePolicy",
"iam:AttachRolePolicy",
"iam:GetRole",
"iam:GetPolicy",
"iam:List*",
"iam:DetachRolePolicy",
"iam:DeleteRolePolicy",
"iam:DeleteRole",
"iam:DeletePolicy",
"iam:PassRole",
"iam:TagRole",
"iam:TagPolicy"
],
"Resource": [
!Sub "arn:aws:iam::${AWS::AccountId}:policy/pl-s3mwprotection-*",
!Sub "arn:aws:iam::${AWS::AccountId}:role/rl-s3mwprotection-*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:PutBucketNotification",
"s3:PutBucketPolicy",
"s3:*tagging",
"s3:TagResource",
"s3:UntagResource"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/servicecatalog:provisioning": "true"
}
}
}
]
}
#
# Role used for executing ServiceCatalog (Launch Constraints)
#
SCGuardDutyIAMRoleTokyo:
Type: AWS::IAM::Role
Condition: TargetRegionTokyo
DeletionPolicy: Delete
Properties:
RoleName: !Join
- ""
- - 'rl-'
- !Ref CommonName
- '-'
- !FindInMap [ RegionMap, !Ref "AWS::Region", RegionLabels ]
Description: Role used for executing ServiceCatalog
MaxSessionDuration: 3600
Tags:
- Key: "Owner"
Value: "CCoE"
AssumeRolePolicyDocument:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "servicecatalog.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Path: /
ManagedPolicyArns:
- !Ref SCGuardDutyIAMPolicyTokyo
- arn:aws:iam::aws:policy/AWS Service CatalogEndUserFullAccess
#
# Sharing and Importing Portfolios
#
SCAcceptedPortfolioShareTokyo:
Type: AWS Service Catalog::AcceptedPortfolioShare
Condition: TargetRegionTokyo
Properties:
PortfolioId: !Ref PortfolioIdTokyo
#
# Granting Access to Users
#
SCPortfolioPrincipalAssociationTokyo:
Type: AWS Service Catalog::PortfolioPrincipalAssociation
DependsOn: SCAcceptedPortfolioShareTokyo
Condition: TargetRegionTokyo
Properties:
PortfolioId: !Ref PortfolioIdTokyo
PrincipalARN: !Join
- ""
- - !Sub arn:aws:iam::${AWS::AccountId}:role/
- !Ref AccessToThePortfolioGrantingAccessToUsers
PrincipalType: IAM
利用者によるローカルポートフォリオ作成のリスク
管理者が作成した起動制約には、セルフサービスによるリソース作成を目的としていても、利用者に付与したくない権限を含む場合があります。利用者に最小権限を適切に付与している場合、このようなセキュリティ課題は回避できます。しかし、開発段階では権限を絞り込めず、一時的にAdministratorAccessを付与するケースもあるかと思います。
この状況で、利用者がポートフォリオをローカルで作成した場合、管理者が設定した起動制約ロールを利用できてしまう点がセキュリティ上の課題となります。
解決策としては、利用者の権限に対して、管理者が作成した起動制約ロールに対するPassRole操作を拒否するポリシーを追加する方法があります。このポリシーを適用することで、不要な権限の濫用を防ぎ、セキュリティリスクを軽減できます。
セキュリティリスクのイメージ

<管理者が作成した起動制約ロールに対するPassRole操作を拒否するポリシー例>
例に挙げたポリシーを利用者のIAMに付与する
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRoleRestictions",
"Effect": "Deny",
"Action": [
"iam:PassRole"
],
"Resource": [
"arn:aws:iam::*:role/rl-cmn-s3mwprotection-*"
],
"Condition": {
"StringEquals": {
"iam:PassedToService": "servicecatalog.amazonaws.com"
}
}
}
]
}
まとめ
AWS Organizationsが利用できない環境では、AWS Service Catalogをメンバーアカウントに展開する際、メンバーアカウント側で手動の操作が必要でした。しかし、AWS CloudFormationを活用することで、これらの操作を代行し、自動化することが可能です。
また、AWS Service Catalogの利用においてもセキュリティリスクが発生することを紹介しました。
AWS Service Catalogは、AWS Organizationsを利用しない環境でも十分に活用できます。また、これをきっかけに、依頼ベースの運用を見直し、利用者自身が主体的に操作できるセルフサービス型の運用へ切り替えることを検討してはいかがでしょうか。
今回共有したノウハウが、皆さまの課題解決や運用効率化の一助となれば幸いです。
CTCは、AWSのビジネス利活用に向けて、お客様のステージに合わせた幅広い構築・運用支援サービスを提供しています。
経験豊富なエンジニアが、ワンストップかつ柔軟にご支援します。
ぜひ、お気軽にお問い合わせください。