Quick Tip: Minimizing Terraformed SCPs

Dec 05, 23

Service Control Policies

In AWS, Service Control Policies are a powerful mechanism for creating centralized guardrails in AWS.

For example, at Figma we’ve rolled out both Service and Region allowlisting using SCPs, dramatically reducing our attack surface.

For general guidance on SCPs, I highly recommend Scott Piper’s guides: AWS SCP Best Practices (Summit Route, 2020) and Using Service Control Policies to protect security baselines (Wiz, 2023)

SCP Quotas

When making extensive use of SCPs, you’re bound to start bumping up against AWS’ relevant quotas.

Value Quota
OU maximum nesting in a root Five levels of OUs deep under a root.
Maximum SCPs attached to root, per OU, per Account 5
Maximum size of a policy document Service control policies: 5120 characters

There is an interesting quirk to that maximum policy size: unlike in IAM, with SCPs whitespace counts towards this character limit!

All characters in your SCP count against its maximum size. The examples in this guide show the SCPs formatted with extra white space to improve their readability. However, to save space if your policy size approaches the maximum size, you can delete any white space, such as space characters and line breaks that are outside quotation marks.
AWS Organizations: Maximum Size of SCPs

Policies have a maximum size between 2048 characters and 10,240 characters, depending on what entity the policy is attached to. For more information, see IAM and AWS STS quotas. Policy size calculations do not include white space characters.
AWS Identity and Access Management: Policy Grammer Notes

Terraforming SCPs

This limit is particularly disruptive when managing SCPs programmatically, including using Terraform. This is because, while the AWS Management Console minimizes whitespace automatically, the SDKs and CLI do not.

minimal-scp: a simple helper to skate under SCP limits

Once we first hit this limit, I decided to offer an improved paved road for our SCPs to avoid this issue.

It took a little research, but the end result was straightforward. Now, we use a minimal-scp module with the following definition:

variable "name" {
  type = string

variable "description" {
  type = string

variable "content" {
  type        = string
  description = "The aws_iam_policy_document .json content to use for the policy"

resource "aws_organizations_policy" "policy" {
  name        = var.name
  description = var.description
  content     = jsonencode(jsondecode(var.content))

output "id" {
  value = aws_organizations_policy.policy.id

Usage is simple, and matches the aws_organizations_policy syntax:

module "example_scp" {
  source = "./modules/minimal-scp"

  name = "example_scp"
  description = "This is an example SCP"
  content = data.iam_policy_document.example_scp.json

The simple trick?

The jsonencode command outputs a minified representation of the input.
jsonencode Function

The More You Know.gif