Intro

I don’t intend for this to be the best strategy for everybody, I reccomend reading through it first and making sure it will work for you.

Setting up Organizations

If you already have an AWS account, create another AWS account to use as your management and billing account (effectively it will be the root account.) You need to do a couple of things for your current account (where your services are located):

  • Remove billing method
  • Disable any billing services / insights, cost and usage reports, billing conductor
  • Enable MFA if you haven’t already

Configure your billing info for your new management account, and enable MFA. Now to setup Organizations:

  • Navigate to the Policies page, click the Service Control Policies link, and enable service control policies.

  • Go to the Invitations page under Account > Invitations and click the Add AWS Account button, invite your old AWS account (the one where all of your services are located.) Follow the rest of the instructions and once the organization is successfully joined, proceed to the next step.

  • Back on the Organizations main page, click the check box next to the Root OU, click the Actions menu, and under Organizational Unit click Create New. You can call it whatever you like.

  • Go to the Service Control Policies page, and create a policy with the following content:

  1{
  2  "Version": "2012-10-17",
  3  "Statement": [
  4    {
  5      "Sid": "Statement6",
  6      "Effect": "Deny",
  7      "Action": [
  8        "ec2:RunInstances"
  9      ],
 10      "Resource": [
 11        "*"
 12      ],
 13      "Condition": {
 14        "StringNotEquals": {
 15          "ec2:InstanceType": [
 16            "t2.micro",
 17            "t3.micro"
 18          ]
 19        }
 20      }
 21    },
 22    {
 23      "Sid": "Statement8",
 24      "Effect": "Deny",
 25      "Action": [
 26        "ec2:AcceptTransitGatewayMulticastDomainAssociations",
 27        "ec2:AcceptTransitGatewayPeeringAttachment",
 28        "ec2:AcceptTransitGatewayVpcAttachment",
 29        "ec2:AssociateTransitGatewayMulticastDomain",
 30        "ec2:AssociateTransitGatewayPolicyTable",
 31        "ec2:AssociateTransitGatewayRouteTable",
 32        "ec2:CreateTransitGateway",
 33        "ec2:CreateTransitGatewayConnect",
 34        "ec2:CreateTransitGatewayConnectPeer",
 35        "ec2:CreateTransitGatewayPeeringAttachment",
 36        "ec2:CreateTransitGatewayPolicyTable",
 37        "ec2:CreateTransitGatewayPrefixListReference",
 38        "ec2:CreateTransitGatewayRoute",
 39        "ec2:CreateTransitGatewayRouteTable",
 40        "ec2:CreateTransitGatewayRouteTableAnnouncement",
 41        "ec2:CreateTransitGatewayVpcAttachment",
 42        "ec2:ApplySecurityGroupsToClientVpnTargetNetwork",
 43        "ec2:AssociateClientVpnTargetNetwork",
 44        "ec2:AttachVpnGateway",
 45        "ec2:AuthorizeClientVpnIngress",
 46        "ec2:CreateClientVpnEndpoint",
 47        "ec2:CreateClientVpnRoute",
 48        "ec2:CreateVpnConnection",
 49        "ec2:CreateVpnConnectionRoute",
 50        "ec2:CreateVpnGateway",
 51        "ec2:CreateCustomerGateway",
 52        "ec2:CreateNatGateway",
 53        "route53:CreateQueryLoggingConfig",
 54        "route53:CreateHealthCheck",
 55        "route53:CreateTrafficPolicy",
 56        "route53:CreateTrafficPolicyInstance",
 57        "route53:CreateTrafficPolicyVersion",
 58        "route53:UpdateTrafficPolicyComment",
 59        "route53:UpdateTrafficPolicyInstance",
 60        "ec2:AllocateHosts",
 61        "ec2:ModifyHosts",
 62        "ec2:PurchaseHostReservation",
 63        "ec2:PurchaseScheduledInstances",
 64        "ec2:RunScheduledInstances",
 65        "ec2:GetReservedInstancesExchangeQuote",
 66        "ec2:AcceptReservedInstancesExchangeQuote",
 67        "ec2:CancelReservedInstancesListing",
 68        "ec2:CreateReservedInstancesListing",
 69        "ec2:DeleteQueuedReservedInstances",
 70        "ec2:ModifyReservedInstances",
 71        "ec2:PurchaseReservedInstancesOffering",
 72        "ec2:CreateImage",
 73        "ec2:ExportImage",
 74        "ec2:RegisterImage",
 75        "ec2:CreateFpgaImage",
 76        "ec2:CopyFpgaImage",
 77        "ec2:CopyImage",
 78        "ec2:CreateRestoreImageTask",
 79        "ec2:CreateStoreImageTask"
 80      ],
 81      "Resource": [
 82        "*"
 83      ]
 84    },
 85    {
 86      "Sid": "Statement7",
 87      "Effect": "Deny",
 88      "Action": [
 89        "s3:PutAccountPublicAccessBlock",
 90        "s3:ListBucket"
 91      ],
 92      "Resource": [
 93        "*"
 94      ]
 95    },
 96    {
 97      "Sid": "Statement10",
 98      "Effect": "Deny",
 99      "Action": [
100        "*"
101      ],
102      "Resource": [
103        "*"
104      ],
105      "Condition": {
106        "StringNotEquals": {
107          "aws:RequestedRegion": [
108            "us-east-1"
109          ]
110        }
111      }
112    },
113    {
114      "Sid": "Statement9",
115      "Effect": "Allow",
116      "Action": [
117        "ec2:*",
118        "lambda:*",
119        "sns:*",
120        "cloudfront:*",
121        "cognito-identity:*",
122        "cognito-sync:*",
123        "cognito-idp:*",
124        "apigateway:*",
125        "s3:*",
126        "elasticloadbalancing:*",
127        "acm:*",
128        "ses:*",
129        "route53:*",
130        "dynamodb:*",
131        "storagegateway:*",
132        "iam:*"
133      ],
134      "Resource": [
135        "*"
136      ]
137    }
138  ]
139}
  • Navigate back to the Organizations page, and click the new OU you just created. Click the policies tab, and attach the new policy you just created.
  • In the Applied policies list, click the radio button for the FullAWSAccess (not the inherited one, but the one that is attached directly) then click Detach. This didn’t make sense to me at first, but inherited just means that if you attach it directly, then it will apply. If it’s not attached directly, it will not apply.
  • Navigate back to the Organizations page, click the check box next to the account you invited to your organization, click the Actions menu, and under AWS Account click Move. Move the account under the new OU you just created and applied a service control policy to.
  • Now go back again to the Organizations page, collapse your OU, and click the link for the account that you moved underneath it.
  • Click the policies tab, first make sure the policy that you created earlier is attached directly to it, then if FullAWSAccess is attached directly to it, detach it.

Thats it, organizations are now set up and you’re using the same policy that I am using.

About the Service Control Policy

Essentially, no services that I can’t imagine myself ever using are enabled. I haven’t finished exploring it much myself, but what’s here seems to work fine and I’ll probably release an update to this later.

Services

The services I’ve allowed are:

  • EC2
  • Lambda
  • SNS
  • cloudfront
  • API Gateway
  • S3
  • Elastic Load Balancer
  • ACM
  • SES
  • Route53
  • DynamoDB
  • Storage Gateway
  • IAM

For EC2 and Route53

I disabled quite a few methods that I don’t intend to use. Stuff like transit gateway and the likes that will only cost money that I don’t want to spend for example.

  • ec2:AcceptTransitGatewayMulticastDomainAssociations
  • ec2:AcceptTransitGatewayPeeringAttachment
  • ec2:AcceptTransitGatewayVpcAttachment
  • ec2:AssociateTransitGatewayMulticastDomain
  • ec2:AssociateTransitGatewayPolicyTable
  • ec2:AssociateTransitGatewayRouteTable
  • ec2:CreateTransitGateway
  • ec2:CreateTransitGatewayConnect
  • ec2:CreateTransitGatewayConnectPeer
  • ec2:CreateTransitGatewayPeeringAttachment
  • ec2:CreateTransitGatewayPolicyTable
  • ec2:CreateTransitGatewayPrefixListReference
  • ec2:CreateTransitGatewayRoute
  • ec2:CreateTransitGatewayRouteTable
  • ec2:CreateTransitGatewayRouteTableAnnouncement
  • ec2:CreateTransitGatewayVpcAttachment
  • ec2:ApplySecurityGroupsToClientVpnTargetNetwork
  • ec2:AssociateClientVpnTargetNetwork
  • ec2:AttachVpnGateway
  • ec2:AuthorizeClientVpnIngress
  • ec2:CreateClientVpnEndpoint
  • ec2:CreateClientVpnRoute
  • ec2:CreateVpnConnection
  • ec2:CreateVpnConnectionRoute
  • ec2:CreateVpnGateway
  • ec2:CreateCustomerGateway
  • ec2:CreateNatGateway
  • route53:CreateQueryLoggingConfig
  • route53:CreateHealthCheck
  • route53:CreateTrafficPolicy
  • route53:CreateTrafficPolicyInstance
  • route53:CreateTrafficPolicyVersion
  • route53:UpdateTrafficPolicyComment
  • route53:UpdateTrafficPolicyInstance
  • ec2:AllocateHosts
  • ec2:ModifyHosts
  • ec2:PurchaseHostReservation
  • ec2:PurchaseScheduledInstances
  • ec2:RunScheduledInstances
  • ec2:GetReservedInstancesExchangeQuote
  • ec2:AcceptReservedInstancesExchangeQuote
  • ec2:CancelReservedInstancesListing
  • ec2:CreateReservedInstancesListing
  • ec2:DeleteQueuedReservedInstances
  • ec2:ModifyReservedInstances
  • ec2:PurchaseReservedInstancesOffering
  • ec2:CreateImage
  • ec2:ExportImage
  • ec2:RegisterImage
  • ec2:CreateFpgaImage
  • ec2:CopyFpgaImage
  • ec2:CopyImage
  • ec2:CreateRestoreImageTask
  • ec2:CreateStoreImageTask"

For EC2, you can only create instances of the following type:

  • t2.micro
  • t3.micro

All other are denied.

S3

The following methods are disabled:

  • s3:PutAccountPublicAccessBlock
  • s3:ListBucket

One other thing I will probably add to this is something to enforce the use of a S3 VPC endpoint. I don’t recall at the moment if the bucket policy can be used to restrict whether or not a bucket can only be accessed via a VPC endpoint but if it is then it is. I also recommend reading Implementing service control policies and VPC endpoint Policies if this topic is something of interest to you.

Regions

I’ve restricted access to only us-east-1 for now. I don’t really have a particular reason other than I just don’t use the account for anything and I just wanted to make a demo, but in general having other regions just makes things more complicated if I don’t need them, so I went with this.

Caveats

There’s a limit to how big your service control policy can get, and then you have to create another. Kinda sucks, but so far this hasn’t proven to be that big of a deal. Other OUs can be created but there are limits to how many. There are also limits to how many policies can be directly attached.