Block public sharing of SSM documents using CloudFormation StackSets to address Security Hub CSPM SSM.7 across all accounts

Block public sharing of SSM documents using CloudFormation StackSets to address Security Hub CSPM SSM.7 across all accounts

You want to configure Security Hub CSPM control SSM.7 all at once across all accounts and all regions. This article introduces how to automatically deploy the SSM document public sharing block setting by combining CloudFormation StackSets and custom resources.
2026.08.14

This page has been translated by machine translation. View original

Introduction

Hello everyone, this is Akaike.
Have you ever wanted to configure Security Hub CSPM control SSM.7 across all accounts at once? I have.

So this time, I'll summarize how to automatically deploy this setting to all AWS accounts using CloudFormation StackSets and address SSM.7.

About SSM.7

What SSM.7 requires is enabling the "block public sharing" setting for SSM documents.
This setting also needs to be configured individually for each region.

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/

Due to the per-account and per-region nature of this setting, the cost of manual work grows as the number of targets increases.
It's also a hassle to configure this every time a new account is created.

So I wanted to use CloudFormation StackSets for bulk deployment, and by also using StackSets' automatic deployment for new accounts, free myself from having to manually configure this each time.

I Was Planning to Use AWS::SSM::ServiceSetting

SSM account-level settings can be configured using the CloudFormation resource AWS::SSM::ServiceSetting.

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

Internally, it appears to execute the UpdateServiceSetting API.
In other words, the following settings that can be configured with the UpdateServiceSetting API can be set using this resource.

  • /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

And among these, /ssm/documents/console/public-sharing-permission corresponds to the SSM document public sharing block setting, so setting it to Disable should do the trick.

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

I thought this would work! But a mysterious error occurred…

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

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

AWS::SSM::ServiceSetting Was Not Available in the Tokyo Region

After investigating the cause, I found that AWS::SSM::ServiceSetting was not registered in the CloudFormation registry for the Tokyo region (ap-northeast-1).
When checking with describe-type, TypeNotFoundException is returned for the Tokyo region, while it can be retrieved normally in the Virginia region (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"
}

Since the TimeCreated on the Virginia region side is 2026-06-18, which is quite recent, it may not have rolled out to all regions yet…
I'd like to hope that it will eventually become available in the Tokyo region as well.

Just to be safe, I tried deploying StackSets from the Virginia region of the management account, but while the stack set itself was created successfully, stack instance creation in the Tokyo region still failed.

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

Handling All Regions with a Custom Resource

Since there are regions where AWS::SSM::ServiceSetting cannot be used, the only way to handle all regions together is to directly call the UpdateServiceSetting API using a custom resource.
So here is the custom resource version.

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

What it does is simple — it just sets Disable using update_service_setting.
Note that nothing is done on Delete.
When a stack is deleted, reverting the setting to Enable (allowing public sharing) would unintentionally bring SSM.7 back to a failing state, so I intentionally left the behavior to retain the block setting.

Verification

Let me try deploying this template to the Tokyo region and Virginia region.

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

The stack creation completed without any issues.

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

After deployment, I'll check the settings on the account side.
The Tokyo region and Virginia region I configured this time are set to Disable (blocking public sharing), and the Osaka region (ap-northeast-3), which was not configured, remains at the default Enable, so that looks correct.

$ 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

After that, if the target regions for StackSets deployment have the desired regions configured and automatic deployment is enabled, the block setting will be automatically applied whenever a new account is added to the organization.

Conclusion

That's all for how to enable the SSM document public sharing block setting using CloudFormation StackSets and address Security Hub CSPM control SSM.7 across all AWS accounts.
I really hope AWS::SSM::ServiceSetting becomes available in all regions soon…

Share this article

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