From e2a55ea438a89ab4a8df3929e022ed17d6a6621f Mon Sep 17 00:00:00 2001 From: "waqas.yousaf" Date: Tue, 29 Oct 2024 12:41:18 +0100 Subject: [PATCH 1/3] OPS-6301: Terraform Module for SCP --- README.md | 38 +++++++++++++++++++++++++++++++++----- main.tf | 40 ++++++++++++++++++++++++++++++++++++++++ outputs.tf | 9 +++++++++ variables.tf | 15 +++++++++++++++ 4 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 main.tf create mode 100644 outputs.tf create mode 100644 variables.tf diff --git a/README.md b/README.md index da64134..974cc45 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# terraform-module-template +# Terraform Module for Service Control Policies Template for Terraform modules ## Providers -No providers. +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | @@ -34,18 +36,44 @@ No providers. ## Required Inputs -No required inputs. +The following input variables are required: + +### [policies](#input\_policies) + +Description: List of policy configurations + +Type: + +```hcl +list(object({ + name = string + description = string + statements = string # Path to the JSON file containing policy statements + target_ids = list(string) # List of target account IDs or OU IDs + })) +``` ## Optional Inputs -No optional inputs. +The following input variables are optional (have default values): + +### [tags](#input\_tags) + +Description: Tags to apply to all resources created in this module + +Type: `map(string)` + +Default: `{}` ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [policy\_arns](#output\_policy\_arns) | Map of policy ARNs. | +| [policy\_ids](#output\_policy\_ids) | Map of policy IDs. | diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..3ea6d7a --- /dev/null +++ b/main.tf @@ -0,0 +1,40 @@ +# Loop over each policy to create policy documents dynamically +data "aws_iam_policy_document" "scp_policies" { + for_each = { for p in var.policies : p.name => p } + + dynamic "statement" { + for_each = jsondecode(file(each.value.statements)) + content { + sid = lookup(statement.value, "Sid", null) + effect = lookup(statement.value, "Effect", "Deny") + actions = statement.value.Action + resources = [lookup(statement.value, "Resource", "*")] + + dynamic "condition" { + for_each = lookup(statement.value, "Condition", {}) + content { + test = condition.key + variable = condition.value[0] + values = condition.value[1] + } + } + } + } +} + +# Create policies with tags +resource "aws_organizations_policy" "scp" { + for_each = data.aws_iam_policy_document.scp_policies + name = each.value.name + description = each.value.description + content = each.value.json + tags = var.tags +} + +# Attach policies to targets with tags +resource "aws_organizations_policy_attachment" "attach_scp" { + for_each = { for p in var.policies : p.name => p } + count = length(each.value.target_ids) + policy_id = aws_organizations_policy.scp[each.key].id + target_id = each.value.target_ids[count.index] +} \ No newline at end of file diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..f359b26 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,9 @@ +output "policy_arns" { + value = { for k, v in aws_organizations_policy.scp : k => v.arn } + description = "Map of policy ARNs." +} + +output "policy_ids" { + value = { for k, v in aws_organizations_policy.scp : k => v.id } + description = "Map of policy IDs." +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..c111729 --- /dev/null +++ b/variables.tf @@ -0,0 +1,15 @@ +variable "policies" { + description = "List of policy configurations" + type = list(object({ + name = string + description = string + statements = string # Path to the JSON file containing policy statements + target_ids = list(string) # List of target account IDs or OU IDs + })) +} + +variable "tags" { + description = "Tags to apply to all resources created in this module" + type = map(string) + default = {} +} From eb89ede76a9d75423b3dfacd0821c3bd0e894169 Mon Sep 17 00:00:00 2001 From: "waqas.yousaf" Date: Wed, 30 Oct 2024 09:08:47 +0100 Subject: [PATCH 2/3] OPS-6301: refactor --- README.md | 18 +++++------------- main.tf | 47 ++++++++++++----------------------------------- variables.tf | 12 +++--------- 3 files changed, 20 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 974cc45..7189ba2 100644 --- a/README.md +++ b/README.md @@ -40,30 +40,22 @@ The following input variables are required: ### [policies](#input\_policies) -Description: List of policy configurations +Description: List of policies with their details Type: ```hcl list(object({ name = string - description = string - statements = string # Path to the JSON file containing policy statements - target_ids = list(string) # List of target account IDs or OU IDs + file = string + target_ids = list(string) + description = string # Ensure this is included })) ``` ## Optional Inputs -The following input variables are optional (have default values): - -### [tags](#input\_tags) - -Description: Tags to apply to all resources created in this module - -Type: `map(string)` - -Default: `{}` +No optional inputs. diff --git a/main.tf b/main.tf index 3ea6d7a..dee4464 100644 --- a/main.tf +++ b/main.tf @@ -1,40 +1,17 @@ -# Loop over each policy to create policy documents dynamically -data "aws_iam_policy_document" "scp_policies" { - for_each = { for p in var.policies : p.name => p } - - dynamic "statement" { - for_each = jsondecode(file(each.value.statements)) - content { - sid = lookup(statement.value, "Sid", null) - effect = lookup(statement.value, "Effect", "Deny") - actions = statement.value.Action - resources = [lookup(statement.value, "Resource", "*")] - - dynamic "condition" { - for_each = lookup(statement.value, "Condition", {}) - content { - test = condition.key - variable = condition.value[0] - values = condition.value[1] - } - } - } - } -} - -# Create policies with tags +# Create an AWS Organization policy for each policy template resource "aws_organizations_policy" "scp" { - for_each = data.aws_iam_policy_document.scp_policies - name = each.value.name + for_each = { for policy in var.policies : policy.name => policy } + + name = each.key description = each.value.description - content = each.value.json - tags = var.tags + content = templatefile(lookup(each.value, "file"), {}) } -# Attach policies to targets with tags resource "aws_organizations_policy_attachment" "attach_scp" { - for_each = { for p in var.policies : p.name => p } - count = length(each.value.target_ids) - policy_id = aws_organizations_policy.scp[each.key].id - target_id = each.value.target_ids[count.index] -} \ No newline at end of file + for_each = { + for policy in aws_organizations_policy.scp : + policy.name => policy + } + policy_id = each.value.id + target_id = flatten([for p in var.policies : p.target_ids if p.name == each.key])[0] +} diff --git a/variables.tf b/variables.tf index c111729..e8deaf3 100644 --- a/variables.tf +++ b/variables.tf @@ -1,15 +1,9 @@ variable "policies" { - description = "List of policy configurations" + description = "List of policies with their details" type = list(object({ name = string + file = string + target_ids = list(string) description = string - statements = string # Path to the JSON file containing policy statements - target_ids = list(string) # List of target account IDs or OU IDs })) } - -variable "tags" { - description = "Tags to apply to all resources created in this module" - type = map(string) - default = {} -} From 3c081ca4fbd988d94b2c76ba726b8b37a754df27 Mon Sep 17 00:00:00 2001 From: "waqas.yousaf" Date: Wed, 30 Oct 2024 10:32:21 +0100 Subject: [PATCH 3/3] OPS-6301: fixed readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7189ba2..9e33fbe 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ list(object({ name = string file = string target_ids = list(string) - description = string # Ensure this is included + description = string })) ```