CloudFormation StackSetsでSSMドキュメントのパブリック共有をブロックしてSecurity Hub CSPMのSSM.7に全アカウントで対応する

CloudFormation StackSetsでSSMドキュメントのパブリック共有をブロックしてSecurity Hub CSPMのSSM.7に全アカウントで対応する

Security Hub CSPM コントロール SSM.7 を全アカウント・全リージョンで一括設定したいですよね。本記事では、CloudFormation StackSets とカスタムリソースを組み合わせて、SSMドキュメントのパブリック共有ブロック設定を自動展開する方法を紹介します。
2026.08.14

はじめに

皆様こんにちは、あかいけです。
Security Hub CSPMのコントロールSSM.7を全アカウントまとめて設定したいと思ったことはありますか?私はあります。

というわけで今回は、CloudFormation StackSetsでこの設定をAWSアカウント全体へ自動展開し、SSM.7に対応する方法をまとめます。

SSM.7 について

SSM.7が求めているのは、SSMドキュメントの「パブリック共有のブロック設定」を有効にすることです。
またこの設定はリージョンごとに個別に設定する必要があります。

https://docs.aws.amazon.com/securityhub/latest/userguide/ssm-controls.html#ssm-7
https://dev.classmethod.jp/articles/securityhub-fsbp-remediation-ssm-7/
https://dev.classmethod.jp/articles/aws-ssm-document-public-sharing-block-multi-account-regions/

アカウント単位かつリージョン単位という性質上、対象が増えるほど手作業のコストが膨らみます。
また新規アカウントを作る度に設定する必要があるのも面倒です。

そこでCloudFormation StackSetsで一括展開し、新規アカウントに対してもStackSetsの自動デプロイを使うことで都度の手動設定から解放されたいと私は思いました。

AWS::SSM::ServiceSetting で設定するつもりでした

SSMのアカウントレベル設定は、CloudFormationのAWS::SSM::ServiceSettingというリソースで設定できます。

https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ssm-servicesetting.html

内部的にはUpdateServiceSetting APIを実行しているようです。
つまりUpdateServiceSetting APIで設定可能な以下の設定は、このリソースで設定できるというわけです。

  • /ssm/appmanager/appmanager-enabled
  • /ssm/automation/customer-script-log-destination
  • /ssm/automation/customer-script-log-group-name
  • /ssm/automation/enable-adaptive-concurrency
  • /ssm/documents/console/public-sharing-permission
  • /ssm/managed-instance/activation-tier
  • /ssm/managed-instance/default-ec2-instance-management-role
  • /ssm/opsinsights/opscenter
  • /ssm/parameter-store/default-parameter-tier
  • /ssm/parameter-store/high-throughput-enabled

https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_UpdateServiceSetting.html

そしてこの中の/ssm/documents/console/public-sharing-permissionがSSMドキュメントのパブリック共有のブロック設定にあたるので、これをDisableに設定すればOKです。

ssm7-servicesetting.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Block public sharing setting for SSM documents. Deployed via StackSets to all member accounts (per Region).

Resources:
  BlockPublicDocumentSharing:
    Type: AWS::SSM::ServiceSetting
    Properties:
      SettingId: /ssm/documents/console/public-sharing-permission
      SettingValue: Disable

これでいけるはず!と思ったのですが、謎のエラーが発生しました…。

Template format error: Unrecognized resource types: [AWS::SSM::ServiceSetting]

スクリーンショット 2026-08-14 17.04.02

東京リージョンでは AWS::SSM::ServiceSetting が使えなかった

原因を調べたところ、東京リージョン(ap-northeast-1)ではこのAWS::SSM::ServiceSettingがCloudFormationレジストリに登録されていませんでした。
describe-typeで確認すると、東京リージョンではTypeNotFoundExceptionが返り、バージニアリージョン(us-east-1)では正常に取得できます。

% aws cloudformation describe-type \
  --type RESOURCE \
  --type-name AWS::SSM::ServiceSetting \
  --region ap-northeast-1

aws: [ERROR]: An error occurred (TypeNotFoundException) when calling the DescribeType operation: The type 'AWS::SSM::ServiceSetting' cannot be found.

Additional error details:
Type: Sender
% aws cloudformation describe-type --type RESOURCE --type-name AWS::SSM::ServiceSetting --region us-east-1     
{
    "Arn": "arn:aws:cloudformation:us-east-1::type/resource/AWS-SSM-ServiceSetting",
    "Type": "RESOURCE",
    "TypeName": "AWS::SSM::ServiceSetting",
    "IsDefaultVersion": true,
    "Description": "Resource Type definition for AWS::SSM::ServiceSetting. ServiceSetting is an account-level setting for an AWS service that defines how a user interacts with or uses a service or feature.",
    "ProvisioningType": "FULLY_MUTABLE",
    "DeprecatedStatus": "LIVE",
    "Visibility": "PUBLIC",
    "TimeCreated": "2026-06-18T17:38:19.655000+00:00"
}

バージニアリージョン側のTimeCreated2026-06-18とかなり最近なので、まだ全リージョンには行き渡っていないのかもしれませんね…。
そのうち東京リージョンでも使えるようになることを期待したいところです。

念の為、管理アカウントのバージニアリージョンからStackSetsをデプロイしてみましたが、スタックセット自体の作成は成功するものの、やはり東京リージョンへのスタックインスタンス作成は失敗しました。

スクリーンショット 2026-08-14 17.13.02 1

カスタムリソースで全リージョンに対応する

AWS::SSM::ServiceSettingが使えないリージョンがある以上、全リージョンをまとめて対応するにはカスタムリソースでUpdateServiceSetting APIを直接叩くしかありません。
というわけで、カスタムリソース版が以下です。

ssm7-custom-resource.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Block public sharing setting for SSM documents via custom resource (for Regions without AWS::SSM::ServiceSetting, e.g. ap-northeast-1). Deployed via StackSets to all member accounts (per Region).

Resources:
  BlockPublicSharingFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: manage-ssm-public-sharing-setting
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - ssm:UpdateServiceSetting
                  - ssm:GetServiceSetting
                Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:servicesetting/ssm/documents/console/public-sharing-permission'
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !Sub 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:*'

  BlockPublicSharingFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: Sets SSM documents public-sharing-permission to Disable (SSM.7).
      Runtime: python3.14
      Handler: index.handler
      Timeout: 30
      Role: !GetAtt BlockPublicSharingFunctionRole.Arn
      Code:
        ZipFile: |
          import boto3
          import cfnresponse

          SETTING_ID = "/ssm/documents/console/public-sharing-permission"

          def handler(event, context):
              try:
                  ssm = boto3.client("ssm")
                  if event["RequestType"] in ("Create", "Update"):
                      ssm.update_service_setting(
                          SettingId=SETTING_ID, SettingValue="Disable"
                      )
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
              except Exception as exc:
                  cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": str(exc)})

  BlockPublicSharing:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BlockPublicSharingFunction.Arn

やっていることはシンプルで、update_service_settingDisableを設定しているだけです。
なお、Delete時には特に何もしていません。
スタックを削除したときに設定をEnable(パブリック共有を許可)へ戻してしまうと、意図せずSSM.7が再び失敗する状態に戻ってしまうため、あえてブロック設定を残す挙動にしています。

動作確認

このテンプレートを東京リージョンとバージニアリージョンにデプロイしてみます。

スクリーンショット 2026-08-14 17.31.10

スタックの作成は問題なく完了しました。

スクリーンショット 2026-08-14 17.36.42

デプロイ後にアカウント側の設定を確認します。
今回設定した東京リージョンとバージニアリージョンはDisable(パブリック共有をブロック)になっており、未設定の大阪リージョン(ap-northeast-3)はデフォルトのEnableのままなのでOKそうです。

$ for region in ap-northeast-1 us-east-1 ap-northeast-3; do
>   value=$(aws ssm get-service-setting \
>     --setting-id /ssm/documents/console/public-sharing-permission \
>     --region "$region" \
>     --query "ServiceSetting.SettingValue" \
>     --output text)
>   value=${value%$'\r'}
>   printf '%-16s: %s\n' "$region" "$value"
> done
ap-northeast-1  : Disable
us-east-1       : Disable
ap-northeast-3  : Enable

あとはStackSetsのデプロイ対象リージョンに対応したいリージョンが設定され、自動デプロイを有効になっていれば、新規アカウントが組織に追加されたタイミングで自動的にブロック設定が入るようになります。

さいごに

以上、CloudFormation StackSetsでSSMドキュメントのパブリック共有のブロック設定を有効にし、AWSアカウント全体でSecurity Hub CSPMコントロールSSM.7に対応する方法でした。
早く全リージョンで AWS::SSM::ServiceSetting が使えるようになって欲しいですね…。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事