Organizations (SCPs)
SCP security baseline for CloudTrail, GuardDuty, Config and S3
Organization-level S3 Block Public Access plus an SCP that stops member accounts from weakening CloudTrail, GuardDuty, AWS Config or leaving the organization.
Published · Updated .
The problem
An intruder with administrator access in a member account usually blinds you first: stops the trail, deletes the GuardDuty detector or trusts their own IP addresses, narrows AWS Config recording, and turns off S3 Block Public Access. This SCP blocks those changes for every principal in member accounts, including root users, except one platform security role.
Earlier versions of this page, and of the S3 Block Public Access page it replaces, claimed more than they blocked. They missed config:PutConfigurationRecorder, GuardDuty filters (which can auto-archive findings) and trusted IP lists (which suppress them), and exempted PlatformSecurityAdmin by name without protecting it.
For S3, the primary control is now organization-level Block Public Access, an AWS Organizations S3 policy available since November 2025. The SCP is defense in depth.
The policies
Security baseline SCP
Service control policy (SCP). Attach to: Workload OUs, after testing on a sandbox OU. Give the OU with your security tooling accounts its own variant (see Pitfalls).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ProtectCloudTrail",
"Effect": "Deny",
"Action": [
"cloudtrail:DeleteEventDataStore",
"cloudtrail:DeleteTrail",
"cloudtrail:PutEventSelectors",
"cloudtrail:PutInsightSelectors",
"cloudtrail:StopEventDataStoreIngestion",
"cloudtrail:StopLogging",
"cloudtrail:UpdateEventDataStore",
"cloudtrail:UpdateTrail"
],
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/PlatformSecurityAdmin"
}
}
},
{
"Sid": "ProtectGuardDuty",
"Effect": "Deny",
"Action": [
"guardduty:Accept*",
"guardduty:Archive*",
"guardduty:Create*",
"guardduty:Delete*",
"guardduty:Disassociate*",
"guardduty:Stop*",
"guardduty:Update*"
],
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/PlatformSecurityAdmin"
}
}
},
{
"Sid": "ProtectConfig",
"Effect": "Deny",
"Action": [
"config:AssociateResourceTypes",
"config:DeleteAggregationAuthorization",
"config:DeleteConfigRule",
"config:DeleteConfigurationRecorder",
"config:DeleteDeliveryChannel",
"config:DeleteEvaluationResults",
"config:DeleteRetentionConfiguration",
"config:DeleteServiceLinkedConfigurationRecorder",
"config:DisassociateResourceTypes",
"config:PutConfigurationRecorder",
"config:PutDeliveryChannel",
"config:PutRetentionConfiguration",
"config:StopConfigurationRecorder"
],
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/PlatformSecurityAdmin"
}
}
},
{
"Sid": "ProtectAccountBlockPublicAccess",
"Effect": "Deny",
"Action": "s3:PutAccountPublicAccessBlock",
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/PlatformSecurityAdmin"
}
}
},
{
"Sid": "DenyLeavingTheOrganization",
"Effect": "Deny",
"Action": "organizations:LeaveOrganization",
"Resource": "*"
}
]
}The form generators on this site don't edit scp documents; copy it, then check it with IAM Access Analyzer as shown below.
Statement by statement
- ProtectCloudTrail: denies stopping, deleting or reconfiguring trails, narrowing what they record (event and Insights selectors), and deleting, changing or stopping CloudTrail Lake event data stores. Member accounts already can't change an organization trail, so this protects trails created in the account.
- ProtectGuardDuty: AWS's example denies all GuardDuty write actions; these wildcards deny the weakening ones, including future additions. They cover DeleteDetector and UpdateDetector, DisassociateFromAdministratorAccount and the legacy DisassociateFromMasterAccount, DisassociateMembers, DeleteMembers, StopMonitoringMembers, filters (an archiving filter is a suppression rule), IP, threat intel, trusted entity and threat entity sets, publishing destinations, malware protection plans, ArchiveFindings and AcceptAdministratorInvitation. Harmless actions such as CreateSampleFindings are denied too.
- ProtectConfig: denies stopping or deleting the recorder, changing its scope (PutConfigurationRecorder and the resource-type actions), deleting or redirecting the delivery channel, changing retention, and deleting rules, evaluation results, aggregation authorizations and service-linked recorders. PutConfigRule stays allowed so teams can add rules; see Pitfalls.
- ProtectAccountBlockPublicAccess: s3:PutAccountPublicAccessBlock is the permission for both setting and deleting the account-level configuration; there is no separate delete permission. Bucket-level settings are left alone: S3 applies the most restrictive combination of organization, account and bucket settings, so a bucket can't loosen what the account blocks.
- DenyLeavingTheOrganization: an account that leaves escapes every SCP. No exemption.
Companion SCP: protect PlatformSecurityAdmin and the role that maintains it
Service control policy (SCP). Attach to: The organization root, or at least everywhere the security baseline applies.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ProtectExemptAndDeployerRoles",
"Effect": "Deny",
"Action": [
"iam:AttachRolePolicy",
"iam:CreateRole",
"iam:DeleteRole",
"iam:DeleteRolePermissionsBoundary",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePermissionsBoundary",
"iam:PutRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription"
],
"Resource": [
"arn:aws:iam::*:role/PlatformSecurityAdmin",
"arn:aws:iam::*:role/OrganizationAccountAccessRole"
],
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/OrganizationAccountAccessRole"
}
}
},
{
"Sid": "OnlySecurityAccountMayAssumeExemptRole",
"Effect": "Deny",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/PlatformSecurityAdmin",
"Condition": {
"StringNotEquals": {
"aws:PrincipalAccount": "444455556666"
}
}
}
]
}The form generators on this site don't edit scp documents; copy it, then check it with IAM Access Analyzer as shown below.
Statement by statement
- ProtectExemptAndDeployerRoles: denies creating, deleting or changing PlatformSecurityAdmin and OrganizationAccountAccessRole: trust policy, inline and attached policies, permissions boundary, settings such as maximum session duration, and tags (which matter once any policy keys on them). The only exempt caller is OrganizationAccountAccessRole, which AWS Organizations creates in the accounts it creates and which trusts the management account. Because that role is itself protected, nobody in a member account can create it, re-point its trust or widen it to become the exempt caller. Every exemption by name needs this: the statement that exempts a name must also protect it.
- OnlySecurityAccountMayAssumeExemptRole: denies sts:AssumeRole on PlatformSecurityAdmin to every covered principal outside the security account 444455556666. SCPs restrict the caller, so this holds even if the trust policy is too broad. Management-account principals aren't subject to SCPs; for them the trust policy alone decides.
Trust policy for PlatformSecurityAdmin
IAM role trust policy. Attach to: PlatformSecurityAdmin in each member account, created through OrganizationAccountAccessRole.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSecurityOperatorsOnly",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::444455556666:role/PlatformSecurityOperator"
},
"Action": "sts:AssumeRole"
}
]
}The form generators on this site don't edit trust policy documents; copy it, then check it with IAM Access Analyzer as shown below.
Statement by statement
- AllowSecurityOperatorsOnly: only PlatformSecurityOperator in the security account can assume the role. Never trust the member account, a principal in it or a service principal.
Before you start
- Organizations with all features and SCPs enabled, and FullAWSAccess still attached.
- CloudTrail, GuardDuty and Config already configured, ideally centrally: an organization trail, a GuardDuty delegated administrator and Config recorders in every Region you use.
- OrganizationAccountAccessRole in each member account, trusting only the management account (for invited accounts, created before they join).
- A security account (444455556666) with a PlatformSecurityOperator role, and a sandbox OU.
Deploy it step by step
- Enforce Block Public Access at the organization level. Enable the S3 policy type, create a policy that assigns all, and attach it to the sandbox OU before the root. The four settings are enabled or disabled together; while the policy applies, account-level changes are refused, and detaching it restores each account's previous settings. First find buckets meant to be public, such as S3 static websites: they will stop being public.
Commands (replace <placeholders>) aws organizations enable-policy-type --root-id <root-id> --policy-type S3_POLICY aws organizations create-policy --type S3_POLICY --name BlockAllS3PublicAccess --description 'Organization-level S3 Block Public Access' --content '{"s3_attributes":{"public_access_block_configuration":{"@@assign":"all"}}}' aws organizations attach-policy --policy-id <s3-policy-id> --target-id <sandbox-ou-id> - Record the current state. In each account, record what the SCP is about to freeze and fix anything wrong now; afterwards only PlatformSecurityAdmin can.
Commands (replace <placeholders>) aws cloudtrail describe-trails aws guardduty list-detectors aws configservice describe-configuration-recorders aws configservice describe-delivery-channels aws s3control get-public-access-block --account-id 111122223333 - Create PlatformSecurityAdmin through the deployer. Assume OrganizationAccountAccessRole and create the role. The deployer options are as on the Region-deny example: OrganizationAccountAccessRole, AWSControlTowerExecution in Control Tower, or stacksets-exec-* if you exempt and protect that pattern. If the role already exists, check its trust policy first.
Commands (replace <placeholders>) aws sts assume-role --role-arn arn:aws:iam::111122223333:role/OrganizationAccountAccessRole --role-session-name <your-name>-security-setup aws iam create-role --role-name PlatformSecurityAdmin --assume-role-policy-document file://platform-security-trust.json aws iam attach-role-policy --role-name PlatformSecurityAdmin --policy-arn <security-permissions-policy-arn> - Validate, create and attach to the sandbox OU. Attach the companion first, then run the checks below in the sandbox account.
Commands (replace <placeholders>) aws accessanalyzer validate-policy --policy-type SERVICE_CONTROL_POLICY --policy-document file://scp.json aws organizations create-policy --type SERVICE_CONTROL_POLICY --name ProtectExemptRoles --description 'Protect PlatformSecurityAdmin and its deployer' --content file://protect-exempt-roles.json aws organizations create-policy --type SERVICE_CONTROL_POLICY --name SecurityBaseline --description 'Protect CloudTrail, GuardDuty, Config and Block Public Access' --content file://security-baseline.json aws organizations attach-policy --policy-id <protect-policy-id> --target-id <sandbox-ou-id> aws organizations attach-policy --policy-id <baseline-policy-id> --target-id <sandbox-ou-id> - Roll out. Attach the companion to the root, the baseline to one workload OU at a time, then the S3 policy to the root. If you also use the Region-deny example, merge both exempt roles into one companion policy to save SCP slots.
Commands (replace <placeholders>) aws organizations attach-policy --policy-id <protect-policy-id> --target-id <root-id> aws organizations attach-policy --policy-id <baseline-policy-id> --target-id <workload-ou-id>
Verify it without changing anything
These checks only read state, simulate, or use dry runs, so they are safe to run in any account. Exact error wording varies by service and over time.
Simulate the weakening actions as a workload administrator
In the sandbox account. Nothing changes, unlike the old advice to try stop-logging on a real trail.
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::111122223333:role/<workload-admin-role> --action-names cloudtrail:StopLogging cloudtrail:PutEventSelectors guardduty:DeleteDetector guardduty:CreateIPSet guardduty:CreateFilter config:StopConfigurationRecorder config:PutConfigurationRecorder s3:PutAccountPublicAccessBlock organizations:LeaveOrganization --context-entries "ContextKeyName=aws:PrincipalArn,ContextKeyValues=arn:aws:iam::111122223333:role/<workload-admin-role>,ContextKeyType=string" --query "EvaluationResults[].[EvalActionName,EvalDecision,OrganizationsDecisionDetail.AllowedByOrganizations]" --output tableExpected: AllowedByOrganizations false for every action. EvalDecision may also reflect identity policies; the Organizations column tests the SCP. If it is empty, SCPs weren't evaluated.
Simulate the same actions as PlatformSecurityAdmin
Proves the exemption works and the security team isn't locked out.
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::111122223333:role/PlatformSecurityAdmin --action-names cloudtrail:StopLogging guardduty:UpdateDetector config:PutConfigurationRecorder s3:PutAccountPublicAccessBlock organizations:LeaveOrganization --context-entries "ContextKeyName=aws:PrincipalArn,ContextKeyValues=arn:aws:iam::111122223333:role/PlatformSecurityAdmin,ContextKeyType=string" --query "EvaluationResults[].[EvalActionName,EvalDecision,OrganizationsDecisionDetail.AllowedByOrganizations]" --output tableExpected: AllowedByOrganizations true for the first four and false for organizations:LeaveOrganization, which has no exemption. The final decision also depends on the role's own permissions.
Confirm that the services are still on
Read-only, in each Region you use. The SCP prevents changes; it turns nothing on.
aws cloudtrail get-trail-status --name <trail-name>
aws guardduty get-detector --detector-id <detector-id>
aws configservice describe-configuration-recorder-statusExpected: IsLogging true, detector Status ENABLED and a recorder that reports recording. Anything off means the baseline is protecting a disabled service.
Check organization-level Block Public Access
describe-effective-policy supports S3 policies (not SCPs). Run it in the management account, then read the settings from inside the account.
aws organizations describe-effective-policy --policy-type S3_POLICY --target-id 111122223333
aws s3control get-public-access-block --account-id 111122223333Expected: The effective policy assigns all to public_access_block_configuration, and the account should report all four settings true. AWS doesn't document how the account API shows organization-managed settings; the effective policy is the authority.
Confirm that PlatformSecurityAdmin can't be taken over
Both calls are read-only.
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::111122223333:role/<workload-admin-role> --action-names iam:CreateRole iam:DeleteRole iam:UpdateAssumeRolePolicy iam:AttachRolePolicy iam:PutRolePolicy sts:AssumeRole --resource-arns arn:aws:iam::111122223333:role/PlatformSecurityAdmin --context-entries "ContextKeyName=aws:PrincipalArn,ContextKeyValues=arn:aws:iam::111122223333:role/<workload-admin-role>,ContextKeyType=string" --query "EvaluationResults[].[EvalActionName,EvalDecision,OrganizationsDecisionDetail.AllowedByOrganizations]" --output table
aws iam get-role --role-name PlatformSecurityAdmin --query Role.AssumeRolePolicyDocumentExpected: AllowedByOrganizations false for every action, and a trust policy naming only arn:aws:iam::444455556666:role/PlatformSecurityOperator.
Common mistakes and troubleshooting
Locking out your own security tooling
The GuardDuty delegated administrator and Config aggregator usually live in a security account. Under the baseline, its operators must act as PlatformSecurityAdmin, so give the Security OU a variant. Work that GuardDuty or Config does through its own service-linked roles isn't affected.
Assuming AWSControlTowerExecution is outside SCPs
It isn't a service-linked role: Organizations creates it for Account Factory accounts, a StackSet creates it when you extend governance, or you create it to enroll an account. SCPs apply to it, which is why Control Tower's own SCPs exempt it by aws:PrincipalArn. This baseline doesn't, so it can block Control Tower from configuring Config or CloudTrail. Exempt AWSControlTowerExecution and stacksets-exec-*, which Control Tower's mandatory role-protection control protects (check it covers the same OUs).
Denying bucket-level Block Public Access changes
The old page also denied s3:PutBucketPublicAccessBlock. That breaks infrastructure code that sets a bucket's own configuration, and adds nothing: the organization or account setting wins.
Leaving PutConfigRule open
config:PutConfigRule also updates existing rules, so a member administrator could weaken a security rule created in the account. Deploy security rules as organization rules or conformance packs, or deny PutConfigRule as AWS's example does, which also stops teams writing their own.
Testing from the management account
SCPs never apply there, so every action succeeds and proves nothing.
Limitations and production considerations
- SCPs don't apply to the management account, to service-linked roles or to principals outside the organization.
- The baseline protects the services, not where their data lands: protect the log archive bucket, its KMS key and log groups where they live. AWS's SCP examples also cover Security Hub, IAM Access Analyzer and Macie.
- Remaining trust is as for the Region-deny example: principals who can assume OrganizationAccountAccessRole, anyone who can change SCPs, and the operators in 444455556666.
- Each SCP is limited to 10,240 characters; both here are far below it.
- Organization-level Block Public Access is all or nothing: an account that needs a public bucket must sit outside the S3 policy's scope.
- Access Analyzer and this site's checks confirm structure, not how AWS will authorize a request. Rely on the sandbox checks, and treat the simulator as a model.
Official AWS documentation
- Service control policies (SCPs)
- Blocking public access to your Amazon S3 storage
- Amazon S3 policies (AWS Organizations)
- Amazon S3 policy syntax and examples
- Amazon S3 Block Public Access now supports organization-level enforcement (November 2025)
- aws organizations describe-effective-policy
- Service control policy examples (AWS Organizations)
- SCP evaluation
- Mandatory controls (AWS Control Tower)
- The AWSControlTowerExecution role, explained
- Creating a trail for an organization
- Managing GuardDuty accounts with AWS Organizations
- GuardDuty entity lists and IP address lists
- Suppression rules in GuardDuty
- Working with the AWS Config configuration recorder
- IAM policy testing with the IAM policy simulator
- Quotas and service limits for AWS Organizations
Account IDs, bucket and organization names are AWS documentation placeholders. Test in a non-production account before use (see the methodology).