AWS released a new type of organizational authorizational policy called Resource Control Policies (RCPs). This policy is considered a "resource-centric" policy, complementary to Service Control Policies "principal-centric" preventative guardrails.
See the AWS release blog for more info about RCPs: https://aws.amazon.com/blogs/aws/introducing-resource-control-policies-rcps-a-new-authorization-policy/
Terraforming
One of the first questions I ask with a new AWS feature or service is "how do I automate it?" For RCPs, I asked a similar question and found that the AWS provider in Terraform had support as of version 5.76.0.
First, ensure you have the policy type enabled on your organization via "ALL" or "RESOURCE_CONTROL_POLICY" like shown below. This will enable RCPs as well as attach RCPFullAWSAccess.
resource "aws_organizations_organization" "org" {
feature_set = "ALL"
enabled_policy_types = [
"AISERVICES_OPT_OUT_POLICY",
"SERVICE_CONTROL_POLICY",
"RESOURCE_CONTROL_POLICY",
]
}
Ensure your provider is up to 5.76. You could also use "~> 5.0".
See Version Constraints documentation for more info on how this works https://developer.hashicorp.com/terraform/language/expressions/version-constraints
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.76"
}
}
}
Now you can create a RCP. I'm using the confused deputy prevention example provided from aws-samples. Note that RCPs have similar limits to SCPs. You can only attach 5 per entity (OU and account), RCPFullAWSAccess counts against the limit, and they have a character limit of 5120 that includes whitespace.
data "aws_iam_policy_document" "rcp1" {
statement {
effect = "Deny"
actions = [
"s3:*",
"sqs:*",
"kms:*",
"secretsmanager:*",
"sts:*",
]
resources = ["*"]
principals {
type = "*"
identifiers = ["*"]
}
condition {
test = "StringNotEqualsIfExists"
variable = "aws:SourceOrgID"
values = [
aws_organizations_organization.org.id
]
}
condition {
test = "Null"
variable = "aws:SourceAccount"
values = [
"false"
]
}
condition {
test = "Bool"
variable = "aws:PrincipalIsAWSService"
values = [
"true"
]
}
}
}
resource "aws_organizations_policy" "rcp1" {
name = "rcp1"
content = data.aws_iam_policy_document.rcp1.json
type = "RESOURCE_CONTROL_POLICY"
}
If you are combining multiple RCPs into a single aws_iam_policy_document
, you may want to use minified_json
instead of json
on the content argument.
resource "aws_organizations_policy" "rcp1" {
name = "rcp1"
content = data.aws_iam_policy_document.rcp1.minified_json
type = "RESOURCE_CONTROL_POLICY"
}
And finally, attach it to an entity. In this case, I am attaching it to an OU I have called "dev" under the organization root.
resource "aws_organizations_organizational_unit" "dev" {
name = "dev"
parent_id = aws_organizations_organization.org.roots[0].id
}
resource "aws_organizations_policy_attachment" "rcp1_dev" {
policy_id = aws_organizations_policy.rcp1.id
target_id = aws_organizations_organizational_unit.dev.id
}
It's very nice to see Terraform provider support so quickly for a new release, especially on governance features.
Happy RCP'ing!