Quick Tip: Minimizing Terraformed SCPs
Dec 05, 23Service 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