# ORG policy evaluations ======================================================= # Controls: ORG-001 through ORG-008 # # Scope guard: all rules require aws_organizations_organization in the plan. # Workload plans without org resources are unaffected. package controls.aws.org import data.common.exemptions as exemptions import data.common.outputs as outputs # Scope guard ------------------------------------------------------------------ _org_in_scope if { some _, rc in input.resource_changes rc.type == "aws_organizations_organization" } # ORG-001 — AWS Organizations must have all features enabled ------------------ deny contains msg if { policy_id := "ICP-TF-AWS-ORG-001" _org_in_scope some _, rc in input.resource_changes rc.type == "aws_organizations_organization" rc.change.actions[_] in ["create", "update"] rc.change.after.feature_set != "ALL" meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("Organization '%s' does not have all features enabled (feature_set = ALL required)", [rc.name]), ) } # ORG-002 — Organizations policy types must be enabled on root ---------------- deny contains msg if { policy_id := "ICP-TF-AWS-ORG-002" _org_in_scope some _, rc in input.resource_changes rc.type == "aws_organizations_organization" rc.change.actions[_] in ["create", "update"] some required in _required_policy_types not required in rc.change.after.enabled_policy_types meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("Organization '%s' is missing required policy type '%s'", [rc.name, required]), ) } _required_policy_types := { "TAG_POLICY", "BACKUP_POLICY", "AISERVICES_OPT_OUT_POLICY", } # ORG-003 — Trusted service access for foundational security services --------- deny contains msg if { policy_id := "ICP-TF-AWS-ORG-003" _org_in_scope some _, rc in input.resource_changes rc.type == "aws_organizations_organization" rc.change.actions[_] in ["create", "update"] some required in _required_trusted_services not required in rc.change.after.aws_service_access_principals meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("Organization '%s' is missing trusted service access for '%s'", [rc.name, required]), ) } _required_trusted_services := { "cloudtrail.amazonaws.com", "config.amazonaws.com", "securityhub.amazonaws.com", "guardduty.amazonaws.com", "access-analyzer.amazonaws.com", "ram.amazonaws.com", } # ORG-004 — Delegated administrator for security services --------------------- deny contains msg if { policy_id := "ICP-TF-AWS-ORG-004" _org_in_scope some required in _required_delegated_services not _delegated_admin_exists(required) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("No delegated administrator configured for service '%s'", [required]), ) } _required_delegated_services := { "securityhub.amazonaws.com", "guardduty.amazonaws.com", "config.amazonaws.com", "access-analyzer.amazonaws.com", } _delegated_admin_exists(service) if { some _, rc in input.resource_changes rc.type == "aws_organizations_delegated_administrator" rc.change.actions[_] in ["create", "update"] rc.change.after.service_principal == service } # ORG-005 — Account alternate contacts for SECURITY, BILLING, OPERATIONS ------ deny contains msg if { policy_id := "ICP-TF-AWS-ORG-005" _org_in_scope some contact_type in _required_contact_types not _alternate_contact_exists(contact_type) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("No alternate contact configured for contact type '%s'", [contact_type]), ) } _required_contact_types := {"SECURITY", "BILLING", "OPERATIONS"} _alternate_contact_exists(contact_type) if { some _, rc in input.resource_changes rc.type == "aws_account_alternate_contact" rc.change.actions[_] in ["create", "update"] rc.change.after.alternate_contact_type == contact_type } # ORG-006 — Organization Tag Policy must be defined and attached -------------- deny contains msg if { policy_id := "ICP-TF-AWS-ORG-006" _org_in_scope not _org_policy_exists("TAG_POLICY") meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "No aws_organizations_policy of type TAG_POLICY found in plan", ) } deny contains msg if { policy_id := "ICP-TF-AWS-ORG-006" _org_in_scope _org_policy_exists("TAG_POLICY") not _org_policy_attached("TAG_POLICY") meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "TAG_POLICY exists but no aws_organizations_policy_attachment found for it", ) } # ORG-007 — Organization Backup Policy must be defined and attached ----------- deny contains msg if { policy_id := "ICP-TF-AWS-ORG-007" _org_in_scope not _org_policy_exists("BACKUP_POLICY") meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "No aws_organizations_policy of type BACKUP_POLICY found in plan", ) } deny contains msg if { policy_id := "ICP-TF-AWS-ORG-007" _org_in_scope _org_policy_exists("BACKUP_POLICY") not _org_policy_attached("BACKUP_POLICY") meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "BACKUP_POLICY exists but no aws_organizations_policy_attachment found for it", ) } # ORG-008 — Organization Units must be defined -------------------------------- deny contains msg if { policy_id := "ICP-TF-AWS-ORG-008" _org_in_scope not _ou_exists meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "No aws_organizations_organizational_unit found in plan — flat account structure detected", ) } _ou_exists if { some _, rc in input.resource_changes rc.type == "aws_organizations_organizational_unit" rc.change.actions[_] in ["create", "update"] } # Helpers ---------------------------------------------------------------------- _org_policy_exists(policy_type) if { some _, rc in input.resource_changes rc.type == "aws_organizations_policy" rc.change.actions[_] in ["create", "update"] rc.change.after.type == policy_type } _org_policy_attached(policy_type) if { some _, policy_rc in input.resource_changes policy_rc.type == "aws_organizations_policy" policy_rc.change.actions[_] in ["create", "update"] policy_rc.change.after.type == policy_type some _, attach_rc in input.resource_changes attach_rc.type == "aws_organizations_policy_attachment" attach_rc.change.actions[_] in ["create", "update"] some r in input.configuration.root_module.resources r.address == attach_rc.address some ref in r.expressions.policy_id.references ref == policy_rc.address }