Skip to content

Commit

Permalink
Breaking Changes: Migration from regional to global API identifiers (#4)
Browse files Browse the repository at this point in the history
* Migration from regional to global API identifiers

* Fix formatting and README

* Fix tflint error

* Fix README
  • Loading branch information
igirardi authored Dec 19, 2024
1 parent 6172564 commit 18af6da
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 64 deletions.
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fail_fast: False

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 # Get the latest from: https://github.com/pre-commit/pre-commit-hooks/releases
rev: v5.0.0 # Get the latest from: https://github.com/pre-commit/pre-commit-hooks/releases
hooks:
- id: check-added-large-files
- id: check-case-conflict
Expand Down Expand Up @@ -39,12 +39,12 @@ repos:
stages: [commit-msg]

- repo: https://github.com/asottile/add-trailing-comma
rev: v2.5.1 # Get the latest from: https://github.com/asottile/add-trailing-comma
rev: v3.1.0 # Get the latest from: https://github.com/asottile/add-trailing-comma
hooks:
- id: add-trailing-comma

- repo: https://github.com/gruntwork-io/pre-commit
rev: v0.1.22 # Get the latest from: https://github.com/gruntwork-io/pre-commit/releases
rev: v0.1.24 # Get the latest from: https://github.com/gruntwork-io/pre-commit/releases
hooks:
- id: terraform-validate
- id: terraform-fmt
Expand All @@ -53,7 +53,7 @@ repos:
- id: golint

- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.81.0 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases
rev: v1.96.2 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases
hooks:
- id: terraform_fmt
- id: terraform_tflint
Expand All @@ -66,7 +66,7 @@ repos:
- id: terraform_checkov

- repo: https://github.com/ambv/black
rev: 23.3.0 # Get the latest from: https://github.com/psf/black/releases
rev: 24.10.0 # Get the latest from: https://github.com/psf/black/releases
hooks:
- id: black
language_version: python3
Expand Down
2 changes: 1 addition & 1 deletion .tflint.hcl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugin "aws" {
enabled = true
version = "0.20.0"
version = "0.36.0"
source = "github.com/terraform-linters/tflint-ruleset-aws"
deep_check = false
}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2024-12-19
### Breaking Changes
- Migration from regional to global API identifiers

## [1.0.0] - 2023-06-22
### Added
- Initial public release
51 changes: 28 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

- [AWS Prescriptive Guidance](#aws-prescriptive-guidance)
- [Goal](#goal)
- [Prerequisites and limitations](#prerequisites-and-limitations)
- [Prerequisites and Limitations](#prerequisites-and-limitations)
- [Architecture](#architecture)
- [Tools](#tools)
- [Best practices](#best-practices)
Expand All @@ -24,24 +24,24 @@ For a complete guide, prerequisites and instructions for using this AWS Prescrip

## Goal

This pattern describes how to use AWS Control Tower controls, HashiCorp Terraform, and infrastructure as code (IaC) to implement and administer preventive, detective, and proactive security controls. A control (also known as a guardrail) is a high-level rule that provides ongoing governance for your overall AWS Control Tower environment. For example, you can use controls to require logging for your AWS accounts and then configure automatic notifications if specific security-related events occur.
This pattern describes how to use AWS Control Tower controls, HashiCorp Terraform, and infrastructure as code (IaC) to implement and administer preventive, detective, and proactive security controls. A [control](https://docs.aws.amazon.com/controltower/latest/userguide/controls.html) (also known as a guardrail) is a high-level rule that provides ongoing governance for your overall AWS Control Tower environment. For example, you can use controls to require logging for your AWS accounts and then configure automatic notifications if specific security-related events occur.

AWS Control Tower helps you implement preventive, detective, and proactive controls that govern your AWS resources and monitor compliance across multiple AWS accounts. Each control enforces a single rule. In this pattern, you use a provided IaC template to specify which controls you want to deploy in your environment.

AWS Control Tower controls apply to an entire organizational unit (OU), and the control affects every AWS account within the OU. Therefore, when users perform any action in any account in your landing zone, the action is subject to the controls that govern the OU.
AWS Control Tower controls apply to an entire [organizational unit (OU)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_getting-started_concepts.html#organizationalunit), and the control affects every AWS account within the OU. Therefore, when users perform any action in any account in your landing zone, the action is subject to the controls that govern the OU.

Implementing AWS Control Tower controls helps establish a strong security foundation for your AWS landing zone. By using this pattern to deploy the controls as IaC through Terraform, you can standardize the controls in your landing zone and more efficiently deploy and manage them.

### Target Audience
### Intended Audience

This pattern is recommended for users who have experience with AWS Control Tower, Terraform, and AWS Organizations.


## Prerequisites and limitations
## Prerequisites and Limitations

### Prerequisites

- Active AWS accounts managed as an organization in AWS Organizations and an AWS Control Tower landing zone. For instructions, see [Create an account structure](https://www.wellarchitectedlabs.com/cost/100_labs/100_1_aws_account_setup/2_account_structure/) (AWS Well-Architected Labs).
- Active AWS accounts managed as an organization in AWS Organizations and an AWS Control Tower landing zone. For instructions, see [Getting started](https://docs.aws.amazon.com/controltower/latest/userguide/getting-started-with-control-tower.html) in the AWS Control Tower documentation.

- AWS Command Line Interface (AWS CLI), [installed](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) and [configured](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).

Expand All @@ -57,6 +57,14 @@ This pattern is recommended for users who have experience with AWS Control Tower

- Terraform backend, [configured](https://developer.hashicorp.com/terraform/language/settings/backends/configuration#using-a-backend-block) (Terraform documentation).

### Limitations

- For AWS Control Tower controls, this pattern requires the use of [global identifiers](https://docs.aws.amazon.com/controltower/latest/controlreference/all-global-identifiers.html) that are in the following format:
`arn:<PARTITION>:controlcatalog:::control/<CONTROL_CATALOG_OPAQUE_ID>`
**Note:** In most cases, the value for `<PARTITION>` is aws.
Previous versions of this pattern used [regional identifiers](https://docs.aws.amazon.com/controltower/latest/controlreference/control-metadata-tables.html) that are no longer supported. We recommend that you migrate from regional identifiers to global identifiers. Global identifiers help you manage controls and expand the number of controls you can use.


## Architecture

This section provides a high-level overview of this solution and the architecture established by the sample code. The following diagram shows controls deployed across the various accounts in the OU.
Expand All @@ -67,7 +75,7 @@ AWS Control Tower controls are categorized according to their behavior and their

There are three primary types of control behaviors:

1. Preventive controls are designed to prevent actions from occurring. These are implemented with [service control policies (SCPs)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) in AWS Organizations. The status of a preventive control is either enforced or not enabled. Preventive controls are supported in all AWS Regions.
1. Preventive controls are designed to prevent actions from occurring. These are implemented with [service control policies (SCPs)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) or [resource control policies (RCPs)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_rcps.html) in AWS Organizations. The status of a preventive control is either enforced or not enabled. Preventive controls are supported in all AWS Regions.

2. Detective controls are designed to detect specific events when they occur and log the action in CloudTrail. These are implemented with AWS [Config rules](https://docs.aws.amazon.com/config/latest/developerguide/evaluate-config.html). The status of a detective control is either clear, in violation, or not enabled. Detective controls apply only in those AWS Regions supported by AWS Control Tower.

Expand Down Expand Up @@ -113,23 +121,23 @@ To deploy this solution, you need

| Name | Version |
|------|---------|
| <a name="requirement_ct"></a> [AWS Control Tower](https://aws.amazon.com/controltower/) | >= 3.0 |
| <a name="requirement_ct"></a> [AWS Control Tower](https://aws.amazon.com/controltower/) | >= 3.2 |

and the following requirements.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.5 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 4.67 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | > 1.5 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | > 4.67 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 4.67.0 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.81.0 |

## Modules

Expand All @@ -146,7 +154,6 @@ No modules.
| [aws_organizations_organizational_units.ous_depth_3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organizational_units) | data source |
| [aws_organizations_organizational_units.ous_depth_4](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organizational_units) | data source |
| [aws_organizations_organizational_units.root](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organizational_units) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |

## Inputs

Expand All @@ -159,7 +166,7 @@ No modules.
| Name | Description |
|------|-------------|
| <a name="output_ous_id_to_arn_map"></a> [ous\_id\_to\_arn\_map](#output\_ous\_id\_to\_arn\_map) | Map from OU id to OU arn for the whole organization |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
<!-- END_TF_DOCS -->


## Controls Configuration File
Expand All @@ -169,32 +176,30 @@ The following is an example of an updated `variables.tfvars` file.
controls = [
{
control_names = [
"AWS-GR_ENCRYPTED_VOLUMES",
"503uicglhjkokaajywfpt6ros",
...
],
organizational_unit_ids = ["ou-1111-11111111", "ou-2222-22222222"...],
},
{
control_names = [
"AWS-GR_SUBNET_AUTO_ASSIGN_PUBLIC_IP_DISABLED",
"50z1ot237wl8u1lv5ufau6qqo",
...
],
organizational_unit_ids = ["ou-1111-11111111"...],
},
]
```

1. In the `controls` section, in the `control_names` parameter, enter the control API identifier. Each control has a unique API identifier for each Region in which AWS Control Tower is available. To find the control identifier, do the following:

1. In [Tables of control metadata](https://docs.aws.amazon.com/controltower/latest/userguide/control-metadata-tables.html), locate the control you want to enable.
1. Open [All global identifiers](https://docs.aws.amazon.com/controltower/latest/controlreference/all-global-identifiers.html) in the AWS Control Tower documentation.

2. In the Control API identifiers, by Region column, locate the API identifier for the Region in which you are making the API call, such as `arn:aws:controltower:us-east-1::control/AWS-GR_AUDIT_BUCKET_ENCRYPTION_ENABLED`.
2. In the JSON-formatted list, locate the control that you want to implement, and then copy its global identifier (also known as the `{CONTROL_CATALOG_OPAQUE_ID}` value). For example, the global identifier for the `AWS-GR_AUDIT_BUCKET_ENCRYPTION_ENABLED` control is `k4izcjxhukijhajp6ks5mjxk`.

3. Extract the control identifier from the Regional identifier, such as `GR_AUDIT_BUCKET_ENCRYPTION_ENABLED`.
3. In the `controls` section, in the `control_names` parameter, enter the global identifier that you copied.

2. In the `controls` section, in the `organizational_unit_ids` parameter, enter the ID of the organizational unit where you want to enable the control, such as `ou-1111-11111111`. Enter the ID in double quotation marks, and separate multiple IDs with commas. For more information about how to retrieve OU IDs, see Viewing the [details of an OU](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_org_details.html#orgs_view_ou).
4. In the `controls` section, in the `organizational_unit_ids` parameter, enter the ID of the organizational unit where you want to enable the control, such as `ou-1111-11111111`. Enter the ID in double quotation marks, and separate multiple IDs with commas. For more information about how to retrieve OU IDs, see Viewing the [details of an OU](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_org_details.html#orgs_view_ou).

3. Save and close the variables.tfvars file. For an example of an updated variables.tfvars file, see the [Additional information](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-and-manage-aws-control-tower-controls-by-using-terraform.html#deploy-and-manage-aws-control-tower-controls-by-using-terraform-additional) section of this pattern.
5. Save and close the variables.tfvars file. For an example of an updated variables.tfvars file, see the [Additional information](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-and-manage-aws-control-tower-controls-by-using-terraform.html#deploy-and-manage-aws-control-tower-controls-by-using-terraform-additional) section of this pattern.


## Deployment
Expand Down
Binary file modified img/ctc-architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 1 addition & 3 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

# AWS Control Tower Controls (sometimes called Guardrails) Terraform Module

data "aws_region" "current" {}

data "aws_organizations_organization" "organization" {}

# Get OUs data resources up to five levels of OUs deep under a root (maximum nesting quota limit)
Expand Down Expand Up @@ -64,7 +62,7 @@ locals {
guardrails_list = flatten([
for i in range(0, length(var.controls)) : [
for pair in setproduct(element(var.controls, i).control_names, element(var.controls, i).organizational_unit_ids) :
{ "arn:aws:controltower:${data.aws_region.current.name}::control/${pair[0]}" = pair[1] }
{ "arn:aws:controlcatalog:::control/${pair[0]}" = pair[1] }
]
])

Expand Down
60 changes: 30 additions & 30 deletions variables.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,41 @@
controls = [
{
control_names = [
"AWS-GR_ENCRYPTED_VOLUMES",
"AWS-GR_EBS_OPTIMIZED_INSTANCE",
"AWS-GR_EC2_VOLUME_INUSE_CHECK",
"AWS-GR_RDS_INSTANCE_PUBLIC_ACCESS_CHECK",
"AWS-GR_RDS_SNAPSHOTS_PUBLIC_PROHIBITED",
"AWS-GR_RDS_STORAGE_ENCRYPTED",
"AWS-GR_RESTRICTED_COMMON_PORTS",
"AWS-GR_RESTRICTED_SSH",
"AWS-GR_RESTRICT_ROOT_USER",
"AWS-GR_RESTRICT_ROOT_USER_ACCESS_KEYS",
"AWS-GR_ROOT_ACCOUNT_MFA_ENABLED",
"AWS-GR_S3_BUCKET_PUBLIC_READ_PROHIBITED",
"AWS-GR_S3_BUCKET_PUBLIC_WRITE_PROHIBITED",
"503uicglhjkokaajywfpt6ros", # AWS-GR_ENCRYPTED_VOLUMES
"2j9gjxqfo040xtx8kd1jf4ni6", # AWS-GR_EBS_OPTIMIZED_INSTANCE
"8c3i4catfgmyy1e19476v06rr", # AWS-GR_EC2_VOLUME_INUSE_CHECK
"4jc77cq1lcr7g64xywwypykv8", # AWS-GR_RDS_INSTANCE_PUBLIC_ACCESS_CHECK
"1h4eyqyyonp19dlrreqf1i3w0", # AWS-GR_RDS_SNAPSHOTS_PUBLIC_PROHIBITED
"e34kieahgkm0lggs5g0s412jt", # AWS-GR_RDS_STORAGE_ENCRYPTED
"df2ta5ytg2zatj1q7y5e09u32", # AWS-GR_RESTRICTED_COMMON_PORTS
"6rilu41n0gb9w6mxrkyewoer4", # AWS-GR_RESTRICTED_SSH
"5kvme4m5d2b4d7if2fs5yg2ui", # AWS-GR_RESTRICT_ROOT_USER
"8ui9y3oace2513xarz8aqojl7", # AWS-GR_RESTRICT_ROOT_USER_ACCESS_KEYS
"24izmu4k16gv9tvd7sexnyrfy", # AWS-GR_ROOT_ACCOUNT_MFA_ENABLED
"8sw3pbid15t9cbww8d2w2qwgf", # AWS-GR_S3_BUCKET_PUBLIC_READ_PROHIBITED
"9j9nwxj789d82sypnukhyyowy", # AWS-GR_S3_BUCKET_PUBLIC_WRITE_PROHIBITED
],
organizational_unit_ids = ["ou-1111-11111111", "ou-2222-22222222"],
},
{
control_names = [
"AWS-GR_SUBNET_AUTO_ASSIGN_PUBLIC_IP_DISABLED",
"AWS-GR_AUTOSCALING_LAUNCH_CONFIG_PUBLIC_IP_DISABLED",
"AWS-GR_DISALLOW_CROSS_REGION_NETWORKING",
"AWS-GR_DISALLOW_VPC_INTERNET_ACCESS",
"AWS-GR_DISALLOW_VPN_CONNECTIONS",
"AWS-GR_DMS_REPLICATION_NOT_PUBLIC",
"AWS-GR_EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK",
"AWS-GR_EC2_INSTANCE_NO_PUBLIC_IP",
"AWS-GR_EKS_ENDPOINT_NO_PUBLIC_ACCESS",
"AWS-GR_ELASTICSEARCH_IN_VPC_ONLY",
"AWS-GR_EMR_MASTER_NO_PUBLIC_IP",
"AWS-GR_LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED",
"AWS-GR_NO_UNRESTRICTED_ROUTE_TO_IGW",
"AWS-GR_REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK",
"AWS-GR_S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS_PERIODIC",
"AWS-GR_SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS",
"AWS-GR_SSM_DOCUMENT_NOT_PUBLIC",
"50z1ot237wl8u1lv5ufau6qqo", # AWS-GR_SUBNET_AUTO_ASSIGN_PUBLIC_IP_DISABLED
"aemn4s3hxv9erree434pvjboi", # AWS-GR_AUTOSCALING_LAUNCH_CONFIG_PUBLIC_IP_DISABLED
"dvuaav61i5cnfazfelmvn9m6k", # AWS-GR_DISALLOW_CROSS_REGION_NETWORKING
"41ngl8m5c4eb1myoz0t707n7h", # AWS-GR_DISALLOW_VPC_INTERNET_ACCESS
"5rlqt6yj6u0v0gb62pqdy4ae", # AWS-GR_DISALLOW_VPN_CONNECTIONS
"dekrrxbiux86m6jdowdsbamze", # AWS-GR_DMS_REPLICATION_NOT_PUBLIC
"87qo8rsoettjrxjevmjqcw1tu", # AWS-GR_EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK
"4v7xtm83uvvyulk1wwpm4qm3s", # AWS-GR_EC2_INSTANCE_NO_PUBLIC_IP
"aeellyghb27pbehyzua1nyena", # AWS-GR_EKS_ENDPOINT_NO_PUBLIC_ACCESS
"2civrte1w8tqff4vbtzdl4abq", # AWS-GR_ELASTICSEARCH_IN_VPC_ONLY
"5cnql6so7p7bs0khdjodjr9e2", # AWS-GR_EMR_MASTER_NO_PUBLIC_IP
"b2gzofz99eb7nsuj5g8wcimse", # AWS-GR_LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED
"b8pjfqosgkgknznstduvel4rh", # AWS-GR_NO_UNRESTRICTED_ROUTE_TO_IGW
"1oxkwnc4hwhi2ndv6ekwy7np7", # AWS-GR_REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK
"6wmutsohbkwhfw6sf7cbt5e81", # AWS-GR_S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS_PERIODIC
"66gfl06uj1v999z53szvu0exa", # AWS-GR_SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS
"dfanrd8y5p7oj8fjyugqnakfr", # AWS-GR_SSM_DOCUMENT_NOT_PUBLIC
],
organizational_unit_ids = ["ou-1111-11111111"],
},
Expand Down
4 changes: 2 additions & 2 deletions versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
# Terraform and Providers Required Versions

terraform {
required_version = "~> 1.5"
required_version = "> 1.5"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.67"
version = "> 4.67"
}
}
}

0 comments on commit 18af6da

Please sign in to comment.