├── stacks ├── __init__.py └── aws_control_tower_guardrails_stack.py ├── helpers ├── requirements.txt ├── .pylintrc ├── .flake8 ├── .isort.cfg ├── requirements-dev.txt ├── .mypy.ini ├── run_tests.sh ├── README.md └── controlcatalog_list_controls.py ├── .pylintrc ├── NOTICE ├── package.json ├── requirements ├── requirements.in ├── requirements-dev.in ├── requirements.txt └── requirements-dev.txt ├── .flake8 ├── .isort.cfg ├── img └── ctc-architecture.png ├── .mypy.ini ├── CODE_OF_CONDUCT.md ├── CHANGELOG.md ├── source.bat ├── .gitignore ├── LICENSE ├── scripts ├── run_cfn_nag.sh ├── install_deps.sh ├── update_deps.sh └── run_tests.sh ├── cdk.json ├── app.py ├── CONTRIBUTING.md ├── constants.py └── README.md /stacks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /helpers/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | pandas -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | disable = C0111, C0301 3 | -------------------------------------------------------------------------------- /helpers/.pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | disable = C0111 3 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "aws-cdk": "^2.84.0" 4 | } 5 | } -------------------------------------------------------------------------------- /requirements/requirements.in: -------------------------------------------------------------------------------- 1 | aws-cdk-lib 2 | constructs 3 | urllib3<2 4 | boto3 5 | cdk-nag -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | select = C,E,F,W,B,B950 4 | extend-ignore = E203, E501, W503 5 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile = black 3 | force_single_line = True 4 | single_line_exclusions = typing 5 | -------------------------------------------------------------------------------- /helpers/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | select = C,E,F,W,B,B950 4 | extend-ignore = E203, E501 5 | -------------------------------------------------------------------------------- /helpers/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile = black 3 | force_single_line = True 4 | single_line_exclusions = typing 5 | -------------------------------------------------------------------------------- /img/ctc-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-control-tower-controls-cdk/HEAD/img/ctc-architecture.png -------------------------------------------------------------------------------- /helpers/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | pandas 3 | isort 4 | flake8 5 | bandit 6 | black 7 | mypy 8 | pandas-stubs 9 | pylint 10 | radon -------------------------------------------------------------------------------- /.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | strict = True 3 | 4 | [mypy-aws_cdk.*] 5 | ignore_missing_imports = True 6 | 7 | [mypy-boto3.*] 8 | ignore_missing_imports = True 9 | -------------------------------------------------------------------------------- /helpers/.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | strict = True 3 | 4 | [mypy-aws_cdk.*] 5 | ignore_missing_imports = True 6 | 7 | [mypy-boto3.*] 8 | ignore_missing_imports = True 9 | -------------------------------------------------------------------------------- /requirements/requirements-dev.in: -------------------------------------------------------------------------------- 1 | -c requirements.in 2 | 3 | bandit 4 | black 5 | coverage 6 | flake8 7 | isort 8 | mypy 9 | pylint 10 | radon 11 | safety 12 | xenon -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [2.0.0] - 2024-12-19 8 | ### Breaking Changes 9 | - Migration from regional to global API identifiers 10 | 11 | ## [1.0.0] - 2023-06-22 12 | ### Added 13 | - Initial public release 14 | -------------------------------------------------------------------------------- /source.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem The sole purpose of this script is to make the command 4 | rem 5 | rem source .venv/bin/activate 6 | rem 7 | rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows. 8 | rem On Windows, this command just runs this batch file (the argument is ignored). 9 | rem 10 | rem Now we don't need to document a Windows command for activating a virtualenv. 11 | 12 | echo Executing .venv\Scripts\activate.bat for you 13 | .venv\Scripts\activate.bat 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | .pytest_cache 4 | *.egg-info 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # Environments 12 | .env 13 | .venv 14 | env/ 15 | venv/ 16 | ENV/ 17 | env.bak/ 18 | venv.bak/ 19 | 20 | # CDK Context & Staging files 21 | .cdk.staging/ 22 | cdk.out/ 23 | 24 | # Byte-compiled / optimized / DLL 25 | __pycache__/ 26 | *.py[cod] 27 | *$py.class 28 | 29 | # C extensions 30 | *.so 31 | 32 | # Coverage.py 33 | .coverage 34 | coverage.xml 35 | htmlcov/ 36 | 37 | # Environment 38 | .venv/ 39 | node_modules/ 40 | 41 | # macOS 42 | .DS_Store 43 | 44 | # mypy 45 | .mypy_cache/ 46 | 47 | # PyCharm 48 | .idea/ 49 | 50 | # pyenv 51 | .python-version -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /scripts/run_cfn_nag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the "Software"), to deal in 8 | # the Software without restriction, including without limitation the rights to 9 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | # the Software, and to permit persons to whom the Software is furnished to do so. 11 | 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 14 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 15 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 16 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | 19 | npx cdk synth 20 | 21 | cfn_nag_scan -i ./cdk.out/ -t '.+\.template\.json' 22 | -------------------------------------------------------------------------------- /scripts/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the "Software"), to deal in 8 | # the Software without restriction, including without limitation the rights to 9 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | # the Software, and to permit persons to whom the Software is furnished to do so. 11 | 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 14 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 15 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 16 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | 19 | set -o errexit 20 | set -o verbose 21 | 22 | # Install AWS CDK Toolkit locally 23 | npm install 24 | 25 | # Install project dependencies 26 | pip install -r requirements/requirements.txt -r requirements/requirements-dev.txt 27 | -------------------------------------------------------------------------------- /scripts/update_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the "Software"), to deal in 8 | # the Software without restriction, including without limitation the rights to 9 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | # the Software, and to permit persons to whom the Software is furnished to do so. 11 | 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 14 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 15 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 16 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | 19 | set -o errexit 20 | set -o verbose 21 | 22 | # Update project dependencies 23 | npm update 24 | pip install pip-tools 25 | pip-compile --upgrade requirements/requirements.in 26 | pip-compile --upgrade requirements/requirements-dev.in 27 | -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "source.bat", 12 | "**/__init__.py", 13 | "python/__pycache__", 14 | "tests" 15 | ] 16 | }, 17 | "context": { 18 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 19 | "@aws-cdk/core:stackRelativeExports": true, 20 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 21 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 22 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 23 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 24 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 25 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 26 | "@aws-cdk/core:checkSecretUsage": true, 27 | "@aws-cdk/aws-iam:minimizePolicies": true, 28 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 29 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 30 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 31 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 32 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 33 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 34 | "@aws-cdk/core:enablePartitionLiterals": true, 35 | "@aws-cdk/core:target-partitions": [ 36 | "aws", 37 | "aws-cn" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | import aws_cdk 18 | import cdk_nag 19 | 20 | from constants import ACCOUNT_ID 21 | from constants import AWS_CONTROL_TOWER_REGION 22 | from stacks.aws_control_tower_guardrails_stack import AwsControlTowerGuardrailsStack 23 | 24 | env = aws_cdk.Environment(region=AWS_CONTROL_TOWER_REGION, account=ACCOUNT_ID) 25 | 26 | app = aws_cdk.App() 27 | AwsControlTowerGuardrailsStack(app, "aws-control-tower-guardrails", env=env) 28 | aws_cdk.Aspects.of(app).add(cdk_nag.AwsSolutionsChecks()) 29 | 30 | app.synth() 31 | -------------------------------------------------------------------------------- /helpers/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the "Software"), to deal in 8 | # the Software without restriction, including without limitation the rights to 9 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | # the Software, and to permit persons to whom the Software is furnished to do so. 11 | 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 14 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 15 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 16 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | 19 | set -o errexit 20 | set -o verbose 21 | 22 | targets=(controlcatalog_list_controls.py) 23 | 24 | # Find common security issues (https://bandit.readthedocs.io) 25 | bandit --recursive "${targets[@]}" 26 | 27 | # Python code formatter (https://black.readthedocs.io) 28 | black --check --diff "${targets[@]}" 29 | 30 | # Style guide enforcement (https://flake8.pycqa.org) 31 | flake8 --config .flake8 "${targets[@]}" 32 | 33 | # Sort imports (https://pycqa.github.io/isort) 34 | isort --settings-path .isort.cfg --check --diff "${targets[@]}" 35 | 36 | # Static type checker (https://mypy.readthedocs.io) 37 | mypy --config-file .mypy.ini "${targets[@]}" 38 | 39 | # Check for errors, enforce a coding standard, look for code smells (http://pylint.pycqa.org) 40 | pylint --rcfile .pylintrc "${targets[@]}" 41 | 42 | # Report code complexity (https://radon.readthedocs.io) 43 | radon mi "${targets[@]}" 44 | -------------------------------------------------------------------------------- /helpers/README.md: -------------------------------------------------------------------------------- 1 | # Extract Controls Using Control Catalog API 2 | 3 | [The Control Catalog](https://docs.aws.amazon.com/controlcatalog/latest/userguide/what-is-controlcatalog.html) is a part of AWS Control Tower, which lists controls for several AWS services. It is a consolidated catalog of AWS controls. AWS Control Catalog is available through the console and through the AWS Control Catalog application programming interface (API). 4 | For more information, see the [AWS Control Catalog API Reference](https://docs.aws.amazon.com/controlcatalog/latest/APIReference/Welcome.html). 5 | 6 | The provide helper Python script allows to export all controls using the AWS Control Catalog API to a csv file for a compherensive view of all available controls. 7 | 8 | To use the helper script: 9 | 10 | 1. Assume the IAM role that has permissions to use the AWS control Catalog API. For more information about assuming an IAM role in the AWS CLI, see [Use an IAM role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html). 11 | 12 | 2. If you are using Linux or MacOS: 13 | 1. Enter the following command to create a virtual environment. 14 | ``` 15 | $ python3 -m venv .venv 16 | ``` 17 | 2. After the virtual environment is created, enter the following command to activate it. 18 | ``` 19 | $ source .venv/bin/activate 20 | ``` 21 | 3. If you are using Windows: 22 | 1. Enter the following command to activate a virtual environment. 23 | ``` 24 | % .venv\Scripts\activate.bat 25 | ``` 26 | 27 | 4. After the virtual environment is activated, enter the following command to install the dependencies. 28 | ``` 29 | $ pip install -r helpers/requirements.txt 30 | ``` 31 | 32 | 5. From the helpers folder execute the scrip to export AWS controls to a csv file. 33 | ``` 34 | $ python3 controlcatalog_list_controls.py 35 | ``` 36 | An example of exported controls is provided in the helpers folder [here](controlcatalog_list_controls.csv). 37 | -------------------------------------------------------------------------------- /requirements/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.13 3 | # by the following command: 4 | # 5 | # pip-compile requirements/requirements.in 6 | # 7 | attrs==25.3.0 8 | # via 9 | # cattrs 10 | # jsii 11 | aws-cdk-asset-awscli-v1==2.2.231 12 | # via aws-cdk-lib 13 | aws-cdk-asset-node-proxy-agent-v6==2.1.0 14 | # via aws-cdk-lib 15 | aws-cdk-cloud-assembly-schema==41.2.0 16 | # via aws-cdk-lib 17 | aws-cdk-lib==2.189.0 18 | # via 19 | # -r requirements/requirements.in 20 | # cdk-nag 21 | boto3==1.37.32 22 | # via -r requirements/requirements.in 23 | botocore==1.37.32 24 | # via 25 | # boto3 26 | # s3transfer 27 | cattrs==24.1.3 28 | # via jsii 29 | cdk-nag==2.35.68 30 | # via -r requirements/requirements.in 31 | constructs==10.4.2 32 | # via 33 | # -r requirements/requirements.in 34 | # aws-cdk-lib 35 | # cdk-nag 36 | importlib-resources==6.5.2 37 | # via jsii 38 | jmespath==1.0.1 39 | # via 40 | # boto3 41 | # botocore 42 | jsii==1.111.0 43 | # via 44 | # aws-cdk-asset-awscli-v1 45 | # aws-cdk-asset-node-proxy-agent-v6 46 | # aws-cdk-cloud-assembly-schema 47 | # aws-cdk-lib 48 | # cdk-nag 49 | # constructs 50 | publication==0.0.3 51 | # via 52 | # aws-cdk-asset-awscli-v1 53 | # aws-cdk-asset-node-proxy-agent-v6 54 | # aws-cdk-cloud-assembly-schema 55 | # aws-cdk-lib 56 | # cdk-nag 57 | # constructs 58 | # jsii 59 | python-dateutil==2.9.0.post0 60 | # via 61 | # botocore 62 | # jsii 63 | s3transfer==0.11.4 64 | # via boto3 65 | six==1.17.0 66 | # via python-dateutil 67 | typeguard==2.13.3 68 | # via 69 | # aws-cdk-asset-awscli-v1 70 | # aws-cdk-asset-node-proxy-agent-v6 71 | # aws-cdk-cloud-assembly-schema 72 | # aws-cdk-lib 73 | # cdk-nag 74 | # constructs 75 | # jsii 76 | typing-extensions==4.13.2 77 | # via jsii 78 | urllib3==1.26.20 79 | # via 80 | # -r requirements/requirements.in 81 | # botocore 82 | -------------------------------------------------------------------------------- /scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the "Software"), to deal in 8 | # the Software without restriction, including without limitation the rights to 9 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | # the Software, and to permit persons to whom the Software is furnished to do so. 11 | 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 14 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 15 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 16 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | 19 | set -o errexit 20 | set -o verbose 21 | 22 | targets=(stacks app.py constants.py) 23 | 24 | # Find common security issues (https://bandit.readthedocs.io) 25 | bandit --recursive "${targets[@]}" 26 | 27 | # Python code formatter (https://black.readthedocs.io) 28 | black --check --diff "${targets[@]}" 29 | 30 | # Style guide enforcement (https://flake8.pycqa.org) 31 | flake8 --config .flake8 "${targets[@]}" 32 | 33 | # Sort imports (https://pycqa.github.io/isort) 34 | isort --settings-path .isort.cfg --check --diff "${targets[@]}" 35 | 36 | # Static type checker (https://mypy.readthedocs.io) 37 | mypy --config-file .mypy.ini "${targets[@]}" 38 | 39 | # Check for errors, enforce a coding standard, look for code smells (http://pylint.pycqa.org) 40 | pylint --rcfile .pylintrc "${targets[@]}" 41 | 42 | # Check dependencies for security issues (https://pyup.io/safety) 43 | safety scan \ 44 | -r requirements/requirements.txt \ 45 | -r requirements/requirements-dev.txt 46 | 47 | # Report code complexity (https://radon.readthedocs.io) 48 | radon mi "${targets[@]}" 49 | 50 | # Exit with non-zero status if code complexity exceeds thresholds (https://xenon.readthedocs.io) 51 | xenon --max-absolute C --max-modules A --max-average A "${targets[@]}" 52 | -------------------------------------------------------------------------------- /helpers/controlcatalog_list_controls.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | from typing import Any, Dict, List, Tuple, Union 18 | 19 | import boto3 20 | import pandas as pd 21 | 22 | 23 | def flatten_dict( 24 | d: Dict[str, Any], parent_key: str = "", sep: str = "_" 25 | ) -> Dict[str, Union[str, int, float, bool, None]]: 26 | """ 27 | Flatten a nested dictionary into a single level dictionary. 28 | 29 | Args: 30 | d: The nested dictionary to flatten 31 | parent_key: The parent key for nested values 32 | sep: Separator to use between nested keys 33 | 34 | Returns: 35 | A flattened dictionary with concatenated keys 36 | """ 37 | items: List[Tuple[str, Union[str, int, float, bool, None]]] = [] 38 | 39 | for k, v in d.items(): 40 | new_key: str = f"{parent_key}{sep}{k}" if parent_key else k 41 | 42 | if isinstance(v, dict): 43 | items.extend(flatten_dict(v, new_key, sep=sep).items()) 44 | else: 45 | items.append((new_key, v)) 46 | 47 | return dict(items) 48 | 49 | 50 | def get_control_tower_controls() -> pd.DataFrame: 51 | # Create AWS Control Catalog client 52 | client = boto3.client("controlcatalog") 53 | 54 | paginator = client.get_paginator("list_controls") 55 | 56 | flatten_controls = [] 57 | # Paginate through all controls 58 | for page in paginator.paginate(): 59 | for control in page["Controls"]: 60 | flat_data = flatten_dict(control) 61 | # Convert to DataFrame 62 | flatten_controls.append(flat_data) 63 | 64 | return pd.DataFrame(flatten_controls) 65 | 66 | 67 | if __name__ == "__main__": 68 | # Get all controls 69 | df = get_control_tower_controls() 70 | # Define the CSV file name 71 | CSV_FILE = "controlcatalog_list_controls.csv" 72 | 73 | # Export DataFrame to CSV 74 | df.to_csv(CSV_FILE, index=False) 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /requirements/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.13 3 | # by the following command: 4 | # 5 | # pip-compile requirements/requirements-dev.in 6 | # 7 | annotated-types==0.7.0 8 | # via pydantic 9 | astroid==3.3.9 10 | # via pylint 11 | authlib==1.5.2 12 | # via safety 13 | bandit==1.8.3 14 | # via -r requirements/requirements-dev.in 15 | black==25.1.0 16 | # via -r requirements/requirements-dev.in 17 | certifi==2025.1.31 18 | # via requests 19 | cffi==1.17.1 20 | # via cryptography 21 | charset-normalizer==3.4.1 22 | # via requests 23 | click==8.1.8 24 | # via 25 | # black 26 | # nltk 27 | # safety 28 | # typer 29 | colorama==0.4.6 30 | # via radon 31 | coverage==7.8.0 32 | # via -r requirements/requirements-dev.in 33 | cryptography==44.0.2 34 | # via authlib 35 | dill==0.3.9 36 | # via pylint 37 | dparse==0.6.4 38 | # via 39 | # safety 40 | # safety-schemas 41 | filelock==3.16.1 42 | # via safety 43 | flake8==7.2.0 44 | # via -r requirements/requirements-dev.in 45 | idna==3.10 46 | # via requests 47 | isort==6.0.1 48 | # via 49 | # -r requirements/requirements-dev.in 50 | # pylint 51 | jinja2==3.1.6 52 | # via safety 53 | joblib==1.4.2 54 | # via nltk 55 | mando==0.7.1 56 | # via radon 57 | markdown-it-py==3.0.0 58 | # via rich 59 | markupsafe==3.0.2 60 | # via jinja2 61 | marshmallow==3.26.1 62 | # via safety 63 | mccabe==0.7.0 64 | # via 65 | # flake8 66 | # pylint 67 | mdurl==0.1.2 68 | # via markdown-it-py 69 | mypy==1.15.0 70 | # via -r requirements/requirements-dev.in 71 | mypy-extensions==1.0.0 72 | # via 73 | # black 74 | # mypy 75 | nltk==3.9.1 76 | # via safety 77 | packaging==24.2 78 | # via 79 | # black 80 | # dparse 81 | # marshmallow 82 | # safety 83 | # safety-schemas 84 | pathspec==0.12.1 85 | # via black 86 | pbr==6.1.1 87 | # via stevedore 88 | platformdirs==4.3.7 89 | # via 90 | # black 91 | # pylint 92 | psutil==6.1.1 93 | # via safety 94 | pycodestyle==2.13.0 95 | # via flake8 96 | pycparser==2.22 97 | # via cffi 98 | pydantic==2.9.2 99 | # via 100 | # safety 101 | # safety-schemas 102 | pydantic-core==2.23.4 103 | # via pydantic 104 | pyflakes==3.3.2 105 | # via flake8 106 | pygments==2.19.1 107 | # via rich 108 | pylint==3.3.6 109 | # via -r requirements/requirements-dev.in 110 | pyyaml==6.0.2 111 | # via 112 | # bandit 113 | # xenon 114 | radon==6.0.1 115 | # via 116 | # -r requirements/requirements-dev.in 117 | # xenon 118 | regex==2024.11.6 119 | # via nltk 120 | requests==2.32.3 121 | # via 122 | # safety 123 | # xenon 124 | rich==14.0.0 125 | # via 126 | # bandit 127 | # typer 128 | ruamel-yaml==0.18.10 129 | # via 130 | # safety 131 | # safety-schemas 132 | safety==3.3.1 133 | # via -r requirements/requirements-dev.in 134 | safety-schemas==0.0.11 135 | # via safety 136 | shellingham==1.5.4 137 | # via typer 138 | six==1.17.0 139 | # via mando 140 | stevedore==5.4.1 141 | # via bandit 142 | tomlkit==0.13.2 143 | # via pylint 144 | tqdm==4.67.1 145 | # via nltk 146 | typer==0.15.2 147 | # via safety 148 | typing-extensions==4.13.2 149 | # via 150 | # mypy 151 | # pydantic 152 | # pydantic-core 153 | # safety 154 | # safety-schemas 155 | # typer 156 | urllib3==1.26.20 157 | # via 158 | # -c /Users/igirardi/Desktop/Desktop/APG-2025-CT-Update/PR-12-CDK/aws-control-tower-controls-cdk/GitHub-Temp/aws-control-tower-controls-cdk/requirements/requirements.in 159 | # requests 160 | xenon==0.9.3 161 | # via -r requirements/requirements-dev.in 162 | 163 | # The following packages are considered to be unsafe in a requirements file: 164 | # setuptools 165 | -------------------------------------------------------------------------------- /constants.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | # List of Control Tower Guardrails 18 | # https://docs.aws.amazon.com/controltower/latest/userguide/controltower-ug.pdf 19 | 20 | # Organizational Unit Identifier 21 | 22 | # The pattern for an organizational unit ID string requires "ou-" followed by 23 | # from 4 to 32 lowercase letters or digits 24 | # (the ID of the root that contains the OU). 25 | # This string is followed by a second "-" dash and from 8 to 32 26 | # additional lowercase letters or digits. 27 | 28 | 29 | ACCOUNT_ID = "111111111111" 30 | ROLE_ARN = "" 31 | AWS_CONTROL_TOWER_REGION = "eu-west-1" 32 | 33 | # pylint: disable=duplicate-code 34 | GUARDRAILS_CONFIGURATION = [ 35 | { 36 | "Enable-Control": { 37 | "503uicglhjkokaajywfpt6ros", # AWS-GR_ENCRYPTED_VOLUMES 38 | "2j9gjxqfo040xtx8kd1jf4ni6", # AWS-GR_EBS_OPTIMIZED_INSTANCE 39 | "8c3i4catfgmyy1e19476v06rr", # AWS-GR_EC2_VOLUME_INUSE_CHECK 40 | "4jc77cq1lcr7g64xywwypykv8", # AWS-GR_RDS_INSTANCE_PUBLIC_ACCESS_CHECK 41 | "1h4eyqyyonp19dlrreqf1i3w0", # AWS-GR_RDS_SNAPSHOTS_PUBLIC_PROHIBITED 42 | "e34kieahgkm0lggs5g0s412jt", # AWS-GR_RDS_STORAGE_ENCRYPTED 43 | "6rilu41n0gb9w6mxrkyewoer4", # AWS-GR_RESTRICTED_SSH 44 | "5kvme4m5d2b4d7if2fs5yg2ui", # AWS-GR_RESTRICT_ROOT_USER 45 | "24izmu4k16gv9tvd7sexnyrfy", # AWS-GR_ROOT_ACCOUNT_MFA_ENABLED 46 | "8sw3pbid15t9cbww8d2w2qwgf", # AWS-GR_S3_BUCKET_PUBLIC_READ_PROHIBITED 47 | "9j9nwxj789d82sypnukhyyowy", # AWS-GR_S3_BUCKET_PUBLIC_WRITE_PROHIBITED 48 | }, 49 | "OrganizationalUnitIds": ["ou-1111-11111111"], 50 | }, 51 | { 52 | "Enable-Control": { 53 | "df2ta5ytg2zatj1q7y5e09u32": { # AWS-GR_RESTRICTED_COMMON_PORTS 54 | "Tags": [{"key": "Environment", "value": "Production"}] 55 | }, 56 | "7mo7a2h2ebsq71l8k6uzr96ou": { # CT.S3.PV.5 57 | "Tags": [{"key": "Environment", "value": "Production"}] 58 | }, 59 | "dvhe47fxg5o6lryqrq9g6sxg4": { # CT.SECRETSMANAGER.PV.1 60 | "Tags": [{"key": "Environment", "value": "Production"}] 61 | }, 62 | "eolw7feyvr8b4l2lfhp3bneou": { # CT.KMS.PV.7 63 | "Tags": [{"key": "Environment", "value": "Production"}] 64 | }, 65 | "ek6wc2bmgzmho1kk6bn236mqt": { # CT.EC2.PV.7 66 | "Tags": [{"key": "Environment", "value": "Production"}] 67 | }, 68 | "ka8e3pkqefnjsxuyc26ji580": { # CT.MULTISERVICE.PV.1 69 | "Parameters": { 70 | "AllowedRegions": ["eu-west-1", "us-east-1", "us-west-2"], 71 | "ExemptedPrincipalArns": [], 72 | "ExemptedActions": [], 73 | } 74 | }, 75 | "9sqqct2tcfsnr10yl4f2av1mq": { # CT.EC2.PV.6 76 | "Parameters": { 77 | "ExemptedPrincipalArns": ["arn:aws:iam::*:role/RoleName"], 78 | } 79 | }, 80 | "3zbcht7oxkzts9r1z20nz5lcw": { # AWS-GR_RESTRICT_S3_CROSS_REGION_REPLICATION 81 | "Parameters": { 82 | "ExemptedPrincipalArns": ["arn:aws:iam::*:role/RoleName"], 83 | } 84 | }, 85 | "8ui9y3oace2513xarz8aqojl7": { # AWS-GR_RESTRICT_ROOT_USER_ACCESS_KEYS 86 | "Parameters": { 87 | "ExemptedPrincipalArns": ["arn:aws:iam::*:role/RoleName"], 88 | } 89 | }, 90 | }, 91 | "OrganizationalUnitIds": ["ou-1111-11111111"], 92 | }, 93 | { 94 | "Enable-Control": { 95 | "7z1uzm6s8qk7ym7m0sm56cq0u", # AWS-GR_IAM_USER_MFA_ENABLED 96 | "50z1ot237wl8u1lv5ufau6qqo", # AWS-GR_SUBNET_AUTO_ASSIGN_PUBLIC_IP_DISABLED 97 | "aemn4s3hxv9erree434pvjboi", # AWS-GR_AUTOSCALING_LAUNCH_CONFIG_PUBLIC_IP_DISABLED 98 | "dekrrxbiux86m6jdowdsbamze", # AWS-GR_DMS_REPLICATION_NOT_PUBLIC 99 | "87qo8rsoettjrxjevmjqcw1tu", # AWS-GR_EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK 100 | "4v7xtm83uvvyulk1wwpm4qm3s", # AWS-GR_EC2_INSTANCE_NO_PUBLIC_IP 101 | "aeellyghb27pbehyzua1nyena", # AWS-GR_EKS_ENDPOINT_NO_PUBLIC_ACCESS 102 | "2civrte1w8tqff4vbtzdl4abq", # AWS-GR_ELASTICSEARCH_IN_VPC_ONLY 103 | "5cnql6so7p7bs0khdjodjr9e2", # AWS-GR_EMR_MASTER_NO_PUBLIC_IP 104 | "b2gzofz99eb7nsuj5g8wcimse", # AWS-GR_LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED 105 | "b8pjfqosgkgknznstduvel4rh", # AWS-GR_NO_UNRESTRICTED_ROUTE_TO_IGW 106 | "1oxkwnc4hwhi2ndv6ekwy7np7", # AWS-GR_REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK 107 | "6wmutsohbkwhfw6sf7cbt5e81", # AWS-GR_S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS_PERIODIC 108 | "66gfl06uj1v999z53szvu0exa", # AWS-GR_SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS 109 | }, 110 | "OrganizationalUnitIds": ["ou-2222-22222222"], 111 | }, 112 | { 113 | "Enable-Control": { 114 | "dfanrd8y5p7oj8fjyugqnakfr": { # AWS-GR_SSM_DOCUMENT_NOT_PUBLIC 115 | "Tags": [{"key": "Environment", "value": "Production"}] 116 | }, 117 | "dvuaav61i5cnfazfelmvn9m6k": { # AWS-GR_DISALLOW_CROSS_REGION_NETWORKING 118 | "Parameters": { 119 | "ExemptedPrincipalArns": ["arn:aws:iam::*:role/RoleName"], 120 | }, 121 | "Tags": [{"key": "Environment", "value": "Production"}], 122 | }, 123 | "41ngl8m5c4eb1myoz0t707n7h": { # AWS-GR_DISALLOW_VPC_INTERNET_ACCESS 124 | "Parameters": { 125 | "ExemptedPrincipalArns": ["arn:aws:iam::*:role/RoleName"], 126 | } 127 | }, 128 | "5rlqt6yj6u0v0gb62pqdy4ae": { # AWS-GR_DISALLOW_VPN_CONNECTIONS 129 | "Parameters": { 130 | "ExemptedPrincipalArns": ["arn:aws:iam::*:role/RoleName"], 131 | } 132 | }, 133 | "4wtwsih93j7vct3k0pyvrs3e2": { # AWS-GR_AUDIT_BUCKET_POLICY_CHANGES_PROHIBITED 134 | "Parameters": { 135 | "ExemptedPrincipalArns": ["arn:aws:iam::*:role/RoleName"], 136 | } 137 | }, 138 | }, 139 | "OrganizationalUnitIds": ["ou-2222-22222222"], 140 | }, 141 | ] 142 | -------------------------------------------------------------------------------- /stacks/aws_control_tower_guardrails_stack.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | import importlib 18 | from typing import Dict, Generator, List 19 | 20 | import boto3 21 | from aws_cdk import CfnTag 22 | from aws_cdk import Stack 23 | from aws_cdk.aws_controltower import CfnEnabledControl 24 | from constructs import Construct 25 | 26 | from constants import AWS_CONTROL_TOWER_REGION 27 | from constants import GUARDRAILS_CONFIGURATION 28 | 29 | ROLE_ARN = getattr(importlib.import_module("constants"), "ROLE_ARN", None) 30 | 31 | 32 | class AwsControlTowerGuardrailsStack(Stack): 33 | """ 34 | This stack manages AWS Control Tower Guardrails from 35 | the configuration GUARDRAILS_CONFIGURATION. 36 | Guardrails format arn:aws:controlcatalog:::control/. 37 | """ 38 | 39 | def __init__( # type: ignore 40 | self, scope: Construct, construct_id: str, **kwargs 41 | ) -> None: 42 | """ 43 | Initialize the stack that manages AWS Control Tower Guardrails. 44 | 45 | Args: 46 | scope (Construct): CDK construct 47 | construct_id (str): construct id 48 | """ 49 | super().__init__(scope, construct_id, **kwargs) 50 | 51 | self.enable_guardrails() 52 | self.add_dependencies() 53 | 54 | def chunks( 55 | self, input_list: List[CfnEnabledControl], num: int 56 | ) -> Generator[List[CfnEnabledControl], None, None]: 57 | """ 58 | Yield successive n-sized chunks from an input list. 59 | 60 | Args: 61 | input_list (List[CfnEnabledControl]): List of CDK construcs CfnEnabledControl 62 | num (int): Number of chunks 63 | 64 | Yields: 65 | Generator[List[CfnEnabledControl], None, None]: Generator for the chunked List 66 | of CDK construcs CfnEnabledControl 67 | """ 68 | 69 | for i in range(0, len(input_list), num): 70 | yield input_list[i : i + num] 71 | 72 | def get_organizational_unit_arns( 73 | self, organizational_units_ids: List[str] 74 | ) -> Dict[str, str]: 75 | """ 76 | Get organizational unit arn from the id. 77 | 78 | Args: 79 | organizational_units_ids (List[str]): List of organizational unit ids 80 | 81 | Returns: 82 | Dict[str, str]: map from organizational unit arn to organizational id 83 | """ 84 | 85 | if ROLE_ARN is not None and ROLE_ARN != "": 86 | session_name = ROLE_ARN.split("/")[-1][0:64] 87 | response = boto3.client("sts").assume_role( 88 | RoleArn=ROLE_ARN, RoleSessionName=session_name 89 | ) 90 | boto3_session = boto3.session.Session( 91 | aws_access_key_id=response["Credentials"]["AccessKeyId"], 92 | aws_secret_access_key=response["Credentials"]["SecretAccessKey"], 93 | aws_session_token=response["Credentials"]["SessionToken"], 94 | ) 95 | client = boto3_session.client( 96 | "organizations", region_name=AWS_CONTROL_TOWER_REGION 97 | ) 98 | else: 99 | client = boto3.client("organizations") 100 | 101 | organizational_units_arns = {} 102 | 103 | for organizational_unit_id in organizational_units_ids: 104 | 105 | response = client.describe_organizational_unit( 106 | OrganizationalUnitId=organizational_unit_id 107 | ) 108 | organizational_units_arns[response["OrganizationalUnit"]["Arn"]] = ( 109 | organizational_unit_id 110 | ) 111 | 112 | return organizational_units_arns 113 | 114 | def enable_guardrails(self) -> None: 115 | """ 116 | Construct list of L1 CKD constructs to enable guardrails. 117 | 118 | Raises: 119 | Exception: Invalid / unsupported guardrail name from the configuration file. 120 | """ 121 | 122 | self.cfn_enabled_controls = [] 123 | 124 | for guardrails in GUARDRAILS_CONFIGURATION: 125 | 126 | enable_guardrails = guardrails["Enable-Control"] 127 | 128 | organizational_units_ids = list(guardrails["OrganizationalUnitIds"]) 129 | organizational_units_arns = self.get_organizational_unit_arns( 130 | organizational_units_ids 131 | ) 132 | 133 | for ( 134 | organizational_unit_arn, 135 | ou_id, 136 | ) in organizational_units_arns.items(): 137 | if isinstance(enable_guardrails, dict): 138 | control_names = set(enable_guardrails.keys()) 139 | elif isinstance(enable_guardrails, set): 140 | control_names = enable_guardrails 141 | else: 142 | raise ValueError( 143 | "Guardrails configuration is invalid." 144 | " Enable-Control is expected to be dict or set." 145 | ) 146 | 147 | for control_name in control_names: 148 | cfn_enabled_control = CfnEnabledControl( 149 | self, 150 | f"CfnEnabledControl-{control_name}-{ou_id}", 151 | control_identifier=( 152 | f"arn:aws:controlcatalog:::control/{control_name}" 153 | ), 154 | target_identifier=organizational_unit_arn, 155 | ) 156 | if ( 157 | isinstance(enable_guardrails, dict) 158 | and enable_guardrails[control_name] 159 | ): 160 | if "Parameters" in enable_guardrails[control_name]: 161 | cfn_enabled_control.parameters = [ 162 | CfnEnabledControl.EnabledControlParameterProperty( 163 | key=k, value=v 164 | ) 165 | for k, v in enable_guardrails[control_name][ 166 | "Parameters" 167 | ].items() 168 | if v 169 | ] 170 | if "Tags" in enable_guardrails[control_name]: 171 | cfn_enabled_control.tags = [ 172 | CfnTag(key=tag["key"], value=tag["value"]) 173 | for tag in enable_guardrails[control_name]["Tags"] 174 | ] 175 | self.cfn_enabled_controls.append(cfn_enabled_control) 176 | 177 | def add_dependencies(self) -> None: 178 | """ 179 | Add resource dependencies to deploy only ten guardrails in parallel 180 | because of the conncurrency limit in the API. 181 | """ 182 | 183 | # Split the API calls in concurrent chunks 184 | if len(self.cfn_enabled_controls) % 100 == 0: 185 | num_chunks = len(self.cfn_enabled_controls) // 100 186 | else: 187 | num_chunks = len(self.cfn_enabled_controls) // 100 + 1 188 | 189 | for chunk in self.chunks(self.cfn_enabled_controls, num_chunks): 190 | 191 | # Deploy sequencially each element in the chunk 192 | for control_i, control_j in zip(chunk, chunk[1:]): 193 | control_j.node.add_dependency(control_i) 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deploy and manage AWS Control Tower controls by using AWS CDK and AWS CloudFormation 2 | 3 | 4 | - [AWS Prescriptive Guidance](#aws-prescriptive-guidance) 5 | - [Goal](#goal) 6 | - [Prerequisites and Limitations](#prerequisites-and-limitations) 7 | - [Architecture](#architecture) 8 | - [Tools](#tools) 9 | - [Best practices](#best-practices) 10 | - [Control Behavior And Guidance](#control-behavior-and-guidance) 11 | - [Setup](#setup) 12 | - [Requirements](#requirements) 13 | - [Prepare to enable the controls](#prepare-to-enable-the-controls) 14 | - [Controls Configuration File](#controls-configuration-file) 15 | - [Deployment](#deployment) 16 | - [Useful Commands](#useful-commands) 17 | - [IAM policy](#iam-policy) 18 | - [Trust policy](#trust-policy) 19 | - [AWS Control Catalog](#aws-control-catalog) 20 | - [Authors](#authors) 21 | - [Security](#security) 22 | - [License](#license) 23 | 24 | ## AWS Prescriptive Guidance 25 | 26 | For a complete guide, prerequisites and instructions for using this AWS Prescriptive Guidance pattern, see [Deploy and manage AWS Control Tower controls by using AWS CDK and AWS CloudFormation](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-and-manage-aws-control-tower-controls-by-using-aws-cdk-and-aws-cloudformation.html). 27 | 28 | 29 | ## Goal 30 | 31 | This pattern describes how to use AWS CloudFormation and AWS Cloud Development Kit (AWS CDK) to implement and administer preventive, detective, and proactive AWS Control Tower controls as infrastructure as code (IaC). 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. 32 | 33 | 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. 34 | 35 | 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. 36 | 37 | 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 CloudFormation and AWS CDK, you can standardize the controls in your landing zone and more efficiently deploy and manage them. This solution uses [cdk_nag](https://github.com/cdklabs/cdk-nag#readme) to scan the AWS CDK application during deployment. This tool checks the application for adherence to AWS best practices. 38 | 39 | To deploy AWS Control Tower controls as IaC, you can also use HashiCorp Terraform instead of AWS CDK. For more information, see [Deploy and manage AWS Control Tower controls by using Terraform](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-and-manage-aws-control-tower-controls-by-using-terraform.html). 40 | 41 | ### Intended Audience 42 | 43 | This pattern is recommended for users who have experience with AWS Control Tower, CloudFormation, AWS CDK, and AWS Organizations. 44 | 45 | ## Prerequisites and Limitations 46 | 47 | ### Prerequisites 48 | 49 | - 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. 50 | 51 | - 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). 52 | 53 | - Node package manager (npm), installed and configured for the AWS CDK. 54 | 55 | - [Prerequisites](https://docs.aws.amazon.com/cdk/v2/guide/work-with.html#work-with-prerequisites) for AWS CDK. 56 | 57 | - Permissions to assume an existing AWS Identity and Access Management (IAM) role in a deployment account. 58 | 59 | - Permissions to assume an IAM role in the organization’s management account that that can be used to bootstrap AWS CDK. The role must have permissions to modify and deploy CloudFormation resources. For more information, see [Bootstrapping](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html#bootstrapping-howto) in the AWS CDK documentation. 60 | 61 | - Permissions to create IAM roles and policies in the organization’s management account. For more information, see [Permissions required to access IAM resources](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_permissions-required.html) in the IAM documentation. 62 | 63 | - Apply the service control policy (SCP)-based control with the identifier CT.CLOUDFORMATION.PR.1. This SCP must be activated to deploy proactive controls. For instructions, see [Disallow management of resource types, modules, and hooks within the AWS CloudFormation registry](https://docs.aws.amazon.com/controltower/latest/userguide/elective-controls.html#disallow-cfn-extensions). 64 | 65 | ### Limitations 66 | 67 | - 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: 68 | `arn::controlcatalog:::control/` 69 | **Note:** In most cases, the value for `` is aws. 70 | 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. 71 | 72 | - This pattern provides instructions for deploying this solution across AWS accounts, from a deployment account to the organization’s management account. For testing purposes, you can deploy this solution directly in the management account, but instructions for this configuration are not explicitly provided. 73 | 74 | 75 | ## Architecture 76 | 77 | 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. 78 | 79 | ![Architecture](img/ctc-architecture.png) 80 | 81 | AWS Control Tower controls are categorized according to their behavior and their guidance. 82 | 83 | There are three primary types of control behaviors: 84 | 85 | 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. 86 | 87 | 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. 88 | 89 | 3. Proactive controls scan resources that would be provisioned by AWS CloudFormation and check whether they are compliant with your company policies and objectives. Resources that are not compliant will not be provisioned. These are implemented with [AWS CloudFormation hooks](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks.html). The status of a proactive control is PASS, FAIL, or SKIP. 90 | 91 | Control guidance refers to the recommended practice for how to apply each control to your OUs. AWS Control Tower provides three categories of guidance: mandatory, strongly recommended, and elective. The guidance of a control is independent of its behavior. For more information, see [Control behavior and guidance](https://docs.aws.amazon.com/controltower/latest/userguide/controls.html#control-behavior). 92 | 93 | 94 | ## Tools 95 | 96 | ### AWS services 97 | 98 | - [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html) is a software development framework that helps you define and provision AWS Cloud infrastructure in code. The [AWS CDK Toolkit](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) is the primary tool for interacting with your AWS CDK app. 99 | 100 | - [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html) helps you set up AWS resources, provision them quickly and consistently, and manage them throughout their lifecycle across AWS accounts and Regions. 101 | 102 | - [AWS Config](https://docs.aws.amazon.com/config/latest/developerguide/WhatIsConfig.html) provides a detailed view of the resources in your AWS account and how they’re configured. It helps you identify how resources are related to one another and how their configurations have changed over time. 103 | 104 | - [AWS Control Tower](https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html) helps you set up and govern an AWS multi-account environment, following prescriptive best practices. 105 | 106 | - [AWS Organizations](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_introduction.html) is an account management service that helps you consolidate multiple AWS accounts into an organization that you create and centrally manage. 107 | 108 | ### Other tools 109 | 110 | - [cdk_nag](https://github.com/cdklabs/cdk-nag#readme) is an open-source tool that uses a combination of rule packs to check AWS Cloud Development Kit (AWS CDK) applications for adherence to best practices. 111 | 112 | - [npm](https://docs.npmjs.com/about-npm) is a software registry that runs in a Node.js environment and is used to share or borrow packages and manage deployment of private packages. 113 | 114 | - [Python](https://www.python.org/) is a general-purpose computer programming language. 115 | 116 | 117 | ## Best practices 118 | 119 | - Adhere to the [principle of least-privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege) (IAM documentation). The sample IAM policy and trust policy provided in this pattern include the minimum permissions required, and the AWS CDK stacks created in the management account are restricted by these permissions. 120 | 121 | - Adhere to the [Best practices for AWS Control Tower administrators](https://docs.aws.amazon.com/controltower/latest/userguide/best-practices.html) (AWS Control Tower documentation). 122 | 123 | - Adhere to the [Best practices for developing and deploying cloud infrastructure with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/best-practices.html) (AWS CDK documentation). 124 | 125 | - When bootstrapping the AWS CDK, customize the bootstrap template to define policies and the trusted accounts that should have the ability to read and write to any resource in the management account. For more information, see [Customizing bootstrapping](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html#bootstrapping-customizing). 126 | 127 | - Use code analysis tools, such as [cfn_nag](https://github.com/stelligent/cfn_nag), to scan the generated CloudFormation templates. The cfn-nag tool looks for patterns in CloudFormation templates that might indicate the infrastructure is not secure. You can also use cdk-nag to check your CloudFormation templates by using the [cloudformation-include](https://docs.aws.amazon.com/cdk/latest/guide/use_cfn_template.html#use_cfn_template_install) module. 128 | 129 | 130 | ## Control Behavior And Guidance 131 | 132 | [Controls are categorized according to their behavior and their guidance.](https://docs.aws.amazon.com/controltower/latest/userguide/controls.html) 133 | 134 | [For a full list of preventive, detective and proactive available controls, see the The AWS Control Tower controls library.](https://docs.aws.amazon.com/controltower/latest/userguide/controls-reference.html) 135 | 136 | 137 | ## Setup 138 | 139 | ### Requirements 140 | To deploy this solution, you need 141 | 142 | 143 | | Name | Version | 144 | |------|---------| 145 | | [AWS Control Tower](https://aws.amazon.com/controltower/) | >= 3.2 | 146 | | [Python](https://www.python.org/) | >= 3.9 | 147 | | [npm](https://www.npmjs.com/) | >= 8.9.0 | 148 | 149 | 150 | The `cdk.json` file tells the CDK Toolkit how to execute the code. 151 | The `package.json` requires `npm` to be already installed. 152 | 153 | 154 | ### Prepare to enable the controls 155 | 156 | 1. Create an IAM policy in the management account with the permissions defined in IAM policy in the [Additional information](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-and-manage-aws-control-tower-controls-by-using-aws-cdk-and-aws-cloudformation.html#deploy-and-manage-aws-control-tower-controls-by-using-aws-cdk-and-aws-cloudformation-additional) section. For instructions, see [Creating IAM policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create.html) in the IAM documentation. Make note of the Amazon Resource Name (ARN) of the policy. The following is an example ARN. 157 | ``` 158 | arn:aws:iam:::policy/ 159 | ``` 160 | 161 | 2. Create an IAM role in the management account, attach the IAM permission policy that you created in the previous step, and attach the custom trust policy in Trust policy in the [Additional information](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-and-manage-aws-control-tower-controls-by-using-aws-cdk-and-aws-cloudformation.html#deploy-and-manage-aws-control-tower-controls-by-using-aws-cdk-and-aws-cloudformation-additional) section. For instructions, see [Creating a role using custom trust policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-custom.html) in the IAM documentation. The following is an example ARN for the new role. 162 | ``` 163 | arn:aws:iam:: MANAGEMENT-ACCOUNT-ID:role/ROLE-NAME 164 | ``` 165 | 166 | 3. In the management account, assume a role that has permissions to bootstrap AWS CDK. 167 | 168 | 4. Enter the following command, replacing the following: 169 | - `` is the ID of the organization’s management account. 170 | - `` is the AWS Region where Control Tower is deployed. For a complete list of Region codes, see [Regional endpoints](https://docs.aws.amazon.com/general/latest/gr/rande.html#regional-endpoints) in AWS General Reference. 171 | - `` is the ID of the deployment account. 172 | - `` is the name of the IAM role you are using the deployment account. 173 | - `` is the name of the policy you created in the management account. 174 | ``` 175 | $ npx cdk bootstrap aws://MANAGEMENT-ACCOUNT-ID/AWS-CONTROL-TOWER-REGION \ 176 | --trust arn:aws:iam::DEPLOYMENT-ACCOUNT-ID:role/DEPLOYMENT-ROLE-NAME \ 177 | --cloudformation-execution-policies arn:aws:iam::MANAGEMENT-ACCOUNT-ID:policy/POLICY-NAME 178 | ``` 179 | 180 | ### Controls Configuration File 181 | The following is an example of an updated constants.py file. 182 | ``` 183 | ACCOUNT_ID = 111122223333 184 | AWS_CONTROL_TOWER_REGION = us-east-2 185 | ROLE_ARN = "arn:aws:iam::111122223333:role/CT-Controls-Role" 186 | GUARDRAILS_CONFIGURATION = [ 187 | { 188 | "Enable-Control": { 189 | "dvuaav61i5cnfazfelmvn9m6k": { # AWS-GR_DISALLOW_CROSS_REGION_NETWORKING 190 | "Parameters": { 191 | "ExemptedPrincipalArns": ["arn:aws:iam::111122223333:role/RoleName"] 192 | }, 193 | "Tags": [{"key": "Environment", "value": "Production"}] 194 | }, 195 | ... 196 | }, 197 | "OrganizationalUnitIds": ["ou-1111-11111111", "ou-2222-22222222"...], 198 | }, 199 | { 200 | "Enable-Control": { 201 | "50z1ot237wl8u1lv5ufau6qqo", # AWS-GR_SUBNET_AUTO_ASSIGN_PUBLIC_IP_DISABLED 202 | ... 203 | }, 204 | "OrganizationalUnitIds": ["ou-2222-22222222"...], 205 | }, 206 | ] 207 | ``` 208 | 209 | 1. In the cloned repository, open the constants.py file. 210 | 211 | 2. In the `ACCOUNT_ID` parameter, enter the ID of your management account. 212 | 213 | 3. In the `` parameter, enter AWS Region where AWS Control Tower is deployed. 214 | 215 | 4. In the `ROLE_ARN` parameter, enter the ARN of the role you created in the management account. 216 | 217 | 5. Open [All global identifiers](https://docs.aws.amazon.com/controltower/latest/controlreference/all-global-identifiers.html) in the AWS Control Tower documentation. 218 | 219 | 6. 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_ENCRYPTED_VOLUMES` control is `503uicglhjkokaajywfpt6ros`. 220 | 221 | 7. In the `GUARDRAILS_CONFIGURATION` section, in the `Enable-Control` parameter, enter the global identifier that you copied. Enter the identifier in double quotation marks, and separate multiple identifiers with commas. 222 | 223 | 8. In the `GUARDRAILS_CONFIGURATION` section, in the `OrganizationalUnitIds` 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). 224 | 225 | 9. Save and close the constants.py file. For an example of an updated constants.py file, see the [Additional information](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-and-manage-aws-control-tower-controls-by-using-aws-cdk-and-aws-cloudformation.html#deploy-and-manage-aws-control-tower-controls-by-using-aws-cdk-and-aws-cloudformation-additional) section of this pattern. 226 | 227 | 228 | ## Deployment 229 | 230 | 1. In the deployment account, assume the IAM role that has permissions to deploy the AWS CDK stacks in the management account. For more information about assuming an IAM role in the AWS CLI, see [Use an IAM role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html). 231 | 232 | 2. If you are using Linux or MacOS: 233 | 1. Enter the following command to create a virtual environment. 234 | ``` 235 | $ python3 -m venv .venv 236 | ``` 237 | 2. After the virtual environment is created, enter the following command to activate it. 238 | ``` 239 | $ source .venv/bin/activate 240 | ``` 241 | 3. If you are using Windows: 242 | 1. Enter the following command to activate a virtual environment. 243 | ``` 244 | % .venv\Scripts\activate.bat 245 | ``` 246 | 247 | 4. After the virtual environment is activated, enter the following command to run the install_deps.sh script. This script installs the required dependencies. 248 | ``` 249 | $ ./scripts/install_deps.sh 250 | ``` 251 | 252 | 5. Enter the following command to synthesize and deploy the CloudFormation stack. 253 | ``` 254 | $ npx cdk synth 255 | $ npx cdk deploy 256 | ``` 257 | 258 | ## Useful Commands 259 | 260 | * `npx cdk ls` list all stacks in the app 261 | * `npx cdk synth` emits the synthesized CloudFormation template 262 | * `npx cdk deploy` deploy this stack 263 | * `npx cdk destroy` destroy this stack 264 | * `npx cdk diff` compare deployed stack with current state 265 | * `npx cdk docs` open CDK documentation 266 | 267 | 268 | ## IAM policy 269 | 270 | The following sample policy allows the minimum actions required to enable or disable AWS Control Tower controls when deploying AWS CDK stacks from a deployment account to the management account. 271 | ``` 272 | { 273 | "Version": "2012-10-17", 274 | "Statement": [ 275 | { 276 | "Effect": "Allow", 277 | "Action": [ 278 | "controltower:EnableControl", 279 | "controltower:DisableControl", 280 | "controltower:GetControlOperation", 281 | "controltower:GetEnabledControl", 282 | "controltower:ListControlOperations", 283 | "controltower:ListEnabledControls", 284 | "controltower:ListTagsForResource", 285 | "controltower:ResetEnabledControl", 286 | "controltower:TagResource", 287 | "controltower:UntagResource", 288 | "controltower:UpdateEnabledControl", 289 | "organizations:AttachPolicy", 290 | "organizations:CreatePolicy", 291 | "organizations:DeleteResourcePolicy", 292 | "organizations:DeletePolicy", 293 | "organizations:DescribeOrganization", 294 | "organizations:DescribeOrganizationalUnit", 295 | "organizations:DescribePolicy", 296 | "organizations:DescribeResourcePolicy", 297 | "organizations:DetachPolicy", 298 | "organizations:DisablePolicyType", 299 | "organizations:EnablePolicyType", 300 | "organizations:ListAccounts", 301 | "organizations:ListAWSServiceAccessForOrganization", 302 | "organizations:ListChildren", 303 | "organizations:ListOrganizationalUnitsForParent", 304 | "organizations:ListParents", 305 | "organizations:ListPolicies", 306 | "organizations:ListPoliciesForTarget", 307 | "organizations:ListRoots", 308 | "organizations:ListTagsForResource", 309 | "organizations:ListTargetsForPolicy", 310 | "organizations:TagResource", 311 | "organizations:PutResourcePolicy", 312 | "organizations:UntagResource", 313 | "organizations:UpdatePolicy", 314 | "ssm:GetParameters" 315 | ], 316 | "Resource": "*" 317 | } 318 | ] 319 | } 320 | ``` 321 | 322 | ## Trust policy 323 | 324 | The following custom trust policy allows a specific IAM role in the deployment account to assume the IAM role in the management account. Replace the following: 325 | - `` is the ID of the deployment account 326 | - `` is the name of the role in the deployment account that is allowed to assume the role in the management account 327 | 328 | ``` 329 | { 330 | “Version”: “2012-10-17”, 331 | “Statement”: [ 332 | { 333 | “Effect”: “Allow”, 334 | “Principal”: { 335 | “AWS”: “arn:aws:iam:::role/” 336 | }, 337 | “Action”: “sts:AssumeRole”, 338 | “Condition”: {} 339 | } 340 | ] 341 | } 342 | ``` 343 | 344 | ## AWS Control Catalog 345 | 346 | An example of exported controls using the AWS Control Catalog API is provided in the helpers folder [here](helpers/controlcatalog_list_controls.csv). 347 | 348 | ## Authors 349 | 350 | Pattern created by Ivan Girardi (AWS) and Iker Reina Fuente (AWS). 351 | 352 | ## Security 353 | 354 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 355 | 356 | ## License 357 | 358 | This library is licensed under the MIT-0 License. See the [LICENSE](LICENSE) file. 359 | --------------------------------------------------------------------------------