# scp

Creates one Service Control Policy and optionally attaches it to a target — matching `aws_organizations_policy` and `aws_organizations_policy_attachment`.

## Usage

```hcl
locals {
  scp_deny_root_access = {
    Version = "2012-10-17"
    Statement = [
      {
        Sid      = "DenyRootUser"
        Effect   = "Deny"
        Action   = ["*"]
        Resource = "*"
        Condition = {
          StringLike = {
            "aws:PrincipalArn" = "arn:aws:iam::*:root"
          }
        }
      }
    ]
  }
}

module "scp_deny_root" {
  source = "../hcascm-iac-modules-5b6f30c6dce3/modules/scp"

  service_control_policy = {
    name            = "DenyRootUserAccess"
    description     = "Deny root user access in member accounts"
    policy_document = local.scp_deny_root_access
  }

  target_id = "ou-xxxx"
}
```

Use one module block per SCP. Set `target_id = null` to create the policy without attaching.

## Import

Use import when an SCP **already exists in AWS** (console, CLI, or pre-Terraform) and you want Terraform to manage it without recreating `p-xxxx`. For new policies, use [Usage](#usage) and `terraform apply` — no import needed.

The import command only needs the policy id, but `aws_organizations_policy` requires `content` in configuration ([provider docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_policy)). Declare **what** is being adopted (policy document) together with **which** policy (`p-xxxx`) and **where** it is attached (`target_id`, if any).

### What to gather from AWS

```bash
# List SCPs
aws organizations list-policies --filter SERVICE_CONTROL_POLICY \
  --query 'Policies[].{Name:Name,Id:Id}' --output table

# Name, description, and policy JSON (convert Content to HCL policy_document)
aws organizations describe-policy --policy-id p-xxxxxxxx \
  --query 'Policy.{Name:PolicySummary.Name,Description:PolicySummary.Description,Content:Content}'

# Attachment target (skip if unattached)
aws organizations list-targets-for-policy --policy-id p-xxxxxxxx \
  --query 'Targets[].{TargetId:TargetId,Type:Type}' --output table
```


| Value                                    | Maps to module input                              |
| ---------------------------------------- | ------------------------------------------------- |
| Policy name                              | `service_control_policy.name`                     |
| Description                              | `service_control_policy.description`              |
| Content JSON                             | `service_control_policy.policy_document` (as HCL) |
| Policy id `p-xxxx`                       | Policy `import` id                                |
| Target `ou-xxxx` / `r-xxxx` / account id | `target_id` + attachment `import` id              |


### Step 1 — Write module config matching AWS

Config must match the existing policy **before** import. Resource addresses use `count`, so `[0]` is required.

```hcl
locals {
  scp_deny_root_access = {
    Version = "2012-10-17"
    Statement = [
      {
        Sid      = "DenyRootUser"
        Effect   = "Deny"
        Action   = ["*"]
        Resource = "*"
        Condition = {
          StringLike = {
            "aws:PrincipalArn" = "arn:aws:iam::*:root"
          }
        }
      }
    ]
  }
}

module "scp_deny_root" {
  source = "../hcascm-iac-modules-5b6f30c6dce3/modules/scp"

  service_control_policy = {
    name            = "DenyRootUserAccess"
    description     = "Deny root user access"
    policy_document = local.scp_deny_root_access
  }

  target_id = "ou-xxxxxxxx"   # null if not attached
}
```

### Step 2 — Import into state

Always import the **policy** first. When the SCP is attached in AWS, import the **attachment** in the same `plan` / `apply` (do not import attachment alone).

```hcl
import {
  to = module.scp_deny_root.aws_organizations_policy.this[0]
  id = "p-xxxxxxxx"
}

# Only when attached in AWS — id format is target_id:policy_id (not p-xxxx:target_id)
import {
  to = module.scp_deny_root.aws_organizations_policy_attachment.this[0]
  id = "ou-xxxxxxxx:p-xxxxxxxx"
}
```

Examples for other targets: `r-xxxx:p-xxxxxxxx` (root), `123456789012:p-xxxxxxxx` (account).

### Step 3 — Plan, apply, verify

```bash
terraform plan    # expect import (+ tag updates only)
terraform apply
terraform plan    # expect no changes
```

Remove `import` blocks after a successful apply. Keep the module block.

### Policy-only vs attached


| AWS state           | `target_id`                        | Import                                            |
| ------------------- | ---------------------------------- | ------------------------------------------------- |
| Policy only         | `null`                             | Policy `import` only                              |
| Policy + attachment | `ou-xxxx`, `r-xxxx`, or account id | Policy + attachment `import` (`target_id:p-xxxx`) |


### Troubleshooting


| Plan shows | Fix |
| ---------- | --- |
| Policy content change | `policy_document` does not match AWS — align with `describe-policy` Content |
| Replace policy | `name` or `description` mismatch |
| Create attachment | Missing attachment import or wrong `target_id` |
| `ListTargetsForPolicy` / pattern error on import | Attachment import id order wrong — use `target_id:p-xxxx`, not `p-xxxx:target_id` |
| Create new policy during attachment import | Policy not imported — uncomment/add policy `import` block |
| Tag-only changes | Accept apply or add matching `tags` |

<!-- BEGIN_TF_DOCS — DO NOT EDIT BELOW THIS LINE -->
## Requirements
| --------- | --------- |
| terraform | >= 1.5 |
| aws       | ~> 5.0   |


## Inputs


| Name                   | Description                                     | Type          | Default | Required |
| ---------------------- | ----------------------------------------------- | ------------- | ------- | -------- |
| service_control_policy | Single SCP to create                            | `object(...)` | `null`  | no       |
| target_id              | Attachment target (r-xxxx, ou-xxxx, account id) | `string`      | `null`  | no       |
| tags                   | Tags for the policy                             | `map(string)` | `{}`    | no       |


## Outputs


| Name                 | Description                 |
| -------------------- | --------------------------- |
| policy_id            | SCP policy ID               |
| policy_arn           | SCP policy ARN              |
| policy_name          | SCP policy name             |
| attachment_target_id | Attached target ID, or null |
<!-- END_TF_DOCS — DO NOT EDIT ABOVE THIS LINE -->
