Organizations
Structure¶
- Account management service which allows to consolidate multiple AWS accounts into an Organization that can be centrally managed
- Features
- Centralized, consolidated billing
- Organize your accounts into groups/OUs for access control
- Attach policy based controls (Service Control Policies)
Foundational OUs¶
OU | Description |
---|---|
Infrastructure | Shared infrastructure services |
Security | Recommended accounts for the Security OU
|
Sandbox | For personal testing |
Workloads | |
PolicyStaging | For testing SCPs |
Suspended |
|
IndividualBusinessUsers |
|
Exceptions |
|
Transitional |
|
Deployments | AWS accounts meant for CI/CD deployments |
Service Control Policies (SCPs)¶
General Info¶
- AWS Service Control Policies (SCPs) are a way of restricting the actions that can be taken in an AWS account so that all IAM users and roles, and even the
root
user, cannot perform them - This feature is part of AWS Organizations, and the SCPs are controlled by the Organization Management (Master) account
- SCPs can deny access only, they cannot allow
- You need to change from
Consolidated Billing
toEnable All Features
to start using SCPs - SCP evaluation: documentation page explaining how SCPs are evaluated with
Allow
andDeny
statements
Restrictions¶
- SCPs cannot restrict the Management account of the Organization
- This is a primary reason why it is best practice not to use the Organization Master account for anything other than Organization activities
- SCPs cannot restrict principals outside of the Organization
- If an S3 bucket is public, an SCP will not be able to stop Internet users from accessing it
- SCPs are similar to IAM boundaries, in that they define the maximum set of actions that can be allowed, but do not actually grant any privileges.
- The default SCP is
Allow *
on*
, but this doesn't mean that anyone in the accounts can do anything - It only means that the SCP is not further restricting them
- The default SCP is
- The Member accounts of the Organization are unable to see the SCPs that have been applied to them
- Further, when actions are denied, there is no way to know whether that is due to an IAM policy, an SCP, or something else
Exceptions¶
Condition Key | Description |
---|---|
aws:PrincipalAccount |
Account level exceptions |
aws:PrincipalOrgPaths |
Environment/OU level exceptions |
aws:PrincipalArn |
User/role level exceptions |
Sample SCPs¶
Deny ability to leave Organization
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": [
"organizations:LeaveOrganization",
"organizations:DeleteOrganization"
],
"Resource": "*"
}
}
Deny ability to close Accounts
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": "organizations:CloseAccount",
"Resource": "*"
}
}
Deny API calls from root users
{
"Version": "2012-10-17",
"Statement": {
"Sid": "DenyRootUser",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": { "aws:PrincipalArn": "arn:aws:iam::*:root" }
}
}
}
Require MFA to perform an API action
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyStopAndTerminateWhenMFAIsNotPresent",
"Effect": "Deny",
"Action": [
"ec2:StopInstances",
"ec2:TerminateInstances"
],
"Resource": "*",
"Condition": {"BoolIfExists": {"aws:MultiFactorAuthPresent": false}}
}
]
}
Deny ability to disrupt CloudTrail
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": [
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging",
"cloudtrail:PutEventSelectors",
"cloudtrail:UpdateTrail"
],
"Resource": ["arn:aws:cloudtrail:*:*:trail/exampletrail"]
}
}
Deny ability to disrupt GuardDuty
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": [
"guardduty:DeleteDetector",
"guardduty:DisassociateFromMasterAccount",
"guardduty:UpdateDetector",
"guardduty:CreateFilter",
"guardduty:CreateIPSet"
],
"Resource": "*"
}
}
Prevent also modifying its configuration:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"guardduty:AcceptInvitation",
"guardduty:ArchiveFindings",
"guardduty:CreateDetector",
"guardduty:CreateFilter",
"guardduty:CreateIPSet",
"guardduty:CreateMembers",
"guardduty:CreatePublishingDestination",
"guardduty:CreateSampleFindings",
"guardduty:CreateThreatIntelSet",
"guardduty:DeclineInvitations",
"guardduty:DeleteDetector",
"guardduty:DeleteFilter",
"guardduty:DeleteInvitations",
"guardduty:DeleteIPSet",
"guardduty:DeleteMembers",
"guardduty:DeletePublishingDestination",
"guardduty:DeleteThreatIntelSet",
"guardduty:DisassociateFromMasterAccount",
"guardduty:DisassociateMembers",
"guardduty:InviteMembers",
"guardduty:StartMonitoringMembers",
"guardduty:StopMonitoringMembers",
"guardduty:TagResource",
"guardduty:UnarchiveFindings",
"guardduty:UntagResource",
"guardduty:UpdateDetector",
"guardduty:UpdateFilter",
"guardduty:UpdateFindingsFeedback",
"guardduty:UpdateIPSet",
"guardduty:UpdatePublishingDestination",
"guardduty:UpdateThreatIntelSet"
],
"Resource": "*"
}
]
}
Deny ability to disable Config or changing its rules
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"config:DeleteConfigRule",
"config:DeleteConfigurationRecorder",
"config:DeleteDeliveryChannel",
"config:StopConfigurationRecorder"
],
"Resource": "*"
}
]
}
Deny ability to disrupt CloudWatch Event collection
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": [
"events:DeleteRule",
"events:DisableRule",
"events:RemoveTargets"
],
"Resource": "arn:aws:events:*:*:rule/default/NAME"
}
}
Prevent also altering its configuration:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"cloudwatch:DeleteAlarms",
"cloudwatch:DeleteDashboards",
"cloudwatch:DisableAlarmActions",
"cloudwatch:PutDashboard",
"cloudwatch:PutMetricAlarm",
"cloudwatch:SetAlarmState"
],
"Resource": "*"
}
]
}
Deny ability to modify an important IAM role
- Such as the ones used by Security teams:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAccessToASpecificRole",
"Effect": "Deny",
"Action": [
"iam:AttachRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePermissionsBoundary",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePermissionsBoundary",
"iam:PutRolePolicy",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription"
],
"Resource": [
"arn:aws:iam::*:role/NAME"
]
}
]
}
Prevent deletion or modification of IAM roles created by the Information Security team
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventInfoSecRoleActions",
"Effect": "Deny",
"Action": ["*"],
"Resource": ["arn:aws:iam::*:role/InfoSec*"],
"Condition": {
"StringNotLike": {"aws:PrincipalARN": "arn:aws:iam::*:role/InfoSec*"}
}
}
]
}
Prevent creation of IAM users with a login profile
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventIAMUserWithLoginprofile",
"Effect": "Deny",
"Action": [
"iam:ChangePassword",
"iam:CreateLoginProfile",
"iam:UpdateLoginProfile",
"iam:UpdateAccountPasswordPolicy"
],
"Resource": ["*"]
}
]
}
Prevent creation of IAM users
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventIAMUserCreation",
"Effect": "Deny",
"Action": [
"iam:CreateUser",
],
"Resource": ["*"]
}
]
}
Deny ability to create IAM access keys
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": ["iam:CreateAccessKey"],
"Resource": "*"
}
}
Deny ability to create IAM access keys for the root user
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": "iam:CreateAccessKey",
"Resource": ["*"],
"Condition": {
"StringLike": { "aws:PrincipalArn": "arn:aws:iam::*:root" }
}
}
}
Require the use of IMDSv2
- Require role credentials for an EC2 to have been retrieved using the IMDSv2:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "RequireAllEc2RolesToUseV2", "Effect": "Deny", "Action": "*", "Resource": "*", "Condition": { "NumericLessThan": { "ec2:RoleDelivery": "2.0" } } } ] }
- Ensure that EC2s can only be created if you enforce that they use IMDSv2:
{ "Version": "2012-10-17", "Statement": { "Sid": "RequireImdsV2", "Effect": "Deny", "Action": "ec2:RunInstances", "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringNotEquals": { "ec2:MetadataHttpTokens": "required" } } } }
- Deny option to change type of metadata:
{ "Version": "2012-10-17", "Statement": { "Effect": "Deny", "Action": "ec2:ModifyInstanceMetadataOptions", "Resource": "*" } }
- Ensure the hop count that can be set is restricted:
{ "Version": "2012-10-17", "Statement": { "Sid": "MaxImdsHopLimit", "Effect": "Deny", "Action": "ec2:RunInstances", "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "NumericGreaterThan": {"ec2:MetadataHttpPutResponseHopLimit": "1"} } } }
Prevent public S3 access account-wide
- Prevents modifying S3 public access block settings account-wide:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventS3PublicAccess",
"Action": ["s3:PutAccountPublicAccessBlock"],
"Resource": "*",
"Effect": "Deny"
}
]
}
Prevent deleting Amazon VPC flow logs
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:DeleteFlowLogs",
"logs:DeleteLogGroup",
"logs:DeleteLogStream"
],
"Resource": "*"
}
]
}
Deny ability to make a VPC accessible from the Internet that isn't already
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:AttachInternetGateway",
"ec2:CreateInternetGateway",
"ec2:CreateEgressOnlyInternetGateway",
"ec2:CreateVpcPeeringConnection",
"ec2:AcceptVpcPeeringConnection",
"globalaccelerator:Create*",
"globalaccelerator:Update*"
],
"Resource": "*"
}
]
}
Allow only approved services
{
"Version": "2012-10-17",
"Statement": {
"Sid": "AllowList",
"Effect": "Allow",
"Action": ["a4b:*","access-analyzer:*","account:*","acm:*","acm-pca:*","amplify:*","apigateway:*","application-autoscaling:*","applicationinsights:*","appmesh:*","appmesh-preview:*","appstream:*","appsync:*","arsenal:*","artifact:*","athena:*","autoscaling:*","autoscaling-plans:*","aws-marketplace:*","aws-marketplace-management:*","aws-portal:*","backup:*","backup-storage:*","batch:*","budgets:*","cassandra:*","ce:*","chatbot:*","chime:*","cloud9:*","clouddirectory:*","cloudformation:*","cloudfront:*","cloudhsm:*","cloudsearch:*","cloudtrail:*","cloudwatch:*","codebuild:*","codecommit:*","codedeploy:*","codeguru-profiler:*","codeguru-reviewer:*","codepipeline:*","codestar:*","codestar-notifications:*","cognito-identity:*","cognito-idp:*","cognito-sync:*","comprehend:*","comprehendmedical:*","compute-optimizer:*","config:*","connect:*","cur:*","dataexchange:*","datapipeline:*","datasync:*","dax:*","dbqms:*","deeplens:*","deepracer:*","detective:*","devicefarm:*","directconnect:*","discovery:*","dlm:*","dms:*","ds:*","dynamodb:*","ebs:*","ec2:*","ec2-instance-connect:*","ec2messages:*","ecr:*","ecs:*","eks:*","elastic-inference:*","elasticache:*","elasticbeanstalk:*","elasticfilesystem:*","elasticloadbalancing:*","elasticmapreduce:*","elastictranscoder:*","es:*","events:*","execute-api:*","firehose:*","fms:*","forecast:*","frauddetector:*","freertos:*","fsx:*","gamelift:*","glacier:*","globalaccelerator:*","glue:*","greengrass:*","groundstation:*","groundtruthlabeling:*","guardduty:*","health:*","iam:*","imagebuilder:*","importexport:*","inspector:*","iot:*","iot-device-tester:*","iot1click:*","iotanalytics:*","iotevents:*","iotsitewise:*","iotthingsgraph:*","kafka:*","kendra:*","kinesis:*","kinesisanalytics:*","kinesisvideo:*","kms:*","lakeformation:*","lambda:*","launchwizard:*","lex:*","license-manager:*","lightsail:*","logs:*","machinelearning:*","macie:*","managedblockchain:*","mechanicalturk:*","mediaconnect:*","mediaconvert:*","medialive:*","mediapackage:*","mediapackage-vod:*","mediastore:*","mediatailor:*","mgh:*","mobileanalytics:*","mobilehub:*","mobiletargeting:*","mq:*","neptune-db:*","networkmanager:*","opsworks:*","opsworks-cm:*","organizations:*","outposts:*","personalize:*","pi:*","polly:*","pricing:*","qldb:*","quicksight:*","ram:*","rds:*","rds-data:*","rds-db:*","redshift:*","rekognition:*","resource-groups:*","robomaker:*","route53:*","route53domains:*","route53resolver:*","s3:*","sagemaker:*","savingsplans:*","schemas:*","sdb:*","secretsmanager:*","securityhub:*","serverlessrepo:*","servicecatalog:*","servicediscovery:*","servicequotas:*","ses:*","shield:*","signer:*","sms:*","sms-voice:*","snowball:*","sns:*","sqs:*","ssm:*","ssmmessages:*","sso:*","sso-directory:*","states:*","storagegateway:*","sts:*","sumerian:*","support:*","swf:*","synthetics:*","tag:*","textract:*","transcribe:*","transfer:*","translate:*","trustedadvisor:*","waf:*","waf-regional:*","wafv2:*","wam:*","wellarchitected:*","workdocs:*","worklink:*","workmail:*","workmailmessageflow:*","workspaces:*","xray:*"],
"Resource": "*"
}
}
Region enforcement
- Enforce that only
eu-west-1
can be used, along with the global services:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictRegion",
"Effect": "Deny",
"NotAction": [
"a4b:*",
"budgets:*",
"ce:*",
"chime:*",
"cloudfront:*",
"cur:*",
"globalaccelerator:*",
"health:*",
"iam:*",
"importexport:*",
"mobileanalytics:*",
"organizations:*",
"route53:*",
"route53domains:*",
"shield:*",
"support:*",
"trustedadvisor:*",
"waf:*",
"wellarchitected:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"eu-west-1"
]
}
}
}
]
}
Additional Samples
Other collections of SCPs:
- terraform_aws_scp: collection of SCPs written in Terraform (generic, PCI, HIPAA)
- aws-scps-with-terraform: a template to easily create and apply SCPs
- service-control-policy-examples
- AWS IAM Permissions Guardrails
- Securing resource tags (ABAC) used for authorization
- List of expensive long-term effect AWS IAM actions that should be disabled via SCP
Terraform Sample¶
The following sample defines an SCP which denies accounts the ability to leave the Organization,
and attaches it to an OU named prod
(aws_organizations_organizational_unit.prod.id
):
resource "aws_organizations_policy" "deny_leave_org" {
name = "Deny ability to leave Organization"
type = "SERVICE_CONTROL_POLICY"
content = <<CONTENT
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "organizations:LeaveOrganization",
"Resource": "*"
}
]
}
CONTENT
}
resource "aws_organizations_policy_attachment" "deny_leave_org_ou_prod" {
policy_id = aws_organizations_policy.deny_leave_org.id
target_id = aws_organizations_organizational_unit.prod.id
}
Tooling¶
Tool | Description |
---|---|
aws-allowlister | Automatically compile an AWS Service Control Policy that ONLY allows AWS services that are compliant with your preferred compliance frameworks |
scpkit | A python module to aid in Service Control Policy management in AWS accounts |