Service Control Policies (SCPs) offer central access controls for all IAM entities in AWS accounts. You can use them to enforce the permissions you want everyone in your business to follow or to be compliant with specific laws which you need to follow (eg.: you can deny regions or services to be DSGVO compliant). If you want to use SCPs you need to create and apply those SCPs through AWS Organizations - SCPs can be assigned to AWS accounts or Organizational Units (OUs).
❗️ The Master account of the Organization can't be restricted by using SCPs.
❌ SCPs cannot restrict principals outside of the Organization.
⚠️ SCPs are similar to IAM boundaries.
In the following section I will introduce some of my and AWS SCP - Best Practices which I am using. This SCPs will help you to secure your accounts and resources - plus it will help you to avoid unnecessary costs.
When using an Organization, expensive services should be deployed centrally and shared with all accounts eg. private ACM. That way you can save some costs.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyACM",
"Effect": "Deny",
"Action": [
"acm-pca:CreateCertificateAuthority",
"acm-pca:DeleteCertificateAuthority",
"acm-pca:CreatePermission",
"acm-pca:DeletePermission",
"acm-pca:DescribeCertificateAuthorityAuditReport",
"acm-pca:RestoreCertificateAuthority",
"acm-pca:TagCertificateAuthority",
"acm-pca:UntagCertificateAuthority"
],
"Resource": [
"*"
],
"Condition": {
"StringNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/AWS-CentralCostTeam"
}
}
}
]
}
When using an Organization and your company need to manage reserved instances centrally you should limit the permissions to one team, which ensures that the reserved instances are fully used.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAccessToRI",
"Effect": "Deny",
"Action": [
"ec2:PurchaseReservedInstancesOffering",
"ec2:AcceptReservedInstancesExchangeQuote",
"ec2:CancelCapacityReservation",
"ec2:CancelReservedInstancesListing",
"ec2:CreateCapacityReservation",
"ec2:CreateReservedInstancesListing"
],
"Resource": [
"*"
],
"Condition": {
"StringNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/AWS-CentralCostTeam"
}
}
}
]
}
The same procedure applies for savings plans as for reserved instances in an Organization.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAccessToCreateSavingsPlans",
"Effect": "Deny",
"Action": [
"savingsplans:CreateSavingsPlans"
],
"Resource": [
"*"
],
"Condition": {
"StringNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/AWS-CentralCostTeam"
}
}
}
]
}
If your account gets compromised it is recommended to have a deny all SCP to protect your data. This SCP is attached to a quarantine OU, which prohibits every action in the compromised account.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Quarantine",
"Effect": "Deny",
"Action": "*",
"Resource": "*"
}
]
}
You need to be sure that your company just uses one region?! The following policy will enforce that your member accounts are using the specified region.
⚠️ You need to define the global services in the NotAction otherwise the accounts are not able to use them.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictToOneRegion",
"Effect": "Deny",
"NotAction": [
"a4b:*",
"artifact:*",
"aws-portal:*",
"budgets:*",
"ce:*",
"chime:*",
"cloudfront:*",
"cur:*",
"datapipeline:GetAccountLimits",
"directconnect:",
"globalaccelerator:*",
"health:*",
"iam:*",
"importexport:*",
"mobileanalytics:*",
"organizations:*",
"resource-groups:*",
"route53:*",
"route53domains:*",
"s3:GetBucketLocation",
"s3:ListAllMyBuckets",
"shield:*",
"support:*",
"tag:*",
"trustedadvisor:*",
"waf:*",
"wellarchitected:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"eu-central-1"
]
}
}
}
]
}
⚠️ If you want to use Lambda@Edge you need to add also the service lambda:* to NotAction.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictToOneRegion",
"Effect": "Deny",
"NotAction": [
"lambda:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"eu-central-1"
]
}
}
}
]
}
GuardDuty is a threat detection service that continuously monitors for malicious activity and unauthorized behavior in you accounts. In an Organization you should use a master account and connect member accounts to it. The following policy ensures it can’t be turned off, the member can't be disassociated from the master or that a user can't create sample findings.
{
"Effect": "Deny",
"Action": [
"guardduty:DeleteDetector",
"guardduty:CreateSampleFindings",
"guardduty:DisassociateFromMasterAccount"
],
"Resource": "*",
"Condition": {
"StringNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/StackSet-GuardDuty-Member-LambdaExecutionRole*"
}
}
}
Config is a service to audit and evaluate the configurations in your accounts. The following SCP prevents users or roles to disable or alter Config, except the specified role which is needed for a Lambda to enable and configure Config in all regions.
{
"Effect": "Deny",
"Action": [
"config:DeleteConfigurationRecorder",
"config:DeleteDeliveryChannel",
"config:StopConfigurationRecorder",
"config:PutDeliveryChannel",
"config:StartConfigurationRecorder"
],
"Resource": "*",
"Condition": {
"StringNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/StackSet-Enable-Config-*"
}
}
}
Secure your Organization that no accounts can leave your Organization, where they would no longer be restricted by your SCPs.
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": "organizations:LeaveOrganization",
"Resource": "*"
}
}
AWS recommends a SCP for denying use of the root user. The following policy enforces that the root user can't be used.
{
"Version": "2012-10-17",
"Statement": {
"Sid": "DenyRootUser",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": { "aws:PrincipalArn": "arn:aws:iam::*:root" }
}
}
}
Encrypt everything - like Werner Vogels says. The following SCPs will help you that users are not able to create unencrypted resources for the following services.
This SCP ensures that all Amazon S3 buckets use AES256 encryption in an AWS Account. You can adjust that also for KMS Key usage you just need to change s3:x-amz-server-side-encryption
to aws:kms
.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObject"
],
"Resource": "*",
"Effect": "Deny",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
},
{
"Action": [
"s3:PutObject"
],
"Resource": "*",
"Effect": "Deny",
"Condition": {
"Bool": {
"s3:x-amz-server-side-encryption": false
}
}
}
]
}
This SCP prevent creation of unencrypted RDS database.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RDS",
"Effect": "Deny",
"Action": [
"rds:CreateDBInstance"
],
"Resource": [
"*"
],
"Condition": {
"ForAnyValue:StringEquals": {
"rds:DatabaseEngine": [
"mariadb",
"mysql",
"oracle-ee",
"oracle-se2",
"oracle-se1",
"oracle-se",
"postgres",
"sqlserver-ee",
"sqlserver-se",
"sqlserver-ex",
"sqlserver-web"
]
},
"Bool": {
"rds:StorageEncrypted": "false"
}
}
},
{
"Sid": "Aurora",
"Effect": "Deny",
"Action": [
"rds:CreateDBCluster"
],
"Resource": [
"*"
],
"Condition": {
"Bool": {
"rds:StorageEncrypted": "false"
}
}
}
]
}
Lets start with the bad news, unfortunately there is no audit mode for SCPs, but you can use the API - GetOrganizationsAccessReport from the IAM Access Advisor to identify unused services. This API will help you to see whether services are used in an account or OU. I always use a separate OU to test new SCPs by moving a test account to see, if my SCP works as desired.