├── VERSION ├── PYTHON_VERSION ├── examples ├── multiple-regions-customization │ └── multiple-regions │ │ ├── api_helpers │ │ ├── python │ │ │ └── requirements.txt │ │ ├── post-api-helpers.sh │ │ └── pre-api-helpers.sh │ │ ├── main.tf │ │ └── terraform │ │ ├── backend.jinja │ │ └── aft-providers.jinja ├── multiple-account-customizations │ ├── account-customization-dev │ │ ├── api_helpers │ │ │ ├── post-api-helpers.sh │ │ │ └── pre-api-helpers.sh │ │ └── terraform │ │ │ ├── aft-providers.jinja │ │ │ ├── main.tf │ │ │ └── backend.jinja │ ├── account-customization-prod │ │ ├── api_helpers │ │ │ ├── pre-api-helpers.sh │ │ │ └── post-api-helpers.sh │ │ └── terraform │ │ │ ├── aft-providers.jinja │ │ │ ├── main.tf │ │ │ └── backend.jinja │ └── modules │ │ ├── production │ │ └── main.tf │ │ ├── security │ │ └── main.tf │ │ └── development │ │ └── main.tf ├── codecommit+tf_oss │ └── main.tf ├── github+tf_oss │ └── main.tf ├── gitlab+tf_oss │ └── main.tf ├── bitbucket+tf_enterprise │ └── main.tf ├── githubenterprise+tf_cloud │ └── main.tf └── gitlabselfmanaged+tf_cloud │ └── main.tf ├── sources ├── aft-customizations-repos │ ├── aft-global-customizations │ │ ├── api_helpers │ │ │ ├── python │ │ │ │ └── requirements.txt │ │ │ ├── pre-api-helpers.sh │ │ │ └── post-api-helpers.sh │ │ ├── terraform │ │ │ ├── aft-providers.jinja │ │ │ └── backend.jinja │ │ └── README.md │ ├── aft-account-customizations │ │ └── ACCOUNT_TEMPLATE │ │ │ ├── api_helpers │ │ │ ├── python │ │ │ │ └── requirements.txt │ │ │ ├── post-api-helpers.sh │ │ │ └── pre-api-helpers.sh │ │ │ └── terraform │ │ │ ├── aft-providers.jinja │ │ │ └── backend.jinja │ ├── aft-account-provisioning-customizations │ │ ├── terraform │ │ │ ├── states │ │ │ │ └── customizations.asl.json │ │ │ ├── iam │ │ │ │ ├── trust-policies │ │ │ │ │ └── states.tpl │ │ │ │ └── role-policies │ │ │ │ │ └── iam-aft-states.tpl │ │ │ ├── versions.tf │ │ │ ├── aft-providers.jinja │ │ │ ├── states.tf │ │ │ ├── iam.tf │ │ │ └── backend.jinja │ │ └── README.md │ └── aft-account-request │ │ ├── terraform │ │ ├── README.md │ │ ├── aft-providers.jinja │ │ ├── modules │ │ │ └── aft-account-request │ │ │ │ ├── versions.tf │ │ │ │ ├── variables.tf │ │ │ │ └── ddb.tf │ │ └── backend.jinja │ │ └── examples │ │ └── account-request.tf ├── aft-lambda-layer │ ├── aft_common │ │ ├── __init__.py │ │ ├── exceptions.py │ │ ├── aft_types.py │ │ ├── s3.py │ │ ├── notifications.py │ │ ├── premium_support.py │ │ ├── ssm.py │ │ └── ddb.py │ ├── setup.py │ └── pyproject.toml └── aft-customizations-common │ └── templates │ └── customizations_pipeline │ ├── versions.tf │ ├── aft-providers.jinja │ ├── backend.jinja │ └── data.tf ├── CODEOWNERS ├── NOTICE ├── src ├── __init__.py └── aft_lambda │ ├── __init__.py │ ├── aft_customizations │ ├── __init__.py │ ├── aft_customizations_get_pipeline_executions.py │ └── aft_customizations_execute_pipeline.py │ ├── aft_feature_options │ ├── __init__.py │ └── aft_enroll_support.py │ ├── aft_account_request_framework │ ├── __init__.py │ ├── aft_account_request_action_trigger.py │ └── aft_controltower_event_logger.py │ ├── aft_account_provisioning_framework │ ├── __init__.py │ ├── aft_account_provisioning_framework_create_role.py │ ├── aft_account_provisioning_framework_tag_account.py │ └── aft_account_provisioning_framework_persist_metadata.py │ └── aft_builder │ └── codebuild_trigger.py ├── modules ├── aft-code-repositories │ ├── s3.tf │ ├── outputs.tf │ ├── data.tf │ ├── iam │ │ ├── trust-policies │ │ │ ├── events.tpl │ │ │ ├── codebuild.tpl │ │ │ └── codepipeline.tpl │ │ └── role-policies │ │ │ ├── ct_aft_cwe_policy.tpl │ │ │ ├── ct_aft_codebuild_oss_backend_policy.tpl │ │ │ ├── ct_aft_account_request_codepipeline_policy.tpl │ │ │ └── ct_aft_account_provisioning_customizations_codepipeline_policy.tpl │ ├── versions.tf │ ├── locals.tf │ ├── codecommit.tf │ ├── variables.tf │ └── codestar.tf ├── aft-iam-roles │ ├── admin-role │ │ ├── data.tf │ │ ├── main.tf │ │ ├── trust_policy.tpl │ │ └── iam.tf │ ├── service-role │ │ ├── data.tf │ │ ├── trust_policy.tpl │ │ └── main.tf │ ├── data.tf │ ├── locals.tf │ ├── iam │ │ ├── aft_admin_role_trust_policy.tpl │ │ └── aft_admin_role_policy.tpl │ ├── versions.tf │ └── outputs.tf ├── aft-lambda-layer │ ├── outputs.tf │ ├── iam │ │ ├── trust-policies │ │ │ ├── lambda.tpl │ │ │ └── codebuild.tpl │ │ └── role-policies │ │ │ ├── codebuild-trigger.tpl │ │ │ └── codebuild.tpl │ ├── data.tf │ ├── versions.tf │ ├── locals.tf │ ├── main.tf │ ├── lambda.tf │ ├── readme.md │ ├── variables.tf │ ├── iam.tf │ ├── buildspecs │ │ └── aft-lambda-layer.yml │ └── codebuild.tf ├── aft-customizations │ ├── iam │ │ ├── trust-policies │ │ │ ├── lambda.tpl │ │ │ ├── states.tpl │ │ │ ├── codebuild.tpl │ │ │ └── codepipeline.tpl │ │ └── role-policies │ │ │ ├── ct_aft_codebuild_oss_backend_policy.tpl │ │ │ ├── aft_get_pipeline_status_lambda.tpl │ │ │ ├── aft_execute_pipeline_lambda.tpl │ │ │ ├── aft_codepipeline_customizations_policy.tpl │ │ │ ├── aft_identify_targets_lambda.tpl │ │ │ └── aft_states_invoke_customizations_policy.tpl │ ├── locals.tf │ ├── versions.tf │ ├── s3 │ │ └── bucket-policies │ │ │ └── aft_codepipeline_customizations_bucket.tpl │ ├── outputs.tf │ ├── data.tf │ ├── states.tf │ └── s3.tf ├── aft-feature-options │ ├── iam │ │ ├── trust-policies │ │ │ ├── lambda.tpl │ │ │ └── states.tpl │ │ └── role-policies │ │ │ ├── aft_features_states.tpl │ │ │ ├── aft_enroll_support.tpl │ │ │ ├── aft_enable_cloudtrail.tpl │ │ │ └── aft_delete_default_vpc_lambda.tpl │ ├── locals.tf │ ├── versions.tf │ ├── kms.tf │ ├── data.tf │ ├── states.tf │ ├── kms │ │ └── key-policies │ │ │ └── log-key.tpl │ ├── s3 │ │ └── bucket-policies │ │ │ ├── aft_access_logs.tpl │ │ │ └── aft_logging_bucket.tpl │ ├── outputs.tf │ ├── variables.tf │ └── states │ │ └── aft_features.asl.json ├── aft-account-request-framework │ ├── iam │ │ ├── trust-policies │ │ │ ├── events.tpl │ │ │ ├── lambda.tpl │ │ │ ├── states.tpl │ │ │ └── backup.tpl │ │ └── role-policies │ │ │ ├── aft-sts-assume_ct_admin.tpl │ │ │ ├── aftService-assumerole.tpl │ │ │ ├── events-control-tower-events.tpl │ │ │ ├── aft-get-ct-role.tpl │ │ │ ├── aftService-ssm.tpl │ │ │ ├── aftService-gitlab-permissions.tpl │ │ │ ├── aft-states.tpl │ │ │ ├── aft-sts-assume.tpl │ │ │ ├── lambda-controltower-event-logger.tpl │ │ │ ├── lambda-account-request-processor.tpl │ │ │ ├── lambda-invoke-aft-account-provisioning-framework.tpl │ │ │ ├── lambda-account-request-audit-trigger.tpl │ │ │ └── lambda-aft-cleanup-resources.tpl │ ├── versions.tf │ ├── sns.tf │ ├── sqs.tf │ ├── backup.tf │ ├── locals.tf │ ├── variables.tf │ └── kms.tf ├── aft-account-provisioning-framework │ ├── iam │ │ ├── trust-policies │ │ │ ├── lambda.tpl │ │ │ └── states.tpl │ │ └── role-policies │ │ │ └── lambda-aft-account-provisioning-framework.tpl │ ├── locals.tf │ ├── versions.tf │ ├── data.tf │ ├── outputs.tf │ ├── cloudwatch.tf │ ├── states.tf │ └── variables.tf ├── aft-ssm-parameters │ └── versions.tf ├── aft-backend │ ├── versions.tf │ ├── variables.tf │ ├── s3 │ │ └── bucket-policies │ │ │ ├── aft_backend_bucket.tpl │ │ │ └── aft_access_logs_primary_backend_bucket.tpl │ └── outputs.tf └── aft-archives │ ├── outputs.tf │ └── data.tf ├── CODE_OF_CONDUCT.md ├── .coveragerc ├── CONTRIBUTING.md ├── versions.tf ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request_template.md │ └── bug_report_template.md └── pull_request_template.md ├── SECURITY.md ├── data.tf └── providers.tf /VERSION: -------------------------------------------------------------------------------- 1 | 1.17.1 2 | -------------------------------------------------------------------------------- /PYTHON_VERSION: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /examples/multiple-regions-customization/multiple-regions/api_helpers/python/requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-global-customizations/api_helpers/python/requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @ouyanguf @wiltangg @aviwshah @dashbat @dhingraa-github @sk-at-amazon @aspiratr-aws @yashl 2 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/python/requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | AWS Control Tower Account Factory for Terraform 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /src/aft_lambda/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/s3.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/aft_common/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_customizations/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_feature_options/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_account_request_framework/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_account_provisioning_framework/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/admin-role/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | data "aws_partition" "current" {} 6 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/service-role/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | data "aws_partition" "current" {} 6 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "layer_version_arn" { 5 | value = aws_lambda_layer_version.layer_version.arn 6 | } 7 | -------------------------------------------------------------------------------- /examples/multiple-regions-customization/multiple-regions/api_helpers/post-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Post-API Helpers" 7 | -------------------------------------------------------------------------------- /examples/multiple-regions-customization/multiple-regions/api_helpers/pre-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Pre-API Helpers" 7 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-global-customizations/api_helpers/pre-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Pre-API Helpers" 7 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-global-customizations/api_helpers/post-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Post-API Helpers" 7 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-dev/api_helpers/post-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Post-API Helpers" 7 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-dev/api_helpers/pre-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Pre-API Helpers" 7 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-prod/api_helpers/pre-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Pre-API Helpers" 7 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "codeconnections_connection_arn" { 5 | value = lookup(local.connection_arn, var.vcs_provider) 6 | } 7 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states/customizations.asl.json: -------------------------------------------------------------------------------- 1 | { 2 | "StartAt": "Pass", 3 | "States": { 4 | "Pass": { 5 | "Type": "Pass", 6 | "End": true 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-prod/api_helpers/post-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Post-API Helpers" 7 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "aws_partition" "current" {} 5 | 6 | data "aws_region" "current" {} 7 | 8 | data "aws_caller_identity" "current" {} 9 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "aws_caller_identity" "aft_management" { 5 | provider = aws.aft_management 6 | } 7 | 8 | data "aws_partition" "current" {} 9 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/post-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Post-API Helpers" 7 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/pre-api-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | echo "Executing Pre-API Helpers" 7 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/trust-policies/lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "lambda.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/trust-policies/states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "states.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-feature-options/iam/trust-policies/lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "lambda.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-feature-options/iam/trust-policies/states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "states.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/iam/trust-policies/events.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "events.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/trust-policies/codebuild.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "codebuild.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/aft_common/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | class ServiceRoleNotAssociated(Exception): 5 | pass 6 | 7 | 8 | class NoAccountFactoryPortfolioFound(Exception): 9 | pass 10 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/trust-policies/events.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "events.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/trust-policies/lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "lambda.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/trust-policies/states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "states.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/iam/trust-policies/codebuild.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "codebuild.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/trust-policies/codepipeline.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "codepipeline.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-customizations/locals.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] 6 | } 7 | -------------------------------------------------------------------------------- /modules/aft-feature-options/locals.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] 6 | } 7 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import setuptools 5 | 6 | setuptools.setup( 7 | packages=setuptools.find_packages(), 8 | package_data={"aft_common": ["schemas/*.json"]}, 9 | ) 10 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/iam/trust-policies/lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "lambda.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/iam/trust-policies/states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "states.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/iam/trust-policies/codepipeline.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "codepipeline.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/admin-role/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_providers { 6 | aws = { 7 | source = "hashicorp/aws" 8 | version = ">= 5.11.0, < 6.0.0" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/locals.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] 6 | } 7 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-request/terraform/README.md: -------------------------------------------------------------------------------- 1 | ## Jinja files 2 | 3 | Please note that Jinja files included in this directory must be included in your account request repository and are used by the AFT framework to generate components at runtime, but additional Jinja files will not be processed by AFT. 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/iam/trust-policies/lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": [ 8 | "lambda.amazonaws.com" 9 | ] 10 | }, 11 | "Action": "sts:AssumeRole" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/trust-policies/backup.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "allow", 6 | "Principal": { 7 | "Service": ["backup.amazonaws.com"] 8 | }, 9 | "Action": ["sts:AssumeRole"] 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | [run] 5 | branch = True 6 | omit = tests/* 7 | 8 | [report] 9 | show_missing = True 10 | precision = 1 11 | exclude_lines = 12 | # Required due to conditional mypy imports 13 | if TYPE_CHECKING: 14 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/locals.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | locals { 5 | aft_admin_assumed_role_arn = "arn:${data.aws_partition.current.partition}:sts::${data.aws_caller_identity.aft_management.account_id}:assumed-role/AWSAFTAdmin/AWSAFT-Session" 6 | } 7 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/modules/production/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_ssm_parameter" "example_parameter_prod" { 5 | name = "/aft/example/parameter_prod" 6 | type = "String" 7 | value = "Production customizations" 8 | } 9 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/iam/aft_admin_role_trust_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "AWS": "arn:${data_aws_partition_current_partition}:iam::${aft_account_id}:root" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/modules/security/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_ssm_parameter" "example_parameter_security" { 5 | name = "/aft/example/parameter_security" 6 | type = "String" 7 | value = "Security customizations" 8 | } 9 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/trust-policies/states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "states.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "aws_partition" "current" {} 5 | 6 | data "aws_caller_identity" "session" {} 7 | 8 | data "local_file" "aft_lambda_layer" { 9 | filename = "${path.module}/buildspecs/aft-lambda-layer.yml" 10 | } 11 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/modules/development/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | resource "aws_ssm_parameter" "example_parameter_dev" { 6 | name = "/aft/example/parameter_dev" 7 | type = "String" 8 | value = "developer customizations" 9 | } 10 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.84.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /modules/aft-customizations/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /modules/aft-ssm-parameters/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/iam/trust-policies/codebuild.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": [ 8 | "codebuild.amazonaws.com", 9 | "events.amazonaws.com" 10 | ] 11 | }, 12 | "Action": "sts:AssumeRole" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/aft-sts-assume_ct_admin.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "sts:AssumeRole", 7 | "Resource": "arn:${data_aws_partition_current_partition}:iam::*:role/AWSControlTowerExecution" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/role-policies/iam-aft-states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "states:Start*", 7 | "Resource": "${account_provisioning_customizations_sfn_arn}" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/admin-role/trust_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "${trusted_entity_type}": [ 8 | "${trusted_entity}", 9 | "${aft_admin_assumed_role_arn}" 10 | ] 11 | }, 12 | "Action": "sts:AssumeRole" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/service-role/trust_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "${trusted_entity_type}": [ 8 | "${trusted_entity}", 9 | "${aft_admin_assumed_role_arn}" 10 | ] 11 | }, 12 | "Action": "sts:AssumeRole" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/aftService-assumerole.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": { 4 | "Effect": "Allow", 5 | "Action": "sts:AssumeRole", 6 | "Resource": "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_aft-management_account_id}:role${var_aft_role_admin_path}${var_aft_role_admin_name}" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sources/aft-customizations-common/templates/customizations_pipeline/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/events-control-tower-events.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version" : "2012-10-17", 3 | "Statement" : [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : [ 7 | "events:PutEvents" 8 | ], 9 | "Resource" : [ 10 | "${aws_cloudwatch_event_bus_from-ct-management_arn}" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-request/terraform/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ provider_region }}" 6 | assume_role { 7 | role_arn = "{{ aft_admin_role_arn }}" 8 | } 9 | default_tags { 10 | tags = { 11 | managed_by = "AFT" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/aft-get-ct-role.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "iam:GetRole", 7 | "Resource": "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_aft-management_account_id}:role/AWSControlTowerExecution" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/iam/aft_admin_role_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "sts:AssumeRole", 7 | "Resource": [ 8 | "arn:${data_aws_partition_current_partition}:iam::*:role/AWSAFTExecution", 9 | "arn:${data_aws_partition_current_partition}:iam::*:role/AWSAFTService" 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-global-customizations/terraform/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ provider_region }}" 6 | assume_role { 7 | role_arn = "{{ target_admin_role_arn }}" 8 | } 9 | default_tags { 10 | tags = { 11 | managed_by = "AFT" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-dev/terraform/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ provider_region }}" 6 | assume_role { 7 | role_arn = "{{ target_admin_role_arn }}" 8 | } 9 | default_tags { 10 | tags = { 11 | managed_by = "AFT" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-prod/terraform/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ provider_region }}" 6 | assume_role { 7 | role_arn = "{{ target_admin_role_arn }}" 8 | } 9 | default_tags { 10 | tags = { 11 | managed_by = "AFT" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ provider_region }}" 6 | assume_role { 7 | role_arn = "{{ aft_admin_role_arn }}" 8 | } 9 | default_tags { 10 | tags = { 11 | managed_by = "AFT" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/terraform/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ provider_region }}" 6 | assume_role { 7 | role_arn = "{{ target_admin_role_arn }}" 8 | } 9 | default_tags { 10 | tags = { 11 | managed_by = "AFT" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 4.9.0" 11 | configuration_aliases = [aws.ct_management] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sources/aft-customizations-common/templates/customizations_pipeline/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ region }}" 6 | 7 | shared_credentials_files = ["~/.aws/credentials"] 8 | profile = "aft-management" 9 | 10 | default_tags { 11 | tags = { 12 | managed_by = "AFT" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/locals.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | common_name = "python-layer-builder-${var.lambda_layer_name}-${random_string.resource_suffix.result}" 6 | account_id = data.aws_caller_identity.session.account_id 7 | codebuild_trigger_function_name = "aft-lambda-layer-codebuild-trigger" 8 | } 9 | -------------------------------------------------------------------------------- /modules/aft-backend/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 4.27.0" 11 | configuration_aliases = [aws.primary_region, aws.secondary_region] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/aft-backend/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "primary_region" { 5 | type = string 6 | } 7 | 8 | variable "secondary_region" { 9 | type = string 10 | } 11 | 12 | variable "aft_management_account_id" { 13 | type = string 14 | } 15 | 16 | variable "aft_backend_bucket_access_logs_object_expiration_days" { 17 | type = number 18 | } 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the AWS Control Tower Account Factory for Terraform 2 | 3 | Thank you for your interest in contributing to the AWS Control Tower Account Factory for Terraform. 4 | 5 | At this time, we are not accepting contributions. If contributions are accepted in the future, the AWS Control Tower Account Factory for Terraform is released under the [Apache license](http://aws.amazon.com/apache2.0/) and any code submitted will be released under that license. 6 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-prod/terraform/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "prod_customizations" { 5 | source = "../../modules/production" 6 | providers = { 7 | aws = aws 8 | } 9 | } 10 | 11 | module "security_customizations" { 12 | source = "../../modules/security" 13 | providers = { 14 | aws = aws 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-dev/terraform/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "developer_customizations" { 5 | source = "../../modules/development" 6 | providers = { 7 | aws = aws 8 | } 9 | } 10 | 11 | module "security_customizations" { 12 | source = "../../modules/security" 13 | providers = { 14 | aws = aws 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 4.27.0" 11 | configuration_aliases = [aws.ct_management, aws.log_archive, aws.audit, aws.aft_management] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_sfn_state_machine" "aft_account_provisioning_customizations" { 5 | name = "aft-account-provisioning-customizations" 6 | role_arn = aws_iam_role.aft_states.arn 7 | definition = templatefile("${path.module}/states/customizations.asl.json", {}) 8 | } 9 | -------------------------------------------------------------------------------- /modules/aft-feature-options/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 4.27.0" 11 | configuration_aliases = [aws.ct_management, aws.log_archive, aws.audit, aws.aft_management] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/aftService-ssm.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "ssm:GetParameter", 7 | "Resource": [ 8 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:parameter/aft/*" 9 | ] 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | terraform { 5 | required_version = ">= 1.2.0, < 2.0.0" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 5.11.0, < 6.0.0" 11 | configuration_aliases = [aws.ct_management, aws.log_archive, aws.audit, aws.aft_management, aws.tf_backend_secondary_region] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "aws_partition" "current" {} 5 | data "aws_region" "aft_management" {} 6 | data "aws_caller_identity" "aft_management" {} 7 | data "aws_iam_policy" "AWSLambdaBasicExecutionRole" { 8 | name = "AWSLambdaBasicExecutionRole" 9 | } 10 | 11 | data "aws_iam_policy" "AWSLambdaVPCAccessExecutionRole" { 12 | name = "AWSLambdaVPCAccessExecutionRole" 13 | } 14 | -------------------------------------------------------------------------------- /sources/aft-customizations-common/templates/customizations_pipeline/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | terraform { 5 | required_version = ">= {{ tf_version }}" 6 | backend "s3" { 7 | region = "{{ region }}" 8 | bucket = "{{ bucket }}" 9 | key = "{{ key }}" 10 | dynamodb_table = "{{ dynamodb_table }}" 11 | encrypt = "true" 12 | kms_key_id = "{{ kms_key_id }}" 13 | profile = "aft-management" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest ideas or enhancements for AFT. 4 | labels: enhancement 5 | --- 6 | 7 | **Describe the outcome you'd like** 8 | 9 | A clear and concise description of what you want to happen. 10 | 11 | **Is your feature request related to a problem you are currently experiencing? If so, please describe.** 12 | 13 | A clear and concise description of what the problem is. 14 | 15 | **Additional context** 16 | 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /examples/codecommit+tf_oss/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "aft" { 5 | source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" 6 | # Required Vars 7 | ct_management_account_id = "111122223333" 8 | log_archive_account_id = "444455556666" 9 | audit_account_id = "123456789012" 10 | aft_management_account_id = "777788889999" 11 | ct_home_region = "us-east-1" 12 | tf_backend_secondary_region = "us-west-2" 13 | } 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Contributing to the AWS Control Tower Account Factory for Terraform 2 | 3 | Thank you for your interest in contributing to the AWS Control Tower Account Factory for Terraform. 4 | 5 | At this time, we are not accepting contributions. If contributions are accepted in the future, the AWS Control Tower Account Factory for Terraform is released under the [Apache license](http://aws.amazon.com/apache2.0/) and any code submitted will be released under that license. 6 | 7 | If you have a feature request, please create an issue using the Feature Request template, thanks! 8 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "state_machine_arn" { 5 | value = aws_sfn_state_machine.aft_account_provisioning_framework_sfn.arn 6 | } 7 | 8 | output "create_role_function_arn" { 9 | value = aws_lambda_function.create_role.arn 10 | } 11 | output "tag_account_function_arn" { 12 | value = aws_lambda_function.tag_account.arn 13 | } 14 | output "persist_metadata_function_arn" { 15 | value = aws_lambda_function.persist_metadata.arn 16 | } 17 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "aft_admin_role_arn" { 5 | value = aws_iam_role.aft_admin_role.arn 6 | } 7 | output "ct_management_exec_role_arn" { 8 | value = module.ct_management_exec_role.arn 9 | } 10 | output "log_archive_exec_role_arn" { 11 | value = module.log_archive_exec_role.arn 12 | } 13 | output "audit_exec_role_arn" { 14 | value = module.audit_exec_role.arn 15 | } 16 | output "aft_exec_role_arn" { 17 | value = module.aft_exec_role.arn 18 | } 19 | -------------------------------------------------------------------------------- /modules/aft-backend/s3/bucket-policies/aft_backend_bucket.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "AllowSSLRequestsOnly", 6 | "Action": "s3:*", 7 | "Effect": "Deny", 8 | "Resource": [ 9 | "${aws_s3_bucket_backend_arn}", 10 | "${aws_s3_bucket_backend_arn}/*" 11 | ], 12 | "Condition": { 13 | "Bool": { 14 | "aws:SecureTransport": "false" 15 | } 16 | }, 17 | "Principal": "*" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/iam/role-policies/ct_aft_cwe_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "codepipeline:StartPipelineExecution" 8 | ], 9 | "Resource": [ 10 | "arn:${data_aws_partition_current_partition}:codepipeline:${region}:${account_id}:${account_request_pipeline_name}", 11 | "arn:${data_aws_partition_current_partition}:codepipeline:${region}:${account_id}:${provisioning_customizations_pipeline_name}" 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /examples/multiple-regions-customization/multiple-regions/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | resource "aws_ssm_parameter" "param-us-east-2" { 6 | name = "/aft/example/region" 7 | type = "String" 8 | value = "us-east-2" 9 | 10 | # Declare the custom provider using the alias 11 | provider = aws.us_east_2 12 | } 13 | 14 | resource "aws_ssm_parameter" "param-us-west-1" { 15 | name = "/aft/example/region" 16 | type = "String" 17 | value = "us-west-1" 18 | 19 | # Declare the custom provider using the alias 20 | provider = aws.us_west_1 21 | } 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting Security Issues 2 | 3 | Amazon Web Services (AWS) is dedicated to the responsible disclosure of security vulnerabilities. 4 | 5 | We kindly ask that you **do not** open a public GitHub issue to report security concerns. 6 | 7 | Instead, please submit the issue to the AWS Vulnerability Disclosure Program via [HackerOne](https://hackerone.com/aws_vdp) or send your report via [email](mailto:aws-security@amazon.com). 8 | 9 | For more details, visit the [AWS Vulnerability Reporting Page](http://aws.amazon.com/security/vulnerability-reporting/). 10 | 11 | Thank you in advance for collaborating with us to help protect our customers. 12 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/sns.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_sns_topic" "aft_notifications" { 5 | name = "aft-notifications" 6 | #tfsec:ignore:aws-sns-topic-encryption-use-cmk 7 | kms_master_key_id = var.sns_topic_enable_cmk_encryption ? aws_kms_key.aft.id : "alias/aws/sns" 8 | } 9 | 10 | resource "aws_sns_topic" "aft_failure_notifications" { 11 | name = "aft-failure-notifications" 12 | #tfsec:ignore:aws-sns-topic-encryption-use-cmk 13 | kms_master_key_id = var.sns_topic_enable_cmk_encryption ? aws_kms_key.aft.id : "alias/aws/sns" 14 | } 15 | -------------------------------------------------------------------------------- /modules/aft-customizations/s3/bucket-policies/aft_codepipeline_customizations_bucket.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "AllowSSLRequestsOnly", 6 | "Action": "s3:*", 7 | "Effect": "Deny", 8 | "Resource": [ 9 | "${aft_codepipeline_customizations_bucket_arn}", 10 | "${aft_codepipeline_customizations_bucket_arn}/*" 11 | ], 12 | "Condition": { 13 | "Bool": { 14 | "aws:SecureTransport": "false" 15 | } 16 | }, 17 | "Principal": "*" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "random_string" "resource_suffix" { 5 | length = "8" 6 | lower = true 7 | upper = false 8 | special = false 9 | } 10 | 11 | 12 | resource "aws_lambda_layer_version" "layer_version" { 13 | lifecycle { 14 | create_before_destroy = true 15 | } 16 | 17 | depends_on = [data.aws_lambda_invocation.trigger_codebuild_job] 18 | 19 | layer_name = "${var.lambda_layer_name}-${replace(var.aft_version, ".", "-")}" 20 | compatible_runtimes = ["python${var.lambda_layer_python_version}"] 21 | s3_bucket = var.s3_bucket_name 22 | s3_key = "layer.zip" 23 | } 24 | -------------------------------------------------------------------------------- /modules/aft-feature-options/kms.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_kms_key" "aft_log_key" { 5 | provider = aws.log_archive 6 | description = "KMS key for encrypt/decrypt log files" 7 | enable_key_rotation = "true" 8 | policy = templatefile("${path.module}/kms/key-policies/log-key.tpl", { 9 | log_archive_account_id = var.log_archive_account_id 10 | data_aws_partition_current_partition = data.aws_partition.current.partition 11 | }) 12 | } 13 | 14 | resource "aws_kms_alias" "aft_log_key_alias" { 15 | provider = aws.log_archive 16 | 17 | name = "alias/aft" 18 | target_key_id = aws_kms_key.aft_log_key.key_id 19 | } 20 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/aft_common/aft_types.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | from typing import Any, Dict, Literal, TypedDict 5 | 6 | 7 | class AftAccountInfo(TypedDict): 8 | id: str 9 | email: str 10 | name: str 11 | joined_method: str 12 | joined_date: str 13 | status: str 14 | parent_id: str 15 | parent_type: str 16 | type: Literal["account"] 17 | vendor: Literal["aws"] 18 | 19 | 20 | class AftInvokeAccountCustomizationPayload(TypedDict): 21 | account_info: Dict[Literal["account"], AftAccountInfo] 22 | account_request: Dict[str, Any] 23 | control_tower_event: Dict[str, Any] 24 | account_provisioning: Dict[str, Any] 25 | customization_request_id: str 26 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_iam_role" "aft_states" { 5 | name = "aft-account-provisioning-customizations-role" 6 | assume_role_policy = templatefile("${path.module}/iam/trust-policies/states.tpl", { none = "none" }) 7 | } 8 | 9 | resource "aws_iam_role_policy" "aft_states" { 10 | name = "aft-account-provisioning-customizations-policy" 11 | role = aws_iam_role.aft_states.id 12 | 13 | policy = templatefile("${path.module}/iam/role-policies/iam-aft-states.tpl", { 14 | account_provisioning_customizations_sfn_arn = aws_sfn_state_machine.aft_account_provisioning_customizations.arn 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /examples/multiple-regions-customization/multiple-regions/terraform/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | {% if tf_distribution_type == "oss" -%} 5 | terraform { 6 | required_version = ">= {{ tf_version }}" 7 | backend "s3" { 8 | region = "{{ region }}" 9 | bucket = "{{ bucket }}" 10 | key = "{{ key }}" 11 | dynamodb_table = "{{ dynamodb_table }}" 12 | encrypt = "true" 13 | kms_key_id = "{{ kms_key_id }}" 14 | role_arn = "{{ aft_admin_role_arn }}" 15 | } 16 | } 17 | {% else -%} 18 | terraform { 19 | backend "remote" { 20 | organization = "{{ terraform_org_name }}" 21 | workspaces { 22 | name = "{{ terraform_workspace_name }}" 23 | } 24 | } 25 | } 26 | {% endif %} 27 | -------------------------------------------------------------------------------- /data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "local_file" "version" { 5 | filename = "${path.module}/VERSION" 6 | } 7 | 8 | data "local_file" "python_version" { 9 | filename = "${path.module}/PYTHON_VERSION" 10 | } 11 | 12 | data "aws_service" "home_region_validation" { 13 | service_id = "controltower" 14 | lifecycle { 15 | precondition { 16 | condition = try(contains(local.service_catalog_regional_availability.values, var.ct_home_region), true) == true 17 | error_message = "AFT is not supported on Control Tower home region ${var.ct_home_region}. Refer to https://docs.aws.amazon.com/controltower/latest/userguide/limits.html for more information." 18 | } 19 | } 20 | } 21 | 22 | data "aws_partition" "current" { 23 | } 24 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-dev/terraform/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | {% if tf_distribution_type == "oss" -%} 5 | terraform { 6 | required_version = ">= {{ tf_version }}" 7 | backend "s3" { 8 | region = "{{ region }}" 9 | bucket = "{{ bucket }}" 10 | key = "{{ key }}" 11 | dynamodb_table = "{{ dynamodb_table }}" 12 | encrypt = "true" 13 | kms_key_id = "{{ kms_key_id }}" 14 | role_arn = "{{ aft_admin_role_arn }}" 15 | } 16 | } 17 | {% else -%} 18 | terraform { 19 | backend "remote" { 20 | organization = "{{ terraform_org_name }}" 21 | workspaces { 22 | name = "{{ terraform_workspace_name }}" 23 | } 24 | } 25 | } 26 | {% endif %} 27 | -------------------------------------------------------------------------------- /examples/multiple-account-customizations/account-customization-prod/terraform/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | {% if tf_distribution_type == "oss" -%} 5 | terraform { 6 | required_version = ">= {{ tf_version }}" 7 | backend "s3" { 8 | region = "{{ region }}" 9 | bucket = "{{ bucket }}" 10 | key = "{{ key }}" 11 | dynamodb_table = "{{ dynamodb_table }}" 12 | encrypt = "true" 13 | kms_key_id = "{{ kms_key_id }}" 14 | role_arn = "{{ aft_admin_role_arn }}" 15 | } 16 | } 17 | {% else -%} 18 | terraform { 19 | backend "remote" { 20 | organization = "{{ terraform_org_name }}" 21 | workspaces { 22 | name = "{{ terraform_workspace_name }}" 23 | } 24 | } 25 | } 26 | {% endif %} 27 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-request/terraform/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | {% if tf_distribution_type == "oss" -%} 5 | terraform { 6 | required_version = ">= {{ tf_version }}" 7 | backend "s3" { 8 | region = "{{ region }}" 9 | bucket = "{{ bucket }}" 10 | key = "{{ key }}" 11 | dynamodb_table = "{{ dynamodb_table }}" 12 | encrypt = "true" 13 | kms_key_id = "{{ kms_key_id }}" 14 | assume_role = { 15 | role_arn = "{{ aft_admin_role_arn }}" 16 | } 17 | } 18 | } 19 | {% else -%} 20 | terraform { 21 | backend "remote" { 22 | organization = "{{ terraform_org_name }}" 23 | workspaces { 24 | name = "{{ terraform_workspace_name }}" 25 | } 26 | } 27 | } 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-global-customizations/terraform/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | {% if tf_distribution_type == "oss" -%} 5 | terraform { 6 | required_version = ">= {{ tf_version }}" 7 | backend "s3" { 8 | region = "{{ region }}" 9 | bucket = "{{ bucket }}" 10 | key = "{{ key }}" 11 | dynamodb_table = "{{ dynamodb_table }}" 12 | encrypt = "true" 13 | kms_key_id = "{{ kms_key_id }}" 14 | assume_role = { 15 | role_arn = "{{ aft_admin_role_arn }}" 16 | } 17 | } 18 | } 19 | {% else -%} 20 | terraform { 21 | backend "remote" { 22 | organization = "{{ terraform_org_name }}" 23 | workspaces { 24 | name = "{{ terraform_workspace_name }}" 25 | } 26 | } 27 | } 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | {% if tf_distribution_type == "oss" -%} 5 | terraform { 6 | required_version = ">= {{ tf_version }}" 7 | backend "s3" { 8 | region = "{{ region }}" 9 | bucket = "{{ bucket }}" 10 | key = "{{ key }}" 11 | dynamodb_table = "{{ dynamodb_table }}" 12 | encrypt = "true" 13 | kms_key_id = "{{ kms_key_id }}" 14 | assume_role = { 15 | role_arn = "{{ aft_admin_role_arn }}" 16 | } 17 | } 18 | } 19 | {% else -%} 20 | terraform { 21 | backend "remote" { 22 | organization = "{{ terraform_org_name }}" 23 | workspaces { 24 | name = "{{ terraform_workspace_name }}" 25 | } 26 | } 27 | } 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/terraform/backend.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated backend.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | {% if tf_distribution_type == "oss" -%} 5 | terraform { 6 | required_version = ">= {{ tf_version }}" 7 | backend "s3" { 8 | region = "{{ region }}" 9 | bucket = "{{ bucket }}" 10 | key = "{{ key }}" 11 | dynamodb_table = "{{ dynamodb_table }}" 12 | encrypt = "true" 13 | kms_key_id = "{{ kms_key_id }}" 14 | assume_role = { 15 | role_arn = "{{ aft_admin_role_arn }}" 16 | } 17 | } 18 | } 19 | {% else -%} 20 | terraform { 21 | backend "remote" { 22 | organization = "{{ terraform_org_name }}" 23 | workspaces { 24 | name = "{{ terraform_workspace_name }}" 25 | } 26 | } 27 | } 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/aftService-gitlab-permissions.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "MinimumGitLabPushMirroringPermissions", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "codecommit:GitPull", 9 | "codecommit:GitPush" 10 | ], 11 | "Resource": [ 12 | "arn:${data_aws_partition_current_partition}:codecommit:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:terraform-aws-aft-pipeline-framework", 13 | "arn:${data_aws_partition_current_partition}:codecommit:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:terraform-aws-aft-pipeline-*" 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/sqs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_sqs_queue" "aft_account_request" { 5 | name = "aft-account-request.fifo" 6 | fifo_queue = true 7 | kms_master_key_id = aws_kms_alias.aft.name 8 | visibility_timeout_seconds = 240 9 | kms_data_key_reuse_period_seconds = 300 10 | redrive_policy = jsonencode({ 11 | deadLetterTargetArn = aws_sqs_queue.aft_account_request_dlq.arn 12 | maxReceiveCount = 1 13 | }) 14 | } 15 | 16 | resource "aws_sqs_queue" "aft_account_request_dlq" { 17 | name = "aft-account-request-dlq.fifo" 18 | fifo_queue = true 19 | kms_master_key_id = aws_kms_alias.aft.name 20 | kms_data_key_reuse_period_seconds = 300 21 | } 22 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/aft-states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "states:StartExecution", 7 | "Resource": "${aft_account_provisioning_framework_sfn_arn}" 8 | }, 9 | { 10 | "Effect": "Allow", 11 | "Action": [ 12 | "lambda:InvokeFunction" 13 | ], 14 | "Resource": [ 15 | "arn:${data_aws_partition_current_partition}:lambda:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:function:aft_*" 16 | ] 17 | }, 18 | { 19 | "Effect": "Allow", 20 | "Action": [ 21 | "states:DescribeExecution" 22 | ], 23 | "Resource": "${aft_account_provisioning_customizations_sfn_name}" 24 | } 25 | 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /examples/github+tf_oss/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "aft" { 5 | source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" 6 | # Required Vars 7 | ct_management_account_id = "111122223333" 8 | log_archive_account_id = "444455556666" 9 | audit_account_id = "123456789012" 10 | aft_management_account_id = "777788889999" 11 | ct_home_region = "us-east-1" 12 | tf_backend_secondary_region = "us-west-2" 13 | # VCS Vars 14 | vcs_provider = "github" 15 | account_request_repo_name = "ExampleOrg/example-repo-1" 16 | global_customizations_repo_name = "ExampleOrg/example-repo-2" 17 | account_customizations_repo_name = "ExampleOrg/example-repo-3" 18 | account_provisioning_customizations_repo_name = "ExampleOrg/example-repo-4" 19 | } 20 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/iam/role-policies/codebuild-trigger.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Action": [ 6 | "codebuild:BatchGetBuilds", 7 | "codebuild:StartBuild" 8 | ], 9 | "Effect": "Allow", 10 | "Resource": "arn:${data_aws_partition_current_partition}:codebuild:${aws_region}:${account_id}:project/${codebuild_project_name}" 11 | }, 12 | { 13 | "Action": [ 14 | "logs:CreateLogGroup" 15 | ], 16 | "Effect": "Allow", 17 | "Resource": "arn:${data_aws_partition_current_partition}:logs:${aws_region}:${account_id}:*" 18 | }, 19 | { 20 | "Action": [ 21 | "logs:CreateLogGroup", 22 | "logs:PutLogEvents" 23 | ], 24 | "Effect": "Allow", 25 | "Resource": "arn:${data_aws_partition_current_partition}:logs:${aws_region}:${account_id}:log-group:/aws/lambda/${codebuild_trigger_function_name}:*" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /examples/gitlab+tf_oss/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "aft" { 5 | source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" 6 | # Required Vars 7 | ct_management_account_id = "111122223333" 8 | log_archive_account_id = "444455556666" 9 | audit_account_id = "123456789012" 10 | aft_management_account_id = "777788889999" 11 | ct_home_region = "us-east-1" 12 | tf_backend_secondary_region = "us-west-2" 13 | # VCS Vars 14 | vcs_provider = "gitlab" 15 | account_request_repo_name = "ExampleProject/example-repo-1" 16 | global_customizations_repo_name = "ExampleProject/example-repo-2" 17 | account_customizations_repo_name = "ExampleProject/example-repo-3" 18 | account_provisioning_customizations_repo_name = "ExampleProject/example-repo-4" 19 | } 20 | -------------------------------------------------------------------------------- /modules/aft-customizations/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "aft_customizations_identify_targets_function_arn" { 5 | value = aws_lambda_function.aft_customizations_identify_targets.arn 6 | } 7 | 8 | output "aft_customizations_execute_pipeline_function_arn" { 9 | value = aws_lambda_function.aft_customizations_execute_pipeline.arn 10 | } 11 | 12 | output "aft_customizations_get_pipeline_executions_function_arn" { 13 | value = aws_lambda_function.aft_customizations_get_pipeline_executions.arn 14 | } 15 | 16 | output "aft_codepipeline_customizations_bucket_name" { 17 | value = aws_s3_bucket.aft_codepipeline_customizations_bucket.id 18 | } 19 | 20 | output "aft_codepipeline_customizations_bucket_arn" { 21 | value = aws_s3_bucket.aft_codepipeline_customizations_bucket.arn 22 | } 23 | 24 | output "state_machine_arn" { 25 | value = aws_sfn_state_machine.aft_invoke_customizations_sfn.arn 26 | } 27 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/aft_common/s3.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import json 5 | import logging 6 | from typing import TYPE_CHECKING, List 7 | 8 | from boto3.session import Session 9 | 10 | logger = logging.getLogger("aft") 11 | 12 | if TYPE_CHECKING: 13 | from aft_common.aft_types import AftInvokeAccountCustomizationPayload 14 | else: 15 | AftInvokeAccountCustomizationPayload = object 16 | 17 | 18 | def put_target_account_info( 19 | session: Session, 20 | bucket: str, 21 | key: str, 22 | target_account_info: List[AftInvokeAccountCustomizationPayload], 23 | ) -> None: 24 | 25 | s3 = session.resource("s3") 26 | json_data = json.dumps(target_account_info) 27 | 28 | logger.info(f"Uploading target account info to s3://{bucket}/{key}") 29 | s3.Object(bucket, key).put(Body=json_data) 30 | logger.info(f"Successfully uploaded target account info to s3://{bucket}/{key}") 31 | -------------------------------------------------------------------------------- /examples/multiple-regions-customization/multiple-regions/terraform/aft-providers.jinja: -------------------------------------------------------------------------------- 1 | ## Auto generated providers.tf ## 2 | ## Updated on: {{ timestamp }} ## 3 | 4 | provider "aws" { 5 | region = "{{ provider_region }}" 6 | assume_role { 7 | role_arn = "{{ target_admin_role_arn }}" 8 | } 9 | default_tags { 10 | tags = { 11 | managed_by = "AFT" 12 | } 13 | } 14 | } 15 | 16 | # Custom provider for 'us-east-2' region 17 | provider "aws" { 18 | alias = "us_east_2" 19 | region = "us-east-2" 20 | assume_role { 21 | role_arn = "{{ target_admin_role_arn }}" 22 | } 23 | default_tags { 24 | tags = { 25 | managed_by = "AFT" 26 | } 27 | } 28 | } 29 | 30 | # Custom provider for 'us-west-1' region 31 | provider "aws" { 32 | alias = "us_west_1" 33 | region = "us-west-1" 34 | assume_role { 35 | role_arn = "{{ target_admin_role_arn }}" 36 | } 37 | default_tags { 38 | tags = { 39 | managed_by = "AFT" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /modules/aft-customizations/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "aws_partition" "current" {} 5 | 6 | data "aws_region" "current" {} 7 | 8 | data "aws_caller_identity" "current" {} 9 | 10 | data "aws_region" "aft_management" {} 11 | data "aws_caller_identity" "aft_management" {} 12 | 13 | data "aws_iam_policy" "AWSLambdaBasicExecutionRole" { 14 | name = "AWSLambdaBasicExecutionRole" 15 | } 16 | 17 | data "aws_iam_policy" "AWSLambdaVPCAccessExecutionRole" { 18 | name = "AWSLambdaVPCAccessExecutionRole" 19 | } 20 | 21 | data "local_file" "aft_global_customizations_terraform" { 22 | filename = "${path.module}/buildspecs/aft-global-customizations-terraform.yml" 23 | } 24 | 25 | data "local_file" "aft_account_customizations_terraform" { 26 | filename = "${path.module}/buildspecs/aft-account-customizations-terraform.yml" 27 | } 28 | 29 | data "local_file" "aft_create_pipeline" { 30 | filename = "${path.module}/buildspecs/aft-create-pipeline.yml" 31 | } 32 | -------------------------------------------------------------------------------- /modules/aft-feature-options/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "aws_partition" "current" {} 5 | 6 | data "aws_region" "current" { 7 | provider = aws.aft_management 8 | } 9 | 10 | data "aws_caller_identity" "current" { 11 | provider = aws.aft_management 12 | } 13 | 14 | data "aws_caller_identity" "ct_management" { 15 | provider = aws.ct_management 16 | } 17 | 18 | data "aws_caller_identity" "ct_audit" { 19 | provider = aws.audit 20 | } 21 | 22 | data "aws_caller_identity" "ct_log" { 23 | provider = aws.log_archive 24 | } 25 | 26 | data "aws_iam_policy" "AWSLambdaBasicExecutionRole" { 27 | provider = aws.aft_management 28 | name = "AWSLambdaBasicExecutionRole" 29 | } 30 | 31 | data "aws_iam_policy" "AWSLambdaVPCAccessExecutionRole" { 32 | provider = aws.aft_management 33 | name = "AWSLambdaVPCAccessExecutionRole" 34 | } 35 | 36 | data "aws_organizations_organization" "aft_organization" { 37 | provider = aws.ct_management 38 | } 39 | -------------------------------------------------------------------------------- /modules/aft-feature-options/states.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | state_machine_source = "${path.module}/states/aft_features.asl.json" 6 | replacements_map = { 7 | current_partition = data.aws_partition.current.partition 8 | aft_delete_default_vpc_function_arn = aws_lambda_function.aft_delete_default_vpc.arn 9 | aft_enroll_support_function_arn = aws_lambda_function.aft_enroll_support.arn 10 | aft_enable_cloudtrail_function_arn = aws_lambda_function.aft_enable_cloudtrail.arn 11 | aft_notification_arn = var.aft_sns_topic_arn 12 | aft_failure_notification_arn = var.aft_failure_sns_topic_arn 13 | } 14 | } 15 | 16 | resource "aws_sfn_state_machine" "aft_features" { 17 | provider = aws.aft_management 18 | name = var.aft_features_sfn_name 19 | role_arn = aws_iam_role.aft_features_sfn.arn 20 | definition = templatefile(local.state_machine_source, local.replacements_map) 21 | } 22 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/admin-role/iam.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "trusted_entity_type" { 5 | default = "AWS" 6 | } 7 | 8 | variable "role_name" { 9 | default = "AWSAFTExecution" 10 | } 11 | 12 | variable "trusted_entity" { 13 | 14 | } 15 | variable "aft_admin_session_arn" { 16 | 17 | } 18 | 19 | resource "aws_iam_role" "role" { 20 | name = var.role_name 21 | 22 | assume_role_policy = templatefile("${path.module}/trust_policy.tpl", 23 | { 24 | trusted_entity_type = var.trusted_entity_type 25 | trusted_entity = var.trusted_entity 26 | aft_admin_assumed_role_arn = var.aft_admin_session_arn 27 | 28 | } 29 | ) 30 | } 31 | 32 | resource "aws_iam_role_policy_attachment" "administrator-access-attachment" { 33 | role = aws_iam_role.role.name 34 | policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AdministratorAccess" 35 | } 36 | 37 | output "arn" { 38 | value = aws_iam_role.role.arn 39 | } 40 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/aft-sts-assume.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "sts:AssumeRole", 7 | "Resource": "arn:${data_aws_partition_current_partition}:iam::*:role${var_aft_role_execution_path}${var_aft_role_execution_name}" 8 | }, 9 | { 10 | "Effect": "Allow", 11 | "Action": "logs:CreateLogGroup", 12 | "Resource": "arn:${data_aws_partition_current_partition}:logs:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:*" 13 | }, 14 | { 15 | "Effect": "Allow", 16 | "Action": [ 17 | "logs:CreateLogStream", 18 | "logs:PutLogEvents" 19 | ], 20 | "Resource": [ 21 | "arn:${data_aws_partition_current_partition}:logs:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:log-group:/aws/lambda/*:*" 22 | ] 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /modules/aft-feature-options/kms/key-policies/log-key.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "Allow AWS Services to Encrypt data", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": [ 9 | "vpc-flow-logs.amazonaws.com", 10 | "delivery.logs.amazonaws.com", 11 | "cloudtrail.amazonaws.com" 12 | ] 13 | }, 14 | "Action": [ 15 | "kms:ReEncrypt*", 16 | "kms:GenerateDataKey*", 17 | "kms:Encrypt", 18 | "kms:DescribeKey", 19 | "kms:Decrypt" 20 | ], 21 | "Resource": "*" 22 | }, 23 | { 24 | "Sid": "Enable IAM User Permissions", 25 | "Effect": "Allow", 26 | "Principal": { 27 | "AWS": "arn:${data_aws_partition_current_partition}:iam::${log_archive_account_id}:root" 28 | }, 29 | "Action": "kms:*", 30 | "Resource": "*" 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug/Issue 3 | about: Use this to report bugs with AFT. 4 | labels: bug, pending investigation 5 | --- 6 | 7 | **Terraform Version & Prov:** 8 | 9 | **AFT Version:** 10 | (Can be found in the AFT Management Account in the SSM Parameter `/aft/config/aft/version`) 11 | 12 | **Terraform Version & Provider Versions** 13 | Please provide the outputs of `terraform version` and `terraform providers` from within your AFT environment 14 | 15 | `terraform version` 16 | ``` 17 | {Replace me} 18 | ``` 19 | 20 | `terraform providers` 21 | ``` 22 | {Replace me} 23 | ``` 24 | 25 | **Bug Description** 26 | A clear and concise description of what the bug is. 27 | 28 | **To Reproduce** 29 | Steps to reproduce the behavior: 30 | 1. Go to '...' 31 | 2. Click on '....' 32 | 3. Scroll down to '....' 33 | 4. See error 34 | 35 | **Expected behavior** 36 | A clear and concise description of what you expected to happen. 37 | 38 | **Related Logs** 39 | Provide any related logs or error messages to help explain your problem. 40 | 41 | **Additional context** 42 | Add any other context about the problem here. 43 | -------------------------------------------------------------------------------- /modules/aft-feature-options/iam/role-policies/aft_features_states.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "sns:Publish*", 7 | "Resource": "arn:${data_aws_partition_current_partition}:sns:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:aft-*" 8 | }%{ if sns_topic_enable_cmk_encryption }, 9 | { 10 | "Effect": "Allow", 11 | "Action": [ 12 | "kms:GenerateDataKey*", 13 | "kms:Encrypt", 14 | "kms:Decrypt" 15 | ], 16 | "Resource": [ 17 | "${aws_kms_key_aft_arn}" 18 | ] 19 | }%{ endif }, 20 | { 21 | "Effect": "Allow", 22 | "Action": [ 23 | "lambda:InvokeFunction" 24 | ], 25 | "Resource": [ 26 | "arn:${data_aws_partition_current_partition}:lambda:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:function:aft-*" 27 | ] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /examples/bitbucket+tf_enterprise/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "aft" { 5 | source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" 6 | # Required Vars 7 | ct_management_account_id = "111122223333" 8 | log_archive_account_id = "444455556666" 9 | audit_account_id = "123456789012" 10 | aft_management_account_id = "777788889999" 11 | ct_home_region = "us-east-1" 12 | tf_backend_secondary_region = "us-west-2" 13 | # VCS Vars 14 | vcs_provider = "bitbucket" 15 | account_request_repo_name = "ExampleOrg/example-repo-1" 16 | global_customizations_repo_name = "ExampleOrg/example-repo-2" 17 | account_customizations_repo_name = "ExampleOrg/example-repo-3" 18 | account_provisioning_customizations_repo_name = "ExampleOrg/example-repo-4" 19 | # TF Vars 20 | terraform_distribution = "tfe" 21 | terraform_api_endpoint = "https://terraform.example.com/api/v2/" 22 | terraform_token = "EXAMPLE-uoc1c1qsw7poexampleewjeno1pte3rw" 23 | terraform_org_name = "ExampleOrg" 24 | } 25 | -------------------------------------------------------------------------------- /examples/githubenterprise+tf_cloud/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "aft" { 5 | source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" 6 | # Required Vars 7 | ct_management_account_id = "111122223333" 8 | log_archive_account_id = "444455556666" 9 | audit_account_id = "123456789012" 10 | aft_management_account_id = "777788889999" 11 | ct_home_region = "us-east-1" 12 | tf_backend_secondary_region = "us-west-2" 13 | # VCS Vars 14 | vcs_provider = "githubenterprise" 15 | github_enterprise_url = "https://github.example.com" 16 | account_request_repo_name = "ExampleOrg/example-repo-1" 17 | global_customizations_repo_name = "ExampleOrg/example-repo-2" 18 | account_customizations_repo_name = "ExampleOrg/example-repo-3" 19 | account_provisioning_customizations_repo_name = "ExampleOrg/example-repo-4" 20 | # TF Vars 21 | terraform_distribution = "tfc" 22 | terraform_token = "EXAMPLE-uoc1c1qsw7poexampleewjeno1pte3rw" 23 | terraform_org_name = "ExampleOrg" 24 | } 25 | -------------------------------------------------------------------------------- /modules/aft-iam-roles/service-role/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | terraform { 6 | required_providers { 7 | aws = { 8 | source = "hashicorp/aws" 9 | version = ">= 5.11.0, < 6.0.0" 10 | } 11 | } 12 | } 13 | variable "trusted_entity_type" { 14 | default = "AWS" 15 | } 16 | 17 | variable "role_name" { 18 | default = "AWSAFTService" 19 | } 20 | 21 | variable "trusted_entity" { 22 | 23 | } 24 | variable "aft_admin_session_arn" { 25 | 26 | } 27 | 28 | resource "aws_iam_role" "role" { 29 | name = var.role_name 30 | 31 | assume_role_policy = templatefile("${path.module}/trust_policy.tpl", 32 | { 33 | trusted_entity_type = var.trusted_entity_type 34 | trusted_entity = var.trusted_entity 35 | aft_admin_assumed_role_arn = var.aft_admin_session_arn 36 | 37 | } 38 | ) 39 | } 40 | 41 | resource "aws_iam_role_policy_attachment" "administrator-access-attachment" { 42 | role = aws_iam_role.role.name 43 | policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AdministratorAccess" 44 | } 45 | 46 | output "arn" { 47 | value = aws_iam_role.role.arn 48 | } 49 | -------------------------------------------------------------------------------- /examples/gitlabselfmanaged+tf_cloud/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "aft" { 5 | source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" 6 | # Required Vars 7 | ct_management_account_id = "111122223333" 8 | log_archive_account_id = "444455556666" 9 | audit_account_id = "123456789012" 10 | aft_management_account_id = "777788889999" 11 | ct_home_region = "us-east-1" 12 | tf_backend_secondary_region = "us-west-2" 13 | # VCS Vars 14 | vcs_provider = "gitlabselfmanaged" 15 | gitlab_selfmanaged_url = "https://gitlab.example.com" 16 | account_request_repo_name = "ExampleProject/example-repo-1" 17 | global_customizations_repo_name = "ExampleProject/example-repo-2" 18 | account_customizations_repo_name = "ExampleProject/example-repo-3" 19 | account_provisioning_customizations_repo_name = "ExampleProject/example-repo-4" 20 | # TF Vars 21 | terraform_distribution = "tfc" 22 | terraform_token = "EXAMPLE-uoc1c1qsw7poexampleewjeno1pte3rw" 23 | terraform_org_name = "ExampleOrg" 24 | } 25 | -------------------------------------------------------------------------------- /modules/aft-archives/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "provisioning_framework_archive_path" { 5 | value = data.archive_file.provisioning_framework.output_path 6 | } 7 | 8 | output "provisioning_framework_archive_hash" { 9 | value = data.archive_file.provisioning_framework.output_base64sha256 10 | } 11 | 12 | output "request_framework_archive_path" { 13 | value = data.archive_file.request_framework.output_path 14 | } 15 | 16 | output "request_framework_archive_hash" { 17 | value = data.archive_file.request_framework.output_base64sha256 18 | } 19 | 20 | output "customizations_archive_path" { 21 | value = data.archive_file.customizations.output_path 22 | } 23 | 24 | output "customizations_archive_hash" { 25 | value = data.archive_file.customizations.output_base64sha256 26 | } 27 | 28 | output "feature_options_archive_path" { 29 | value = data.archive_file.feature_options.output_path 30 | } 31 | 32 | output "feature_options_archive_hash" { 33 | value = data.archive_file.feature_options.output_base64sha256 34 | } 35 | 36 | output "builder_archive_path" { 37 | value = data.archive_file.builder.output_path 38 | } 39 | 40 | output "builder_archive_hash" { 41 | value = data.archive_file.builder.output_base64sha256 42 | } 43 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-request/examples/account-request.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | module "sandbox_account_01" { 5 | source = "./modules/aft-account-request" 6 | 7 | control_tower_parameters = { 8 | AccountEmail = "john.doe@amazon.com" 9 | AccountName = "sandbox-account-01" 10 | # Syntax for top-level OU 11 | ManagedOrganizationalUnit = "Sandbox" 12 | # Syntax for nested OU 13 | # ManagedOrganizationalUnit = "Sandbox (ou-xfe5-a8hb8ml8)" 14 | SSOUserEmail = "john.doe@amazon.com" 15 | SSOUserFirstName = "John" 16 | SSOUserLastName = "Doe" 17 | } 18 | 19 | account_tags = { 20 | "ABC:Owner" = "john.doe@amazon.com" 21 | "ABC:Division" = "ENT" 22 | "ABC:Environment" = "Dev" 23 | "ABC:CostCenter" = "123456" 24 | "ABC:Vended" = "true" 25 | "ABC:DivCode" = "102" 26 | "ABC:BUCode" = "ABC003" 27 | "ABC:Project" = "123456" 28 | } 29 | 30 | change_management_parameters = { 31 | change_requested_by = "John Doe" 32 | change_reason = "testing the account vending process" 33 | } 34 | 35 | custom_fields = { 36 | custom1 = "a" 37 | custom2 = "b" 38 | } 39 | 40 | account_customizations_name = "sandbox-customizations" 41 | } 42 | -------------------------------------------------------------------------------- /modules/aft-archives/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "archive_file" "provisioning_framework" { 5 | type = "zip" 6 | source_dir = "${path.module}/../../src/aft_lambda/aft_account_provisioning_framework" 7 | output_path = "${path.module}/../../src/aft_lambda/aft_account_provisioning_framework.zip" 8 | } 9 | 10 | data "archive_file" "request_framework" { 11 | type = "zip" 12 | source_dir = "${path.module}/../../src/aft_lambda/aft_account_request_framework" 13 | output_path = "${path.module}/../../src/aft_lambda/aft_account_request_framework.zip" 14 | } 15 | data "archive_file" "customizations" { 16 | type = "zip" 17 | source_dir = "${path.module}/../../src/aft_lambda/aft_customizations" 18 | output_path = "${path.module}/../../src/aft_lambda/aft_customizations.zip" 19 | } 20 | 21 | data "archive_file" "feature_options" { 22 | type = "zip" 23 | source_dir = "${path.module}/../../src/aft_lambda/aft_feature_options" 24 | output_path = "${path.module}/../../src/aft_lambda/aft_feature_options.zip" 25 | } 26 | 27 | data "archive_file" "builder" { 28 | type = "zip" 29 | source_dir = "${path.module}/../../src/aft_lambda/aft_builder" 30 | output_path = "${path.module}/../../src/aft_lambda/aft_builder.zip" 31 | } 32 | -------------------------------------------------------------------------------- /modules/aft-feature-options/s3/bucket-policies/aft_access_logs.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "Allow PutObject", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": [ 9 | "logging.s3.amazonaws.com" 10 | ] 11 | }, 12 | "Action": "s3:PutObject", 13 | "Resource": [ 14 | "${aws_s3_bucket_aft_access_logs_arn}/*" 15 | ], 16 | "Condition": { 17 | "ArnLike": { 18 | "aws:SourceArn": "${aws_s3_bucket_aft_logging_bucket_arn}" 19 | }, 20 | "StringEquals": { 21 | "aws:SourceAccount": "${log_archive_account_id}" 22 | } 23 | } 24 | }, 25 | { 26 | "Sid": "AllowSSLRequestsOnly", 27 | "Action": "s3:*", 28 | "Effect": "Deny", 29 | "Resource": [ 30 | "${aws_s3_bucket_aft_access_logs_arn}", 31 | "${aws_s3_bucket_aft_access_logs_arn}/*" 32 | ], 33 | "Condition": { 34 | "Bool": { 35 | "aws:SecureTransport": "false" 36 | } 37 | }, 38 | "Principal": "*" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /modules/aft-backend/s3/bucket-policies/aft_access_logs_primary_backend_bucket.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "Allow PutObject", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": [ 9 | "logging.s3.amazonaws.com" 10 | ] 11 | }, 12 | "Action": "s3:PutObject", 13 | "Resource": [ 14 | "${aws_s3_bucket_aft_access_logs_arn}/*" 15 | ], 16 | "Condition": { 17 | "ArnLike": { 18 | "aws:SourceArn": "${aws_s3_bucket_primary_backend_arn}" 19 | }, 20 | "StringEquals": { 21 | "aws:SourceAccount": "${aft_management_account_id}" 22 | } 23 | } 24 | }, 25 | { 26 | "Sid": "AllowSSLRequestsOnly", 27 | "Action": "s3:*", 28 | "Effect": "Deny", 29 | "Resource": [ 30 | "${aws_s3_bucket_aft_access_logs_arn}", 31 | "${aws_s3_bucket_aft_access_logs_arn}/*" 32 | ], 33 | "Condition": { 34 | "Bool": { 35 | "aws:SecureTransport": "false" 36 | } 37 | }, 38 | "Principal": "*" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/locals.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | vcs = { 6 | is_codecommit = lower(var.vcs_provider) == "codecommit" ? true : false 7 | is_bitbucket = lower(var.vcs_provider) == "bitbucket" ? true : false 8 | is_github = lower(var.vcs_provider) == "github" ? true : false 9 | is_github_enterprise = lower(var.vcs_provider) == "githubenterprise" ? true : false 10 | is_gitlab = lower(var.vcs_provider) == "gitlab" ? true : false 11 | is_gitlab_selfmanaged = lower(var.vcs_provider) == "gitlabselfmanaged" ? true : false 12 | } 13 | connection_arn = { 14 | bitbucket = lower(var.vcs_provider) == "bitbucket" ? aws_codeconnections_connection.bitbucket[0].arn : "" 15 | github = lower(var.vcs_provider) == "github" ? aws_codeconnections_connection.github[0].arn : "" 16 | githubenterprise = lower(var.vcs_provider) == "githubenterprise" ? aws_codeconnections_connection.githubenterprise[0].arn : "" 17 | gitlab = lower(var.vcs_provider) == "gitlab" ? aws_codeconnections_connection.gitlab[0].arn : "" 18 | gitlabselfmanaged = lower(var.vcs_provider) == "gitlabselfmanaged" ? aws_codeconnections_connection.gitlabselfmanaged[0].arn : "" 19 | codecommit = "null" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "dynamodb:*Item" 8 | ], 9 | "Resource": [ 10 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:table/${data_aws_dynamo_terraform_oss_backend_table}" 11 | ] 12 | }, 13 | { 14 | "Effect": "Allow", 15 | "Action": [ 16 | "s3:GetObject", 17 | "s3:GetObjectVersion", 18 | "s3:GetBucketVersioning", 19 | "s3:List*", 20 | "s3:PutObjectAcl", 21 | "s3:PutObject" 22 | ], 23 | "Resource": [ 24 | "arn:${data_aws_partition_current_partition}:s3:::${aws_s3_bucket_aft_terraform_oss_backend_bucket_id}", 25 | "arn:${data_aws_partition_current_partition}:s3:::${aws_s3_bucket_aft_terraform_oss_backend_bucket_id}/*" 26 | ] 27 | }, 28 | { 29 | "Effect": "Allow", 30 | "Action": [ 31 | "kms:Decrypt", 32 | "kms:Encrypt", 33 | "kms:GenerateDataKey" 34 | ], 35 | "Resource": "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:key/${aws_s3_bucket_aft_terraform_oss_kms_key_id}" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "dynamodb:*Item" 8 | ], 9 | "Resource": [ 10 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:table/${data_aws_dynamo_terraform_oss_backend_table}" 11 | ] 12 | }, 13 | { 14 | "Effect": "Allow", 15 | "Action": [ 16 | "s3:GetObject", 17 | "s3:GetObjectVersion", 18 | "s3:GetBucketVersioning", 19 | "s3:List*", 20 | "s3:PutObjectAcl", 21 | "s3:PutObject" 22 | ], 23 | "Resource": [ 24 | "arn:${data_aws_partition_current_partition}:s3:::${aws_s3_bucket_aft_terraform_oss_backend_bucket_id}", 25 | "arn:${data_aws_partition_current_partition}:s3:::${aws_s3_bucket_aft_terraform_oss_backend_bucket_id}/*" 26 | ] 27 | }, 28 | { 29 | "Effect": "Allow", 30 | "Action": [ 31 | "kms:Decrypt", 32 | "kms:Encrypt", 33 | "kms:GenerateDataKey" 34 | ], 35 | "Resource": "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:key/${aws_s3_bucket_aft_terraform_oss_kms_key_id}" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/pyproject.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [build-system] 5 | requires = [ 6 | "setuptools", 7 | "wheel", 8 | ] 9 | 10 | 11 | # url="https://github.com/aws-ia/terraform-aws-control_tower_account_factory" 12 | 13 | [project] 14 | name = "aft-common" 15 | authors = [ 16 | {name = "AWS"} 17 | ] 18 | version = "0.2.0" 19 | requires-python = ">=3.12,<4.0.0" 20 | description="Common framework for AWS Control Tower Account Factory for Terraform" 21 | classifiers=[ 22 | "Programming Language :: Python :: 3.12", 23 | "License :: OSI Approved :: MIT License", 24 | "Operating System :: OS Independent", 25 | ] 26 | dependencies = [ 27 | "boto3 == 1.39.3", 28 | "botocore == 1.39.3", 29 | "requests == 2.32.4", 30 | "jsonschema == 4.3.2", 31 | "urllib3 >= 2.6.0" 32 | ] 33 | 34 | 35 | [project.optional-dependencies] 36 | dev = [ 37 | "pytest ~= 8.4.1", 38 | "pytest-cov ~= 6.2.0", 39 | "pytest-subtests ~= 0.14.2", 40 | "black ~= 25.1.0", 41 | "isort ~= 6.0.1", 42 | "pre-commit ~= 4.2.0", 43 | "mypy ~= 1.16.1", 44 | "boto3-stubs[support,stepfunctions, ec2, organizations, servicecatalog, sqs, lambda, sns, sts, cloudtrail, ssm, iam, dynamodb, inspector2, s3] ~= 1.39.0", 45 | "aws_lambda_powertools ~= 3.16.0", 46 | "types-requests == 2.32.4.*", # should match the installed requests version 47 | ] 48 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/role-policies/aft_get_pipeline_status_lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "codepipeline:ListPipelineExecutions", 7 | "Resource": "arn:${data_aws_partition_current_partition}:codepipeline:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*" 8 | }, 9 | { 10 | "Effect": "Allow", 11 | "Action": "codepipeline:ListPipelines", 12 | "Resource": "*" 13 | }, 14 | { 15 | "Effect" : "Allow", 16 | "Action" : [ 17 | "kms:GenerateDataKey", 18 | "kms:Encrypt", 19 | "kms:Decrypt" 20 | ], 21 | "Resource" : [ 22 | "${aws_kms_key_aft_arn}" 23 | ] 24 | }, 25 | { 26 | "Effect" : "Allow", 27 | "Action" : [ 28 | "sns:Publish" 29 | ], 30 | "Resource" : [ 31 | "${aft_sns_topic_arn}", 32 | "${aft_failure_sns_topic_arn}" 33 | ] 34 | }, 35 | { 36 | "Effect" : "Allow", 37 | "Action" : [ 38 | "ssm:GetParameter" 39 | ], 40 | "Resource" : [ 41 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:parameter/aft/*" 42 | 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /modules/aft-customizations/states.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | state_machine_source = "${path.module}/states/invoke_customizations.asl.json" 6 | replacements_map = { 7 | current_partition = data.aws_partition.current.partition 8 | identify_targets_function_arn = aws_lambda_function.aft_customizations_identify_targets.arn 9 | execute_pipeline_function_arn = aws_lambda_function.aft_customizations_execute_pipeline.arn 10 | get_pipeline_executions_function_arn = aws_lambda_function.aft_customizations_get_pipeline_executions.arn 11 | invoke_account_provisioning_sfn_arn = var.invoke_account_provisioning_sfn_arn 12 | maximum_concurrent_customizations = var.maximum_concurrent_customizations 13 | aft_notification_arn = var.aft_sns_topic_arn 14 | aft_failure_notification_arn = var.aft_failure_sns_topic_arn 15 | } 16 | } 17 | 18 | resource "aws_sfn_state_machine" "aft_invoke_customizations_sfn" { 19 | name = "aft-invoke-customizations" 20 | role_arn = aws_iam_role.aft_invoke_customizations_sfn.arn 21 | // Use valid JSON but transform (de-quote) during load to support numeric parameterization 22 | definition = replace( 23 | templatefile("${local.state_machine_source}", local.replacements_map), 24 | "/\"MaxConcurrency\": \"(\\d+)\"/", 25 | "\"MaxConcurrency\": $1" 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /modules/aft-feature-options/iam/role-policies/aft_enroll_support.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : "ssm:GetParameter", 7 | "Resource" : [ 8 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:parameter/aft/*" 9 | ] 10 | }, 11 | { 12 | "Effect" : "Allow", 13 | "Action" : [ 14 | "kms:GenerateDataKey", 15 | "kms:Encrypt", 16 | "kms:Decrypt" 17 | ], 18 | "Resource" : [ 19 | "${aws_kms_key_aft_arn}" 20 | ] 21 | }, 22 | { 23 | "Effect" : "Allow", 24 | "Action" : [ 25 | "sts:AssumeRole" 26 | ], 27 | "Resource" : [ 28 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_current_account_id}:role/AWSAFTAdmin" 29 | ] 30 | }, 31 | { 32 | "Effect" : "Allow", 33 | "Action" : "sts:GetCallerIdentity", 34 | "Resource" : "*" 35 | }, 36 | { 37 | "Effect" : "Allow", 38 | "Action" : [ 39 | "sns:Publish" 40 | ], 41 | "Resource" : [ 42 | "${aws_sns_topic_aft_notifications_arn}", 43 | "${aws_sns_topic_aft_failure_notifications_arn}" 44 | ] 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/backup.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_backup_vault" "aft_controltower_backup_vault" { 5 | name = "aft-controltower-backup-vault" 6 | kms_key_arn = aws_kms_key.aft.arn 7 | } 8 | resource "aws_backup_plan" "aft_controltower_backup_plan" { 9 | name = "aft-controltower-backup-plan" 10 | rule { 11 | rule_name = "aft_controltower_backup_rule" 12 | target_vault_name = aws_backup_vault.aft_controltower_backup_vault.name 13 | schedule = "cron(0 * * * ? *)" 14 | 15 | dynamic "lifecycle" { 16 | for_each = var.backup_recovery_point_retention != null ? [1] : [] 17 | content { 18 | delete_after = var.backup_recovery_point_retention 19 | } 20 | } 21 | 22 | } 23 | } 24 | 25 | resource "aws_backup_selection" "aft_controltower_backup_selection" { 26 | iam_role_arn = aws_iam_role.aft_aws_backup.arn 27 | name = "aft-controltower-backup-selection" 28 | plan_id = aws_backup_plan.aft_controltower_backup_plan.id 29 | resources = [ 30 | aws_dynamodb_table.aft_request_metadata.arn, 31 | aws_dynamodb_table.aft_request.arn, 32 | aws_dynamodb_table.aft_request_audit.arn, 33 | aws_dynamodb_table.aft_controltower_events.arn 34 | ] 35 | 36 | # Explicit workaround due to https://github.com/hashicorp/terraform-provider-aws/issues/22595 37 | condition {} 38 | not_resources = [] 39 | 40 | } 41 | -------------------------------------------------------------------------------- /modules/aft-feature-options/iam/role-policies/aft_enable_cloudtrail.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : "ssm:GetParameter", 7 | "Resource" : [ 8 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:parameter/aft/*" 9 | ] 10 | }, 11 | { 12 | "Effect" : "Allow", 13 | "Action" : [ 14 | "kms:GenerateDataKey*", 15 | "kms:Encrypt", 16 | "kms:Decrypt" 17 | ], 18 | "Resource" : [ 19 | "${aws_kms_key_aft_arn}" 20 | ] 21 | }, 22 | { 23 | "Effect" : "Allow", 24 | "Action" : [ 25 | "sts:AssumeRole" 26 | ], 27 | "Resource" : [ 28 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_current_account_id}:role/AWSAFTAdmin" 29 | ] 30 | }, 31 | { 32 | "Effect" : "Allow", 33 | "Action" : "sts:GetCallerIdentity", 34 | "Resource" : "*" 35 | }, 36 | { 37 | "Effect" : "Allow", 38 | "Action" : [ 39 | "sns:Publish" 40 | ], 41 | "Resource" : [ 42 | "${aws_sns_topic_aft_notifications_arn}", 43 | "${aws_sns_topic_aft_failure_notifications_arn}" 44 | ] 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "account-request-table" { 5 | type = string 6 | description = "name of account-request-table" 7 | default = "aft-request" 8 | } 9 | 10 | variable "account-request-table-hash" { 11 | type = string 12 | description = "name of account-request-table hash key" 13 | default = "id" 14 | } 15 | 16 | variable "control_tower_parameters" { 17 | type = object({ 18 | AccountEmail = string 19 | AccountName = string 20 | ManagedOrganizationalUnit = string 21 | SSOUserEmail = string 22 | SSOUserFirstName = string 23 | SSOUserLastName = string 24 | }) 25 | } 26 | 27 | variable "change_management_parameters" { 28 | type = object({ 29 | change_requested_by = string 30 | change_reason = string 31 | }) 32 | } 33 | 34 | variable "account_tags" { 35 | type = map(any) 36 | description = "map of account-level tags" 37 | default = {} 38 | } 39 | 40 | variable "custom_fields" { 41 | type = map(any) 42 | description = "map of custom fields defined by the customer" 43 | default = {} 44 | } 45 | 46 | variable "account_customizations_name" { 47 | type = string 48 | default = "" 49 | description = "The name of the account customizations to apply" 50 | } 51 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/role-policies/aft_execute_pipeline_lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "codepipeline:StartPipelineExecution", 8 | "codepipeline:ListPipelineExecutions", 9 | "codepipeline:ListPipelines", 10 | "ssm:GetParameter", 11 | "codepipeline:ListTagsForResource" 12 | ], 13 | "Resource": [ 14 | "arn:${data_aws_partition_current_partition}:codepipeline:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*", 15 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:parameter/aft/*" 16 | ] 17 | }, 18 | { 19 | "Effect" : "Allow", 20 | "Action" : [ 21 | "kms:GenerateDataKey", 22 | "kms:Encrypt", 23 | "kms:Decrypt" 24 | ], 25 | "Resource" : [ 26 | "${aws_kms_key_aft_arn}" 27 | ] 28 | }, 29 | { 30 | "Effect": "Allow", 31 | "Action": "sts:GetCallerIdentity", 32 | "Resource": "*" 33 | }, 34 | { 35 | "Effect" : "Allow", 36 | "Action" : [ 37 | "sns:Publish" 38 | ], 39 | "Resource" : [ 40 | "${aft_sns_topic_arn}", 41 | "${aft_failure_sns_topic_arn}" 42 | ] 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/lambda-controltower-event-logger.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version" : "2012-10-17", 3 | "Statement" : [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : [ 7 | "dynamodb:PutItem" 8 | ], 9 | "Resource" : [ 10 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:table/${aws_dynamodb_table_controltower-events_name}" 11 | ] 12 | }, 13 | { 14 | "Effect" : "Allow", 15 | "Action" : "ssm:GetParameter", 16 | "Resource" : [ 17 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:parameter/aft/*" 18 | ] 19 | }, 20 | { 21 | "Effect" : "Allow", 22 | "Action" : [ 23 | "sns:Publish" 24 | ], 25 | "Resource" : [ 26 | "${aws_sns_topic_aft_notifications_arn}", 27 | "${aws_sns_topic_aft_failure_notifications_arn}" 28 | ] 29 | }, 30 | { 31 | "Effect" : "Allow", 32 | "Action" : [ 33 | "kms:GenerateDataKey", 34 | "kms:Encrypt", 35 | "kms:Decrypt" 36 | ], 37 | "Resource" : [ 38 | "${aws_kms_key_aft_arn}", 39 | "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:alias/aws/sns" 40 | ] 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/locals.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] 6 | orgs_endpoint_supported = data.aws_region.aft-management.name == "us-east-1" ? true : false 7 | 8 | vpc_private_route_table_ids = local.vpc_deployment ? try(data.aws_route_tables.aft_private_route_tables[0].ids, []) : [] 9 | vpc_public_route_table_ids = local.vpc_deployment ? try(data.aws_route_tables.aft_public_route_tables[0].ids, []) : [] 10 | vpc_route_table_ids = concat(local.vpc_private_route_table_ids, local.vpc_public_route_table_ids) 11 | 12 | vpc_private_subnet_ids = var.aft_enable_vpc || var.aft_customer_vpc_id != null ? concat(try(tolist([aws_subnet.aft_vpc_private_subnet_01[0].id, aws_subnet.aft_vpc_private_subnet_02[0].id]), []), var.aft_customer_private_subnets) : [] 13 | # public subnets are only applicable when AFT deploys the VPC 14 | vpc_public_subnet_ids = var.aft_enable_vpc && var.aft_customer_vpc_id == null ? tolist([aws_subnet.aft_vpc_public_subnet_01[0].id, aws_subnet.aft_vpc_public_subnet_02[0].id]) : [] 15 | 16 | vpc_cidr = local.vpc_deployment ? data.aws_vpc.aft[0].cidr_block : null 17 | vpc_deployment = var.aft_enable_vpc || var.aft_customer_vpc_id != null ? true : false 18 | vpc_id = var.aft_enable_vpc || var.aft_customer_vpc_id != null ? try(aws_vpc.aft_vpc[0].id, var.aft_customer_vpc_id) : null 19 | } 20 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/codecommit.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_codecommit_repository" "global_customizations" { 5 | count = local.vcs.is_codecommit ? 1 : 0 6 | repository_name = var.global_customizations_repo_name 7 | description = "This repo holds the Global Customizations for the CT-AFT solution" 8 | default_branch = var.global_customizations_repo_branch 9 | } 10 | 11 | resource "aws_codecommit_repository" "account_customizations" { 12 | count = local.vcs.is_codecommit ? 1 : 0 13 | repository_name = var.account_customizations_repo_name 14 | description = "This repo holds the Account Customizations for the CT-AFT solution" 15 | default_branch = var.account_customizations_repo_branch 16 | } 17 | 18 | resource "aws_codecommit_repository" "account_request" { 19 | count = local.vcs.is_codecommit ? 1 : 0 20 | repository_name = var.account_request_repo_name 21 | description = "This repo holds the Account Request Terraform for the CT-AFT solution" 22 | default_branch = var.account_request_repo_branch 23 | } 24 | 25 | resource "aws_codecommit_repository" "account_provisioning_customizations" { 26 | count = local.vcs.is_codecommit ? 1 : 0 27 | repository_name = var.account_provisioning_customizations_repo_name 28 | description = "This repo holds the Account Provisioning Customizations Step Function Terraform Project" 29 | default_branch = var.account_provisioning_customizations_repo_branch 30 | } 31 | -------------------------------------------------------------------------------- /modules/aft-feature-options/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "aws_aft_access_logs_s3_bucket_id" { 5 | value = aws_s3_bucket.aft_access_logs.id 6 | description = "The name of the bucket." 7 | } 8 | 9 | output "aws_aft_access_logs_s3_bucket_arn" { 10 | value = aws_s3_bucket.aft_access_logs.arn 11 | description = "The ARN of the bucket. Will be of format arn::s3:::bucketname." 12 | } 13 | 14 | output "aws_aft_access_logs_s3_bucket_region" { 15 | value = aws_s3_bucket.aft_access_logs.region 16 | description = "The AWS region this bucket resides in." 17 | } 18 | 19 | output "aws_aft_logs_s3_bucket_id" { 20 | value = aws_s3_bucket.aft_logging_bucket.id 21 | description = "The name of the bucket." 22 | } 23 | 24 | output "aws_aft_logs_s3_bucket_arn" { 25 | value = aws_s3_bucket.aft_logging_bucket.arn 26 | description = "The ARN of the bucket. Will be of format arn::s3:::bucketname." 27 | } 28 | 29 | output "aws_aft_logs_s3_bucket_region" { 30 | value = aws_s3_bucket.aft_logging_bucket.region 31 | description = "The AWS region this bucket resides in." 32 | } 33 | 34 | output "aws_aft_log_key_arn" { 35 | value = aws_kms_key.aft_log_key.arn 36 | description = "The ARN of the KMS key used to encrypt contents in the Log bucket" 37 | } 38 | 39 | output "state_machine_arn" { 40 | value = aws_sfn_state_machine.aft_features.arn 41 | description = "The ARN of the Step Functions state machine" 42 | } 43 | -------------------------------------------------------------------------------- /modules/aft-backend/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | output "bucket_id" { 5 | description = "The name of the primary bucket." 6 | value = aws_s3_bucket.primary-backend-bucket.id 7 | } 8 | 9 | output "secondary_bucket_id" { 10 | description = "The name of the secondary bucket." 11 | value = var.secondary_region == "" ? "" : aws_s3_bucket.secondary-backend-bucket[0].id 12 | } 13 | 14 | output "access_logs_bucket_id" { 15 | description = "The name of the access logs bucket for primary bucket." 16 | value = aws_s3_bucket.aft_access_logs_primary_backend_bucket.id 17 | } 18 | 19 | output "table_id" { 20 | description = "The name of the primary table." 21 | value = aws_dynamodb_table.lock-table.id 22 | } 23 | 24 | output "kms_key_id" { 25 | description = "The globally unique identifier for the primary key." 26 | value = aws_kms_key.encrypt-primary-region.key_id 27 | } 28 | 29 | output "kms_key_alias_arn" { 30 | description = "The ARN for the primary key alias." 31 | value = aws_kms_alias.encrypt-alias-primary-region.arn 32 | } 33 | 34 | output "secondary_kms_key_id" { 35 | description = "The globally unique identifier for the secondary key." 36 | value = var.secondary_region == "" ? "" : aws_kms_key.encrypt-secondary-region[0].key_id 37 | } 38 | 39 | output "secondary_kms_key_alias_arn" { 40 | description = "The ARN for the secondary key alias." 41 | value = var.secondary_region == "" ? "" : aws_kms_alias.encrypt-alias-secondary-region[0].arn 42 | } 43 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/lambda-account-request-processor.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version" : "2012-10-17", 3 | "Statement" : [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : [ 7 | "sqs:DeleteMessage", 8 | "sts:AssumeRole", 9 | "sns:Publish", 10 | "sqs:ReceiveMessage" 11 | ], 12 | "Resource" : [ 13 | "${aws_sns_topic_aft_notifications_arn}", 14 | "${aws_sns_topic_aft_failure_notifications_arn}", 15 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_aft-management_account_id}:role/AWSAFTAdmin", 16 | "${aws_sqs_queue_aft_account_request_arn}" 17 | ] 18 | }, 19 | { 20 | "Effect" : "Allow", 21 | "Action" : "sts:GetCallerIdentity", 22 | "Resource" : "*" 23 | }, 24 | { 25 | "Effect" : "Allow", 26 | "Action" : "ssm:GetParameter", 27 | "Resource" : [ 28 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:parameter/aft/*" 29 | ] 30 | }, 31 | { 32 | "Effect" : "Allow", 33 | "Action" : [ 34 | "kms:GenerateDataKey", 35 | "kms:Encrypt", 36 | "kms:Decrypt" 37 | ], 38 | "Resource" : [ 39 | "${aws_kms_key_aft_arn}", 40 | "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:alias/aws/sns" 41 | ] 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_customizations/aft_customizations_get_pipeline_executions.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import inspect 5 | import logging 6 | from typing import TYPE_CHECKING, Any, Dict 7 | 8 | from aft_common import aft_utils as utils 9 | from aft_common import notifications 10 | from aft_common.codepipeline import get_running_pipeline_count, list_pipelines 11 | from aft_common.logger import configure_aft_logger 12 | from boto3.session import Session 13 | 14 | if TYPE_CHECKING: 15 | from aws_lambda_powertools.utilities.typing import LambdaContext 16 | else: 17 | LambdaContext = object 18 | 19 | configure_aft_logger() 20 | logger = logging.getLogger("aft") 21 | 22 | 23 | def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict[str, int]: 24 | session = Session() 25 | try: 26 | pipelines = list_pipelines(session) 27 | running_pipelines = get_running_pipeline_count(session, pipelines) 28 | 29 | return {"running_pipelines": running_pipelines} 30 | 31 | except Exception as error: 32 | notifications.send_lambda_failure_sns_message( 33 | session=session, 34 | message=str(error), 35 | context=context, 36 | subject="Failed to list all AFT account customization pipelines", 37 | ) 38 | message = { 39 | "FILE": __file__.split("/")[-1], 40 | "METHOD": inspect.stack()[0][3], 41 | "EXCEPTION": str(error), 42 | } 43 | logger.exception(message) 44 | raise 45 | -------------------------------------------------------------------------------- /sources/aft-customizations-common/templates/customizations_pipeline/data.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | data "aws_region" "current" {} 5 | 6 | data "aws_caller_identity" "current" {} 7 | 8 | data "aws_ssm_parameter" "aft_vcs_type" { 9 | name = "/aft/config/vcs/provider" 10 | } 11 | 12 | data "aws_ssm_parameter" "codeconnections_connection_arn" { 13 | name = "/aft/config/vcs/codeconnections-connection-arn" 14 | } 15 | 16 | data "aws_ssm_parameter" "aft_global_customizations_repo_name" { 17 | name = "/aft/config/global-customizations/repo-name" 18 | } 19 | 20 | data "aws_ssm_parameter" "aft_global_customizations_repo_branch" { 21 | name = "/aft/config/global-customizations/repo-branch" 22 | } 23 | 24 | data "aws_ssm_parameter" "aft_account_customizations_repo_name" { 25 | name = "/aft/config/account-customizations/repo-name" 26 | } 27 | 28 | data "aws_ssm_parameter" "aft_account_customizations_repo_branch" { 29 | name = "/aft/config/account-customizations/repo-branch" 30 | } 31 | 32 | # Lookups from terraform-aws-aft-pipeline-framework customizations module 33 | data "aws_kms_alias" "aft_key" { 34 | name = "alias/aft" 35 | } 36 | 37 | data "aws_iam_role" "aft_codepipeline_customizations_role" { 38 | name = "aft-codepipeline-customizations-role" 39 | } 40 | 41 | data "aws_s3_bucket" "aft_codepipeline_customizations_bucket" { 42 | bucket = "aft-customizations-pipeline-${data.aws_caller_identity.current.account_id}" 43 | } 44 | 45 | data "aws_ssm_parameter" "vcs_provider" { 46 | name = "/aft/config/vcs/provider" 47 | } 48 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_account_request_framework/aft_account_request_action_trigger.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import inspect 5 | import logging 6 | from typing import TYPE_CHECKING, Any, Dict 7 | 8 | from aft_common import notifications 9 | from aft_common.account_request_record_handler import AccountRequestRecordHandler 10 | from aft_common.aft_utils import sanitize_input_for_logging 11 | from aft_common.auth import AuthClient 12 | from aft_common.logger import configure_aft_logger 13 | 14 | if TYPE_CHECKING: 15 | from aws_lambda_powertools.utilities.typing import LambdaContext 16 | else: 17 | LambdaContext = object 18 | 19 | 20 | configure_aft_logger() 21 | logger = logging.getLogger("aft") 22 | 23 | 24 | def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None: 25 | auth = AuthClient() 26 | try: 27 | record_handler = AccountRequestRecordHandler(auth=auth, event=event) 28 | logger.info(sanitize_input_for_logging(record_handler.record)) 29 | record_handler.process_request() 30 | 31 | except Exception as error: 32 | notifications.send_lambda_failure_sns_message( 33 | session=auth.aft_management_session, 34 | message=str(error), 35 | context=context, 36 | subject="AFT account request failed", 37 | ) 38 | message = { 39 | "FILE": __file__.split("/")[-1], 40 | "METHOD": inspect.stack()[0][3], 41 | "EXCEPTION": str(error), 42 | } 43 | logger.exception(message) 44 | raise 45 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/ddb.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_dynamodb_table_item" "account-request" { 5 | table_name = var.account-request-table 6 | hash_key = var.account-request-table-hash 7 | 8 | item = jsonencode({ 9 | id = { S = lookup(var.control_tower_parameters, "AccountEmail") } 10 | control_tower_parameters = { M = { 11 | AccountEmail = { S = lookup(var.control_tower_parameters, "AccountEmail") } 12 | AccountName = { S = lookup(var.control_tower_parameters, "AccountName") } 13 | ManagedOrganizationalUnit = { S = lookup(var.control_tower_parameters, "ManagedOrganizationalUnit") } 14 | SSOUserEmail = { S = lookup(var.control_tower_parameters, "SSOUserEmail") } 15 | SSOUserFirstName = { S = lookup(var.control_tower_parameters, "SSOUserFirstName") } 16 | SSOUserLastName = { S = lookup(var.control_tower_parameters, "SSOUserLastName") } 17 | } 18 | } 19 | change_management_parameters = { M = { 20 | change_reason = { S = lookup(var.change_management_parameters, "change_reason") } 21 | change_requested_by = { S = lookup(var.change_management_parameters, "change_requested_by") } 22 | } 23 | } 24 | account_tags = { S = jsonencode(var.account_tags) } 25 | account_customizations_name = { S = var.account_customizations_name } 26 | custom_fields = { S = jsonencode(var.custom_fields) } 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/lambda.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | #tfsec:ignore:aws-lambda-enable-tracing 6 | resource "aws_lambda_function" "codebuild_trigger" { 7 | filename = var.builder_archive_path 8 | function_name = local.codebuild_trigger_function_name 9 | description = "AFT Lambda Layer - CodeBuild Trigger" 10 | role = aws_iam_role.codebuild_trigger_lambda_role.arn 11 | handler = "codebuild_trigger.lambda_handler" 12 | source_code_hash = var.builder_archive_hash 13 | memory_size = 1024 14 | runtime = var.lambda_runtime_python_version 15 | timeout = 900 16 | dynamic "vpc_config" { 17 | for_each = var.aft_enable_vpc ? [1] : [] 18 | content { 19 | subnet_ids = var.aft_vpc_private_subnets 20 | security_group_ids = var.aft_vpc_default_sg 21 | } 22 | } 23 | } 24 | 25 | resource "aws_cloudwatch_log_group" "codebuild_trigger_loggroup" { 26 | name = "/aws/lambda/${aws_lambda_function.codebuild_trigger.function_name}" 27 | retention_in_days = var.cloudwatch_log_group_retention 28 | kms_key_id = var.cloudwatch_log_group_enable_cmk_encryption ? var.aft_kms_key_arn : null 29 | } 30 | 31 | data "aws_lambda_invocation" "trigger_codebuild_job" { 32 | function_name = aws_lambda_function.codebuild_trigger.function_name 33 | 34 | input = < PutItemOutputTableTypeDef: 28 | session = boto3.session.Session() 29 | try: 30 | response = ddb.put_ddb_item( 31 | session, 32 | aft_common.ssm.get_ssm_parameter_value( 33 | session, utils.SSM_PARAM_AFT_EVENTS_TABLE 34 | ), 35 | event, 36 | ) 37 | return response 38 | 39 | except Exception as error: 40 | notifications.send_lambda_failure_sns_message( 41 | session=session, 42 | message=str(error), 43 | context=context, 44 | subject="AFT Event Logging failed", 45 | ) 46 | message = { 47 | "FILE": __file__.split("/")[-1], 48 | "METHOD": inspect.stack()[0][3], 49 | "EXCEPTION": str(error), 50 | } 51 | logger.exception(message) 52 | raise 53 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/readme.md: -------------------------------------------------------------------------------- 1 | # Python Layer Builder 2 | 3 | Python Layer Builder deploys a codebuild job which creates an AWS Lambda Layer version from a github repository. 4 | 5 | This project deploys an IAM role, S3 bucket, Codebuild project, and Cloudwatch Event which triggers the codebuild project. 6 | 7 | The codebuild project runs `pip install` using the specified version of python and installs the required packages to a virtual environment, 8 | 9 | It then zips the python/lib/pythonx.x folder, and uploads it to S3. 10 | 11 | Terraform then waits for approximately 3 minutes and attempts to create a lambda layer from this S3 bucket and key. 12 | 13 | If no object is found at this location, the Terraform apply will fail. If this is an update, and something goes wrong with the buid process, the terraform apply will not record any errors. You are encouraged to impement error-handling notifications and integrate them with the python-layer-builder installation in your environment. 14 | 15 | # Requirements File 16 | 17 | The build process expects a python package list at the location: `./layer/requirements.txt` 18 | 19 | # Variables 20 | 21 | layer_name - the name of the lambda layer 22 | aws_region - the region to deploy the layer in 23 | source_url - the url of the github repository which contains the ./layer/ folder, custom packages, and requirements.txt 24 | source_branch - the branch to clone from the source repository. 25 | source_type - Currently, only GITHUB has been tested. 26 | lambda_layer_python_version - Major python version. Defaults to 3.12 27 | github_token - Set $TF_VAR_github_token to securely configure this variable with a personal access token to github. 28 | 29 | # Outputs 30 | 31 | layer_version_arn - the ARN of the lambda layer version 32 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/role-policies/aft_codepipeline_customizations_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "s3:Get*", 8 | "s3:List*", 9 | "s3:Put*" 10 | ], 11 | "Resource": [ 12 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}", 13 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}/*" 14 | ] 15 | }, 16 | { 17 | "Effect": "Allow", 18 | "Action": [ 19 | "codebuild:BatchGetBuilds", 20 | "codebuild:StartBuild" 21 | ], 22 | "Resource": "arn:${data_aws_partition_current_partition}:codebuild:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*customizations*" 23 | }, 24 | { 25 | "Effect": "Allow", 26 | "Action": [ 27 | "codecommit:GetBranch", 28 | "codecommit:GetRepository", 29 | "codecommit:GetCommit", 30 | "codecommit:GitPull", 31 | "codecommit:UploadArchive", 32 | "codecommit:GetUploadArchiveStatus", 33 | "codecommit:CancelUploadArchive" 34 | ], 35 | "Resource": "arn:${data_aws_partition_current_partition}:codecommit:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*" 36 | }, 37 | { 38 | "Effect": "Allow", 39 | "Action": [ 40 | "kms:Decrypt", 41 | "kms:Encrypt", 42 | "kms:GenerateDataKey" 43 | ], 44 | "Resource": "${data_aws_kms_alias_aft_key_target_key_arn}" 45 | }, 46 | { 47 | "Effect": "Allow", 48 | "Action": [ 49 | "codestar-connections:UseConnection", 50 | "codeconnections:UseConnection" 51 | ], 52 | "Resource": "*" 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "lambda_layer_name" { 5 | type = string 6 | validation { 7 | condition = can(regex("^[a-zA-Z0-9\\-]+$", var.lambda_layer_name)) 8 | error_message = "Layer name must contain only alphanumeric characters and hyphens." 9 | } 10 | } 11 | 12 | variable "aft_tf_aws_customizations_module_url_ssm_path" { 13 | type = string 14 | } 15 | 16 | variable "aft_tf_aws_customizations_module_git_ref_ssm_path" { 17 | type = string 18 | } 19 | 20 | variable "aws_region" { 21 | type = string 22 | } 23 | 24 | variable "lambda_layer_codebuild_delay" { 25 | type = string 26 | } 27 | 28 | variable "lambda_layer_python_version" { 29 | type = string 30 | } 31 | 32 | variable "lambda_runtime_python_version" { 33 | type = string 34 | } 35 | 36 | variable "s3_bucket_name" { 37 | type = string 38 | } 39 | 40 | variable "aft_kms_key_arn" { 41 | type = string 42 | } 43 | 44 | variable "aft_vpc_id" { 45 | type = string 46 | default = null 47 | } 48 | 49 | variable "aft_vpc_private_subnets" { 50 | type = list(string) 51 | default = null 52 | } 53 | 54 | variable "aft_vpc_default_sg" { 55 | type = list(string) 56 | default = null 57 | } 58 | variable "aft_version" { 59 | type = string 60 | } 61 | 62 | variable "builder_archive_path" { 63 | type = string 64 | } 65 | 66 | variable "builder_archive_hash" { 67 | type = string 68 | } 69 | 70 | variable "cloudwatch_log_group_retention" { 71 | type = string 72 | } 73 | 74 | variable "cloudwatch_log_group_enable_cmk_encryption" { 75 | type = bool 76 | } 77 | 78 | variable "aft_enable_vpc" { 79 | type = bool 80 | } 81 | 82 | variable "codebuild_compute_type" { 83 | type = string 84 | } 85 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "account_factory_product_name" { 5 | type = string 6 | } 7 | 8 | variable "cloudwatch_log_group_retention" { 9 | type = string 10 | } 11 | 12 | variable "cloudwatch_log_group_enable_cmk_encryption" { 13 | type = bool 14 | } 15 | 16 | variable "aft_account_provisioning_framework_sfn_name" { 17 | type = string 18 | } 19 | 20 | variable "aft_common_layer_arn" { 21 | type = string 22 | } 23 | 24 | variable "aft_vpc_cidr" { 25 | type = string 26 | default = null 27 | } 28 | 29 | variable "aft_vpc_private_subnet_01_cidr" { 30 | type = string 31 | default = null 32 | } 33 | 34 | variable "aft_vpc_private_subnet_02_cidr" { 35 | type = string 36 | default = null 37 | } 38 | 39 | variable "aft_vpc_public_subnet_01_cidr" { 40 | type = string 41 | default = null 42 | } 43 | 44 | variable "aft_vpc_public_subnet_02_cidr" { 45 | type = string 46 | default = null 47 | } 48 | 49 | variable "aft_vpc_endpoints" { 50 | type = bool 51 | default = null 52 | } 53 | 54 | variable "request_framework_archive_path" { 55 | type = string 56 | } 57 | 58 | variable "request_framework_archive_hash" { 59 | type = string 60 | } 61 | variable "concurrent_account_factory_actions" { 62 | type = number 63 | } 64 | 65 | variable "lambda_runtime_python_version" { 66 | type = string 67 | } 68 | 69 | variable "backup_recovery_point_retention" { 70 | type = number 71 | } 72 | 73 | variable "aft_enable_vpc" { 74 | type = bool 75 | } 76 | 77 | variable "aft_customer_vpc_id" { 78 | type = string 79 | } 80 | 81 | variable "aft_customer_private_subnets" { 82 | type = list(string) 83 | } 84 | 85 | variable "sns_topic_enable_cmk_encryption" { 86 | type = bool 87 | } 88 | -------------------------------------------------------------------------------- /providers.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | provider "aws" { 5 | alias = "ct_management" 6 | region = var.ct_home_region 7 | # The default profile or environment variables should authenticate to the Control Tower Management Account as Administrator 8 | default_tags { 9 | tags = local.aft_tags 10 | } 11 | } 12 | 13 | provider "aws" { 14 | alias = "aft_management" 15 | region = var.ct_home_region 16 | assume_role { 17 | role_arn = "arn:${data.aws_partition.current.partition}:iam::${var.aft_management_account_id}:role/AWSControlTowerExecution" 18 | session_name = local.aft_session_name 19 | } 20 | default_tags { 21 | tags = local.aft_tags 22 | } 23 | } 24 | provider "aws" { 25 | alias = "tf_backend_secondary_region" 26 | region = var.tf_backend_secondary_region 27 | assume_role { 28 | role_arn = "arn:${data.aws_partition.current.partition}:iam::${var.aft_management_account_id}:role/AWSControlTowerExecution" 29 | session_name = local.aft_session_name 30 | } 31 | default_tags { 32 | tags = local.aft_tags 33 | } 34 | } 35 | provider "aws" { 36 | alias = "audit" 37 | region = var.ct_home_region 38 | assume_role { 39 | role_arn = "arn:${data.aws_partition.current.partition}:iam::${var.audit_account_id}:role/AWSControlTowerExecution" 40 | session_name = local.aft_session_name 41 | } 42 | default_tags { 43 | tags = local.aft_tags 44 | } 45 | } 46 | provider "aws" { 47 | alias = "log_archive" 48 | region = var.ct_home_region 49 | assume_role { 50 | role_arn = "arn:${data.aws_partition.current.partition}:iam::${var.log_archive_account_id}:role/AWSControlTowerExecution" 51 | session_name = local.aft_session_name 52 | } 53 | default_tags { 54 | tags = local.aft_tags 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /modules/aft-feature-options/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "aft_vpc_private_subnets" { 5 | type = list(string) 6 | default = null 7 | } 8 | 9 | variable "aft_vpc_default_sg" { 10 | type = list(string) 11 | default = null 12 | } 13 | 14 | variable "aft_common_layer_arn" { 15 | type = string 16 | } 17 | 18 | variable "cloudwatch_log_group_retention" { 19 | type = string 20 | } 21 | 22 | variable "cloudwatch_log_group_enable_cmk_encryption" { 23 | type = bool 24 | } 25 | 26 | variable "sns_topic_enable_cmk_encryption" { 27 | type = bool 28 | } 29 | 30 | variable "aft_kms_key_arn" { 31 | type = string 32 | } 33 | 34 | variable "aft_kms_key_id" { 35 | type = string 36 | } 37 | 38 | variable "aft_sns_topic_arn" { 39 | type = string 40 | } 41 | 42 | variable "aft_failure_sns_topic_arn" { 43 | type = string 44 | } 45 | 46 | variable "log_archive_bucket_name" { 47 | type = string 48 | } 49 | 50 | variable "log_archive_access_logs_bucket_name" { 51 | type = string 52 | } 53 | 54 | variable "log_archive_bucket_object_expiration_days" { 55 | type = number 56 | } 57 | 58 | variable "log_archive_account_id" { 59 | type = string 60 | } 61 | 62 | variable "aft_features_sfn_name" { 63 | type = string 64 | } 65 | variable "feature_options_archive_path" { 66 | type = string 67 | } 68 | 69 | variable "feature_options_archive_hash" { 70 | type = string 71 | } 72 | 73 | variable "delete_default_vpc_lambda_function_name" { 74 | type = string 75 | } 76 | 77 | variable "enroll_support_lambda_function_name" { 78 | type = string 79 | } 80 | 81 | variable "enable_cloudtrail_lambda_function_name" { 82 | type = string 83 | } 84 | 85 | variable "lambda_runtime_python_version" { 86 | type = string 87 | } 88 | 89 | variable "aft_enable_vpc" { 90 | type = bool 91 | 92 | } 93 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/iam/role-policies/ct_aft_account_request_codepipeline_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect":"Allow", 6 | "Action": [ 7 | "s3:GetObject", 8 | "s3:GetObjectVersion", 9 | "s3:GetBucketVersioning", 10 | "s3:List*", 11 | "s3:PutObjectAcl", 12 | "s3:PutObject" 13 | ], 14 | "Resource": [ 15 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}", 16 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}/*" 17 | ] 18 | }, 19 | { 20 | "Effect": "Allow", 21 | "Action": [ 22 | "codebuild:BatchGetBuilds", 23 | "codebuild:StartBuild" 24 | ], 25 | "Resource": "arn:${data_aws_partition_current_partition}:codebuild:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*account-request*" 26 | }, 27 | { 28 | "Effect": "Allow", 29 | "Action": [ 30 | "codecommit:GetBranch", 31 | "codecommit:GetRepository", 32 | "codecommit:GetCommit", 33 | "codecommit:GitPull", 34 | "codecommit:UploadArchive", 35 | "codecommit:GetUploadArchiveStatus", 36 | "codecommit:CancelUploadArchive" 37 | ], 38 | "Resource": "arn:${data_aws_partition_current_partition}:codecommit:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*account-request*" 39 | }, 40 | { 41 | "Effect": "Allow", 42 | "Action": [ 43 | "kms:Decrypt", 44 | "kms:Encrypt", 45 | "kms:GenerateDataKey" 46 | ], 47 | "Resource": "${data_aws_kms_alias_aft_key_target_key_arn}" 48 | }, 49 | { 50 | "Effect": "Allow", 51 | "Action": [ 52 | "codestar-connections:UseConnection", 53 | "codeconnections:UseConnection" 54 | ], 55 | "Resource": "*" 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/iam/role-policies/ct_aft_account_provisioning_customizations_codepipeline_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect":"Allow", 6 | "Action": [ 7 | "s3:GetObject", 8 | "s3:GetObjectVersion", 9 | "s3:GetBucketVersioning", 10 | "s3:List*", 11 | "s3:PutObjectAcl", 12 | "s3:PutObject" 13 | ], 14 | "Resource": [ 15 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}", 16 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}/*" 17 | ] 18 | }, 19 | { 20 | "Effect": "Allow", 21 | "Action": [ 22 | "codebuild:BatchGetBuilds", 23 | "codebuild:StartBuild" 24 | ], 25 | "Resource": "arn:${data_aws_partition_current_partition}:codebuild:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*account-provisioning*" 26 | }, 27 | { 28 | "Effect": "Allow", 29 | "Action": [ 30 | "codecommit:GetBranch", 31 | "codecommit:GetRepository", 32 | "codecommit:GetCommit", 33 | "codecommit:GitPull", 34 | "codecommit:UploadArchive", 35 | "codecommit:GetUploadArchiveStatus", 36 | "codecommit:CancelUploadArchive" 37 | ], 38 | "Resource": "arn:${data_aws_partition_current_partition}:codecommit:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:*account-provisioning*" 39 | }, 40 | { 41 | "Effect": "Allow", 42 | "Action": [ 43 | "kms:Decrypt", 44 | "kms:Encrypt", 45 | "kms:GenerateDataKey" 46 | ], 47 | "Resource": "${data_aws_kms_alias_aft_key_target_key_arn}" 48 | }, 49 | { 50 | "Effect": "Allow", 51 | "Action": [ 52 | "codestar-connections:UseConnection", 53 | "codeconnections:UseConnection" 54 | ], 55 | "Resource": "*" 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/aft_common/notifications.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import logging 5 | from typing import TYPE_CHECKING 6 | 7 | from aft_common.aft_utils import sanitize_input_for_logging 8 | from aft_common.constants import SSM_PARAM_SNS_FAILURE_TOPIC_ARN 9 | from aft_common.ssm import get_ssm_parameter_value 10 | from boto3.session import Session 11 | 12 | if TYPE_CHECKING: 13 | from aws_lambda_powertools.utilities.typing import LambdaContext 14 | from mypy_boto3_sns import SNSClient 15 | from mypy_boto3_sns.type_defs import PublishResponseTypeDef 16 | else: 17 | LambdaClient = object 18 | PublishResponseTypeDef = object 19 | SNSClient = object 20 | LambdaContext = object 21 | 22 | logger = logging.getLogger("aft") 23 | 24 | 25 | def send_sns_message( 26 | session: Session, topic: str, sns_message: str, subject: str 27 | ) -> PublishResponseTypeDef: 28 | logger.info("Sending SNS Message") 29 | client: SNSClient = session.client("sns") 30 | response = client.publish(TopicArn=topic, Message=sns_message, Subject=subject) 31 | sanitized_response = sanitize_input_for_logging(response) 32 | logger.info(sanitized_response) 33 | return response 34 | 35 | 36 | def send_lambda_failure_sns_message( 37 | session: Session, message: str, subject: str, context: LambdaContext 38 | ) -> None: 39 | msg = f"""An error occurred in the '{context.function_name}' Lambda function. 40 | For more information, search AWS Request ID '{context.aws_request_id}' in CloudWatch log group '{context.log_group_name}' 41 | Error Message: {message}""" 42 | 43 | failure_sns_topic = get_ssm_parameter_value( 44 | session=session, 45 | param=SSM_PARAM_SNS_FAILURE_TOPIC_ARN, 46 | ) 47 | send_sns_message( 48 | session=session, 49 | topic=failure_sns_topic, 50 | sns_message=msg, 51 | subject=subject, 52 | ) 53 | -------------------------------------------------------------------------------- /modules/aft-feature-options/states/aft_features.asl.json: -------------------------------------------------------------------------------- 1 | { 2 | "StartAt": "Delete Default VPCs", 3 | "States": { 4 | "Delete Default VPCs": { 5 | "Next": "Enroll Enterprise Support", 6 | "Type": "Task", 7 | "Resource": "${ aft_delete_default_vpc_function_arn}", 8 | "ResultPath": "$.targets", 9 | "Catch": [ 10 | { 11 | "ErrorEquals": ["States.ALL"], 12 | "Next": "Notify Failure" 13 | } 14 | ] 15 | }, 16 | "Enroll Enterprise Support": { 17 | "Next": "Enable CloudTrail", 18 | "Type": "Task", 19 | "Resource": "${ aft_enroll_support_function_arn}", 20 | "ResultPath": "$.targets", 21 | "Catch": [ 22 | { 23 | "ErrorEquals": ["States.ALL"], 24 | "Next": "Notify Failure" 25 | } 26 | ] 27 | }, 28 | "Enable CloudTrail": { 29 | "Next": "Notify Success", 30 | "Type": "Task", 31 | "Resource": "${ aft_enable_cloudtrail_function_arn}", 32 | "ResultPath": "$.targets", 33 | "Catch": [ 34 | { 35 | "ErrorEquals": ["States.ALL"], 36 | "Next": "Notify Failure" 37 | } 38 | ] 39 | }, 40 | "Notify Success": { 41 | "Type": "Task", 42 | "Resource": "arn:${current_partition}:states:::sns:publish", 43 | "Parameters": { 44 | "TopicArn": "${aft_notification_arn}", 45 | "Message.$": "$" 46 | }, 47 | "End": true 48 | }, 49 | "Notify Failure": { 50 | "Type": "Task", 51 | "Resource": "arn:${current_partition}:states:::sns:publish", 52 | "Parameters": { 53 | "TopicArn": "${aft_failure_notification_arn}", 54 | "Message.$": "$.Cause" 55 | }, 56 | "Next": "Failed" 57 | }, 58 | "Failed": { 59 | "Type": "Fail" 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/iam.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_iam_role" "codebuild" { 5 | name = local.common_name 6 | assume_role_policy = file("${path.module}/iam/trust-policies/codebuild.tpl") 7 | } 8 | 9 | resource "aws_iam_role" "codebuild_trigger_lambda_role" { 10 | name = "codebuild_trigger_role" 11 | assume_role_policy = file("${path.module}/iam/trust-policies/lambda.tpl") 12 | } 13 | 14 | resource "aws_iam_role_policy" "codebuild" { 15 | role = aws_iam_role.codebuild.name 16 | policy = templatefile("${path.module}/iam/role-policies/codebuild.tpl", { 17 | "data_aws_partition_current_partition" = data.aws_partition.current.partition 18 | "aws_region" = var.aws_region 19 | "account_id" = local.account_id 20 | "layer_name" = var.lambda_layer_name 21 | "s3_bucket_name" = var.s3_bucket_name 22 | "data_aws_kms_alias_aft_key_target_key_arn" = var.aft_kms_key_arn 23 | }) 24 | } 25 | 26 | resource "aws_iam_role_policy" "codebuild_trigger_policy" { 27 | role = aws_iam_role.codebuild_trigger_lambda_role.name 28 | policy = templatefile("${path.module}/iam/role-policies/codebuild-trigger.tpl", { 29 | "data_aws_partition_current_partition" = data.aws_partition.current.partition 30 | "aws_region" = var.aws_region 31 | "account_id" = local.account_id 32 | "codebuild_project_name" = aws_codebuild_project.codebuild.name 33 | "codebuild_trigger_function_name" = local.codebuild_trigger_function_name 34 | }) 35 | } 36 | 37 | resource "aws_iam_role_policy_attachment" "codebuild_trigger_VPC_access" { 38 | role = aws_iam_role.codebuild_trigger_lambda_role.name 39 | policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" 40 | } 41 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/role-policies/aft_identify_targets_lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "sts:AssumeRole" 8 | ], 9 | "Resource": [ 10 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_current_account_id}:role/AWSAFTAdmin" 11 | ] 12 | }, 13 | { 14 | "Effect": "Allow", 15 | "Action": "sts:GetCallerIdentity", 16 | "Resource": "*" 17 | }, 18 | { 19 | "Effect": "Allow", 20 | "Action": "ssm:GetParameter", 21 | "Resource": [ 22 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:parameter/aft/*" 23 | ] 24 | }, 25 | { 26 | "Effect": "Allow", 27 | "Action": [ 28 | "kms:GenerateDataKey*", 29 | "kms:Encrypt", 30 | "kms:Decrypt" 31 | ], 32 | "Resource": [ 33 | "${aws_kms_key_aft_arn}" 34 | ] 35 | }, 36 | { 37 | "Effect": "Allow", 38 | "Action": [ 39 | "dynamodb:GetItem", 40 | "dynamodb:Scan" 41 | ], 42 | "Resource": [ 43 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:table/${request_metadata_table_name}", 44 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:table/${account_request_table_name}" 45 | ] 46 | }, 47 | { 48 | "Effect": "Allow", 49 | "Action": [ 50 | "sns:Publish" 51 | ], 52 | "Resource": [ 53 | "${aft_sns_topic_arn}", 54 | "${aft_failure_sns_topic_arn}" 55 | ] 56 | }, 57 | { 58 | "Effect": "Allow", 59 | "Action": [ 60 | "s3:Put*" 61 | ], 62 | "Resource": [ 63 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}/*" 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_account_provisioning_framework/aft_account_provisioning_framework_create_role.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import inspect 5 | from typing import TYPE_CHECKING, Any, Dict 6 | 7 | from aft_common import notifications 8 | from aft_common.account_provisioning_framework import ProvisionRoles 9 | from aft_common.auth import AuthClient 10 | from aft_common.logger import customization_request_logger 11 | 12 | if TYPE_CHECKING: 13 | from aws_lambda_powertools.utilities.typing import LambdaContext 14 | else: 15 | LambdaContext = object 16 | 17 | 18 | def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None: 19 | action = event["action"] 20 | event_payload = event["payload"] 21 | request_id = event_payload["customization_request_id"] 22 | target_account_id = event_payload["account_info"]["account"]["id"] 23 | 24 | logger = customization_request_logger( 25 | aws_account_id=target_account_id, customization_request_id=request_id 26 | ) 27 | 28 | auth = AuthClient() 29 | try: 30 | if action != "create_role": 31 | raise Exception( 32 | f"Incorrect Command Passed to Lambda Function. Input: {action}. Expected: 'create_role'" 33 | ) 34 | logger.info("Deploying / managing AFT Roles in target account") 35 | provisioning = ProvisionRoles(auth=auth, account_id=target_account_id) 36 | provisioning.deploy_aws_aft_roles() 37 | 38 | except Exception as error: 39 | notifications.send_lambda_failure_sns_message( 40 | session=auth.get_aft_management_session(), 41 | message=str(error), 42 | context=context, 43 | subject="AFT account provisioning failed", 44 | ) 45 | message = { 46 | "FILE": __file__.split("/")[-1], 47 | "METHOD": inspect.stack()[0][3], 48 | "EXCEPTION": str(error), 49 | } 50 | logger.exception(message) 51 | raise 52 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/kms.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_kms_key" "aft" { 5 | description = "AFT KMS key" 6 | enable_key_rotation = "true" 7 | # Use inline policy instead of 'aws_kms_key_policy' resource 8 | # to always make sure policy is fully propagated before the key is used 9 | # e.g. for CloudWatch Log Groups 10 | policy = jsonencode( 11 | { 12 | "Version" : "2012-10-17", 13 | "Id" : "key-default-1", 14 | "Statement" : concat([ 15 | { 16 | "Sid" : "Enable IAM User Permissions", 17 | "Effect" : "Allow", 18 | "Principal" : { 19 | "AWS" : "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.aft-management.account_id}:root" 20 | }, 21 | "Action" : "kms:*", 22 | "Resource" : "*" 23 | }], var.cloudwatch_log_group_enable_cmk_encryption ? [{ 24 | "Sid" : "Allow CloudWatch Logs access", # Reference: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html 25 | "Effect" : "Allow", 26 | "Principal" : { 27 | "Service" : "logs.${data.aws_region.aft-management.name}.amazonaws.com" 28 | }, 29 | "Action" : [ 30 | "kms:Encrypt", 31 | "kms:Decrypt", 32 | "kms:ReEncrypt*", 33 | "kms:GenerateDataKey*", 34 | "kms:Describe*" 35 | ], 36 | "Resource" : "*", 37 | "Condition" : { 38 | "ArnEquals" : { 39 | # Allow all log groups in AFT Mgmt Account 40 | "kms:EncryptionContext:aws:logs:arn" : "arn:${data.aws_partition.current.partition}:logs:${data.aws_region.aft-management.name}:${data.aws_caller_identity.aft-management.account_id}:*" 41 | } 42 | } 43 | }] : []) 44 | }) 45 | } 46 | 47 | 48 | resource "aws_kms_alias" "aft" { 49 | name = "alias/aft" 50 | target_key_id = aws_kms_key.aft.key_id 51 | } 52 | -------------------------------------------------------------------------------- /modules/aft-customizations/s3.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | #tfsec:ignore:aws-s3-enable-bucket-logging 6 | resource "aws_s3_bucket" "aft_codepipeline_customizations_bucket" { 7 | bucket = "aft-customizations-pipeline-${data.aws_caller_identity.current.account_id}" 8 | } 9 | 10 | resource "aws_s3_bucket_policy" "aft_codepipeline_customizations_bucket" { 11 | bucket = aws_s3_bucket.aft_codepipeline_customizations_bucket.id 12 | policy = templatefile("${path.module}/s3/bucket-policies/aft_codepipeline_customizations_bucket.tpl", { 13 | aft_codepipeline_customizations_bucket_arn = aws_s3_bucket.aft_codepipeline_customizations_bucket.arn 14 | }) 15 | } 16 | 17 | resource "aws_s3_bucket_public_access_block" "aft-codepipeline-customizations-block-public-access" { 18 | bucket = aws_s3_bucket.aft_codepipeline_customizations_bucket.id 19 | 20 | block_public_acls = true 21 | block_public_policy = true 22 | ignore_public_acls = true 23 | restrict_public_buckets = true 24 | } 25 | 26 | resource "aws_s3_bucket_versioning" "aft-codepipeline-customizations-bucket-versioning" { 27 | bucket = aws_s3_bucket.aft_codepipeline_customizations_bucket.id 28 | versioning_configuration { 29 | status = "Enabled" 30 | } 31 | } 32 | 33 | resource "aws_s3_bucket_server_side_encryption_configuration" "aft-codepipeline-customizations-bucket-encryption" { 34 | bucket = aws_s3_bucket.aft_codepipeline_customizations_bucket.id 35 | 36 | rule { 37 | apply_server_side_encryption_by_default { 38 | kms_master_key_id = var.aft_kms_key_id 39 | sse_algorithm = "aws:kms" 40 | } 41 | } 42 | } 43 | 44 | resource "aws_s3_bucket_lifecycle_configuration" "aft_codepipeline_customizations_bucket" { 45 | bucket = aws_s3_bucket.aft_codepipeline_customizations_bucket.id 46 | rule { 47 | id = "sfn-data" 48 | filter { 49 | prefix = "sfn/" 50 | } 51 | expiration { 52 | days = var.sfn_s3_bucket_object_expiration_days 53 | } 54 | status = "Enabled" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/iam/role-policies/lambda-aft-account-provisioning-framework.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version" : "2012-10-17", 3 | "Statement" : [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : [ 7 | "dynamodb:BatchGetItem", 8 | "dynamodb:BatchWriteItem", 9 | "dynamodb:DeleteItem", 10 | "dynamodb:GetItem", 11 | "dynamodb:PutItem", 12 | "dynamodb:Query", 13 | "dynamodb:Scan" 14 | ], 15 | "Resource" : [ 16 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:table/aft*" 17 | ] 18 | }, 19 | { 20 | "Effect" : "Allow", 21 | "Action" : "ssm:GetParameter", 22 | "Resource" : [ 23 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:parameter/aft/*" 24 | ] 25 | }, 26 | { 27 | "Effect" : "Allow", 28 | "Action" : [ 29 | "sts:AssumeRole" 30 | ], 31 | "Resource" : [ 32 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_aft-management_account_id}:role/AWSAFTAdmin" 33 | ] 34 | }, 35 | { 36 | "Effect" : "Allow", 37 | "Action" : "sts:GetCallerIdentity", 38 | "Resource" : "*" 39 | }, 40 | { 41 | "Effect" : "Allow", 42 | "Action" : [ 43 | "sns:Publish" 44 | ], 45 | "Resource" : [ 46 | "${aft_sns_topic_arn}", 47 | "${aft_failure_sns_topic_arn}" 48 | ] 49 | }, 50 | { 51 | "Effect" : "Allow", 52 | "Action" : [ 53 | "kms:GenerateDataKey", 54 | "kms:Encrypt", 55 | "kms:Decrypt" 56 | ], 57 | "Resource" : [ 58 | "${aws_kms_key_aft_arn}", 59 | "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:alias/aws/sns" 60 | ] 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | resource "aws_cloudwatch_query_definition" "customization_request_query" { 5 | name = "Account Factory for Terraform/Customization Logs by Customization Request ID" 6 | 7 | log_group_names = [ 8 | "/aws/lambda/${var.create_role_lambda_function_name}", 9 | "/aws/lambda/${var.persist_metadata_lambda_function_name}", 10 | "/aws/lambda/${var.tag_account_lambda_function_name}", 11 | "/aws/lambda/${var.account_metadata_ssm_lambda_function_name}", 12 | "/aws/lambda/${var.delete_default_vpc_lambda_function_name}", 13 | "/aws/lambda/${var.enroll_support_lambda_function_name}", 14 | "/aws/lambda/${var.enable_cloudtrail_lambda_function_name}", 15 | ] 16 | 17 | query_string = < bool: 25 | """ 26 | Query the open support cases, if a case is open with the expected title, return True 27 | else we return False 28 | """ 29 | submitted_enroll_case_title = f"Add Account {account_id} to Enterprise Support" 30 | 31 | # Must use us-east-1 region for Support API 32 | botoconfig = Config.merge( 33 | get_high_retry_botoconfig(), Config(region_name=SUPPORT_API_REGION) 34 | ) 35 | client: SupportClient = ct_management_session.client("support", config=botoconfig) 36 | paginator = client.get_paginator("describe_cases") 37 | pages = paginator.paginate( 38 | includeResolvedCases=True, 39 | language="en", 40 | includeCommunications=False, 41 | ) 42 | for page in pages: 43 | for case in page["cases"]: 44 | if case["subject"] == submitted_enroll_case_title: 45 | return True 46 | 47 | return False 48 | 49 | 50 | def generate_case(session: Session, account_id: str) -> None: 51 | support: SupportClient = session.client("support", region_name=SUPPORT_API_REGION) 52 | support.create_case( 53 | issueType="customer-service", 54 | serviceCode="account-management", 55 | categoryCode="billing", 56 | severityCode="low", 57 | subject=f"Add Account {account_id} to Enterprise Support", 58 | communicationBody=f"Please add account number {account_id} to our enterprise support plan.", 59 | language="en", 60 | ) 61 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-account-provisioning-customizations/README.md: -------------------------------------------------------------------------------- 1 | # AFT Account Provisioning Customizations Customizations 2 | 3 | ## Problem Statement 4 | 5 | AFT provides flexibility to customize the provisioning process for new accounts and integrate with systems prior to the account customization stage. 6 | 7 | While the customization stage does include integrations for pre- and post- customization steps, the Account Provisioning standard allows for further integration by using an AWS Step Functions State Machine to integrate with additional environments. 8 | 9 | Using this state machine integration, customers may define Account Provisioning Customizations steps as: 10 | 11 | * Lambda functions in the language of their choice 12 | * ECS or Fargate Tasks using docker containers 13 | * AWS Step Functions Activities using custom workers, hosted either in AWS or on-prem 14 | * Amazon SNS or SQS integrations to decoupled consumer-based applications 15 | 16 | ## Example Payload 17 | 18 | ``` 19 | { 20 | "account_request": { 21 | "supported_regions": "", 22 | "account_tags": { 23 | "Key": "Value", 24 | }, 25 | "custom_fields": "{}", 26 | "id": "Account Email", 27 | "control_tower_parameters": { 28 | "SSOUserEmail": "", 29 | "AccountEmail": "", 30 | "SSOUserFirstName": "", 31 | "SSOUserLastName": "", 32 | "ManagedOrganizationalUnit": "Sandbox", 33 | "AccountName": "sandbox03" 34 | }, 35 | "customer_baselines": [], 36 | "operation": "create" 37 | }, 38 | "control_tower_event": {}, 39 | "account_info": { 40 | "account": { 41 | "id": "", 42 | "type": "account", 43 | "email": "", 44 | "name": "sandbox03", 45 | "method": "CREATED", 46 | "joined_date": "2021-06-15 13:57:35.129000+00:00", 47 | "status": "ACTIVE", 48 | "parent_id": "", 49 | "parent_type": "ORGANIZATIONAL_UNIT", 50 | "org_name": "Sandbox", 51 | "vendor": "aws" 52 | } 53 | }, 54 | "persist_metadata": { 55 | "StatusCode": 200 56 | }, 57 | "role": { 58 | "Arn": "arn:aws:iam:::role/AWSAFTExecution" 59 | }, 60 | "account_tags": { 61 | "StatusCode": 200 62 | } 63 | } 64 | ``` 65 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/states.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | locals { 5 | state_machine_source = "${path.module}/states/aft_account_provisioning_framework.asl.json" 6 | replacements_map = { 7 | current_partition = data.aws_partition.current.partition 8 | create_role_function_name = aws_lambda_function.create_role.function_name 9 | tag_account_function_name = aws_lambda_function.tag_account.function_name 10 | persist_metadata_function_name = aws_lambda_function.persist_metadata.function_name 11 | account_metadata_ssm_function_name = aws_lambda_function.account_metadata_ssm.function_name 12 | aft_notification_arn = var.aft_sns_topic_arn 13 | aft_failure_notification_arn = var.aft_failure_sns_topic_arn 14 | aft_account_provisioning_customizations_state_machine_arn = "arn:${data.aws_partition.current.partition}:states:${data.aws_region.aft_management.name}:${data.aws_caller_identity.aft_management.account_id}:stateMachine:${var.aft_account_provisioning_customizations_sfn_name}" 15 | customizations_trigger_state_machine_arn = "arn:${data.aws_partition.current.partition}:states:${data.aws_region.aft_management.name}:${data.aws_caller_identity.aft_management.account_id}:stateMachine:${var.trigger_customizations_sfn_name}" 16 | aft_account_provisioning_framework_aft_features_state_machine_arn = "arn:${data.aws_partition.current.partition}:states:${data.aws_region.aft_management.name}:${data.aws_caller_identity.aft_management.account_id}:stateMachine:${var.aft_features_sfn_name}" 17 | } 18 | } 19 | 20 | resource "aws_sfn_state_machine" "aft_account_provisioning_framework_sfn" { 21 | name = var.aft_account_provisioning_framework_sfn_name 22 | role_arn = aws_iam_role.aft_states.arn 23 | definition = templatefile(local.state_machine_source, local.replacements_map) 24 | } 25 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/aft_common/ssm.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import logging 5 | from typing import Dict, List, Sequence 6 | 7 | from aft_common.aft_utils import ( 8 | get_high_retry_botoconfig, 9 | resubmit_request_on_boto_throttle, 10 | yield_batches_from_list, 11 | ) 12 | from aft_common.constants import SSM_PARAMETER_PATH 13 | from boto3.session import Session 14 | 15 | logger = logging.getLogger("aft") 16 | 17 | 18 | @resubmit_request_on_boto_throttle 19 | def put_ssm_parameters(session: Session, parameters: Dict[str, str]) -> None: 20 | client = session.client("ssm", config=get_high_retry_botoconfig()) 21 | 22 | for key, value in parameters.items(): 23 | response = client.put_parameter( 24 | Name=SSM_PARAMETER_PATH + key, Value=value, Type="String", Overwrite=True 25 | ) 26 | 27 | 28 | @resubmit_request_on_boto_throttle 29 | def get_ssm_parameters_names_by_path(session: Session, path: str) -> List[str]: 30 | client = session.client("ssm", config=get_high_retry_botoconfig()) 31 | paginator = client.get_paginator("get_parameters_by_path") 32 | pages = paginator.paginate(Path=path, Recursive=True) 33 | 34 | parameter_names = [] 35 | for page in pages: 36 | parameter_names.extend([param["Name"] for param in page["Parameters"]]) 37 | 38 | return parameter_names 39 | 40 | 41 | @resubmit_request_on_boto_throttle 42 | def delete_ssm_parameters(session: Session, parameters: Sequence[str]) -> None: 43 | batches = yield_batches_from_list( 44 | parameters, batch_size=10 45 | ) # Max batch size for API 46 | client = session.client("ssm", config=get_high_retry_botoconfig()) 47 | for batched_names in batches: 48 | response = client.delete_parameters(Names=batched_names) 49 | 50 | 51 | def get_ssm_parameter_value(session: Session, param: str, decrypt: bool = False) -> str: 52 | client = session.client("ssm") 53 | logger.info("Getting SSM Parameter " + param) 54 | 55 | response = client.get_parameter(Name=param, WithDecryption=decrypt) 56 | 57 | param_value: str = response["Parameter"]["Value"] 58 | return param_value 59 | -------------------------------------------------------------------------------- /modules/aft-account-provisioning-framework/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "aft_account_provisioning_framework_sfn_name" { 5 | type = string 6 | } 7 | 8 | variable "aft_sns_topic_arn" { 9 | type = string 10 | } 11 | 12 | variable "aft_failure_sns_topic_arn" { 13 | type = string 14 | } 15 | 16 | variable "aft_common_layer_arn" { 17 | type = string 18 | } 19 | 20 | variable "aft_kms_key_arn" { 21 | type = string 22 | } 23 | 24 | variable "cloudwatch_log_group_retention" { 25 | type = string 26 | } 27 | 28 | variable "cloudwatch_log_group_enable_cmk_encryption" { 29 | type = bool 30 | } 31 | 32 | variable "aft_account_provisioning_customizations_sfn_name" { 33 | type = string 34 | } 35 | 36 | variable "trigger_customizations_sfn_name" { 37 | type = string 38 | } 39 | 40 | variable "aft_features_sfn_name" { 41 | type = string 42 | } 43 | 44 | variable "aft_vpc_private_subnets" { 45 | type = list(string) 46 | default = null 47 | } 48 | 49 | variable "aft_vpc_default_sg" { 50 | type = list(string) 51 | default = null 52 | } 53 | 54 | variable "provisioning_framework_archive_path" { 55 | type = string 56 | } 57 | 58 | variable "provisioning_framework_archive_hash" { 59 | type = string 60 | } 61 | 62 | variable "create_role_lambda_function_name" { 63 | type = string 64 | } 65 | 66 | variable "tag_account_lambda_function_name" { 67 | type = string 68 | } 69 | 70 | variable "persist_metadata_lambda_function_name" { 71 | type = string 72 | } 73 | 74 | variable "account_metadata_ssm_lambda_function_name" { 75 | type = string 76 | } 77 | 78 | variable "delete_default_vpc_lambda_function_name" { 79 | type = string 80 | } 81 | 82 | variable "enroll_support_lambda_function_name" { 83 | type = string 84 | } 85 | 86 | variable "enable_cloudtrail_lambda_function_name" { 87 | type = string 88 | } 89 | 90 | variable "lambda_runtime_python_version" { 91 | type = string 92 | } 93 | 94 | variable "aft_enable_vpc" { 95 | type = bool 96 | } 97 | 98 | variable "sns_topic_enable_cmk_encryption" { 99 | type = bool 100 | } 101 | -------------------------------------------------------------------------------- /modules/aft-feature-options/iam/role-policies/aft_delete_default_vpc_lambda.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "ec2:DeleteSubnet", 8 | "ec2:DescribeRegions", 9 | "ec2:DetachInternetGateway", 10 | "ec2:DescribeSecurityGroups", 11 | "ec2:DeleteRouteTable", 12 | "ec2:DescribeInternetGateways", 13 | "ec2:DescribeVpcs", 14 | "ec2:DeleteSecurityGroup", 15 | "ec2:DeleteInternetGateway", 16 | "ec2:DeleteVpc", 17 | "ec2:DescribeSubnets", 18 | "ec2:DescribeNetworkAcls", 19 | "ec2:DescribeRouteTables", 20 | "ec2:DeleteNetworkAcl" 21 | ], 22 | "Resource": "*" 23 | }, 24 | { 25 | "Effect" : "Allow", 26 | "Action" : "ssm:GetParameter", 27 | "Resource" : [ 28 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:parameter/aft/*" 29 | ] 30 | }, 31 | { 32 | "Effect" : "Allow", 33 | "Action" : [ 34 | "kms:GenerateDataKey", 35 | "kms:Encrypt", 36 | "kms:Decrypt" 37 | ], 38 | "Resource" : [ 39 | "${aws_kms_key_aft_arn}" 40 | ] 41 | }, 42 | { 43 | "Effect" : "Allow", 44 | "Action" : [ 45 | "sts:AssumeRole" 46 | ], 47 | "Resource" : [ 48 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_current_account_id}:role/AWSAFTAdmin" 49 | ] 50 | }, 51 | { 52 | "Effect" : "Allow", 53 | "Action" : "sts:GetCallerIdentity", 54 | "Resource" : "*" 55 | }, 56 | { 57 | "Effect" : "Allow", 58 | "Action" : [ 59 | "sns:Publish" 60 | ], 61 | "Resource" : [ 62 | "${aws_sns_topic_aft_notifications_arn}", 63 | "${aws_sns_topic_aft_failure_notifications_arn}" 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | variable "vpc_id" { 5 | type = string 6 | } 7 | 8 | variable "subnet_ids" { 9 | type = list(string) 10 | default = null 11 | } 12 | 13 | variable "security_group_ids" { 14 | type = list(string) 15 | } 16 | 17 | variable "cloudwatch_log_group_retention" { 18 | type = string 19 | } 20 | 21 | variable "cloudwatch_log_group_enable_cmk_encryption" { 22 | type = bool 23 | } 24 | 25 | variable "vcs_provider" { 26 | type = string 27 | } 28 | 29 | variable "terraform_distribution" { 30 | type = string 31 | } 32 | 33 | variable "github_enterprise_url" { 34 | type = string 35 | } 36 | 37 | variable "gitlab_selfmanaged_url" { 38 | type = string 39 | } 40 | 41 | variable "account_request_table_name" { 42 | type = string 43 | } 44 | 45 | variable "aft_config_backend_table_id" { 46 | type = string 47 | } 48 | 49 | variable "aft_config_backend_bucket_id" { 50 | type = string 51 | } 52 | 53 | variable "aft_config_backend_kms_key_id" { 54 | type = string 55 | } 56 | 57 | variable "codepipeline_s3_bucket_name" { 58 | type = string 59 | } 60 | 61 | variable "codepipeline_s3_bucket_arn" { 62 | type = string 63 | } 64 | 65 | variable "aft_kms_key_arn" { 66 | type = string 67 | } 68 | 69 | variable "account_request_repo_name" { 70 | type = string 71 | } 72 | 73 | variable "account_request_repo_branch" { 74 | type = string 75 | } 76 | 77 | variable "global_customizations_repo_name" { 78 | type = string 79 | } 80 | 81 | variable "global_customizations_repo_branch" { 82 | type = string 83 | } 84 | 85 | variable "account_customizations_repo_name" { 86 | type = string 87 | } 88 | 89 | variable "account_customizations_repo_branch" { 90 | type = string 91 | } 92 | 93 | variable "account_provisioning_customizations_repo_name" { 94 | type = string 95 | } 96 | 97 | variable "account_provisioning_customizations_repo_branch" { 98 | type = string 99 | } 100 | 101 | variable "global_codebuild_timeout" { 102 | type = number 103 | } 104 | 105 | variable "aft_enable_vpc" { 106 | type = bool 107 | } 108 | 109 | variable "codebuild_compute_type" { 110 | type = string 111 | } 112 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/lambda-invoke-aft-account-provisioning-framework.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version" : "2012-10-17", 3 | "Statement" : [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : [ 7 | "dynamodb:GetItem" 8 | ], 9 | "Resource" : [ 10 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:table/${aws_dynamodb_table_aft-request_name}" 11 | ] 12 | }, 13 | { 14 | "Effect" : "Allow", 15 | "Action" : "ssm:GetParameter", 16 | "Resource" : [ 17 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:parameter/aft/*" 18 | ] 19 | }, 20 | { 21 | "Effect" : "Allow", 22 | "Action" : [ 23 | "sts:AssumeRole", 24 | "sns:Publish" 25 | ], 26 | "Resource" : [ 27 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_aft-management_account_id}:role/AWSAFTAdmin" 28 | ] 29 | }, 30 | { 31 | "Effect" : "Allow", 32 | "Action" : "sts:GetCallerIdentity", 33 | "Resource" : "*" 34 | }, 35 | { 36 | "Effect" : "Allow", 37 | "Action" : "states:StartExecution", 38 | "Resource" : "arn:${data_aws_partition_current_partition}:states:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:stateMachine:${var_aft_account_provisioning_framework_sfn_name}" 39 | }, 40 | { 41 | "Effect" : "Allow", 42 | "Action" : [ 43 | "sns:Publish" 44 | ], 45 | "Resource" : [ 46 | "${aws_sns_topic_aft_notifications_arn}", 47 | "${aws_sns_topic_aft_failure_notifications_arn}" 48 | ] 49 | }, 50 | { 51 | "Effect" : "Allow", 52 | "Action" : [ 53 | "kms:GenerateDataKey", 54 | "kms:Encrypt", 55 | "kms:Decrypt" 56 | ], 57 | "Resource" : [ 58 | "${aws_kms_key_aft_arn}", 59 | "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:alias/aws/sns" 60 | ] 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /modules/aft-code-repositories/codestar.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | resource "aws_codeconnections_connection" "bitbucket" { 6 | count = local.vcs.is_bitbucket ? 1 : 0 7 | name = "ct-aft-bitbucket-connection" 8 | provider_type = "Bitbucket" 9 | } 10 | 11 | resource "aws_codeconnections_connection" "github" { 12 | count = local.vcs.is_github ? 1 : 0 13 | name = "ct-aft-github-connection" 14 | provider_type = "GitHub" 15 | } 16 | 17 | resource "aws_codeconnections_connection" "githubenterprise" { 18 | count = local.vcs.is_github_enterprise ? 1 : 0 19 | name = "ct-aft-github-ent-connection" 20 | host_arn = aws_codeconnections_host.githubenterprise[0].arn 21 | } 22 | 23 | 24 | resource "aws_codeconnections_host" "githubenterprise" { 25 | count = local.vcs.is_github_enterprise ? 1 : 0 26 | name = "github-enterprise-host" 27 | provider_endpoint = var.github_enterprise_url 28 | provider_type = "GitHubEnterpriseServer" 29 | 30 | dynamic "vpc_configuration" { 31 | for_each = var.aft_enable_vpc ? [1] : [] 32 | content { 33 | security_group_ids = var.security_group_ids 34 | subnet_ids = var.subnet_ids 35 | vpc_id = var.vpc_id 36 | } 37 | } 38 | } 39 | 40 | resource "aws_codeconnections_connection" "gitlab" { 41 | count = local.vcs.is_gitlab ? 1 : 0 42 | name = "ct-aft-gitlab-connection" 43 | provider_type = "GitLab" 44 | } 45 | 46 | resource "aws_codeconnections_connection" "gitlabselfmanaged" { 47 | count = local.vcs.is_gitlab_selfmanaged ? 1 : 0 48 | name = "ct-aft-gitlab-selfmgd-connection" 49 | host_arn = aws_codeconnections_host.gitlabselfmanaged[0].arn 50 | } 51 | 52 | resource "aws_codeconnections_host" "gitlabselfmanaged" { 53 | count = local.vcs.is_gitlab_selfmanaged ? 1 : 0 54 | name = "gitlab-selfmanaged-host" 55 | provider_endpoint = var.gitlab_selfmanaged_url 56 | provider_type = "GitLabSelfManaged" 57 | 58 | dynamic "vpc_configuration" { 59 | for_each = var.aft_enable_vpc ? [1] : [] 60 | content { 61 | security_group_ids = var.security_group_ids 62 | subnet_ids = var.subnet_ids 63 | vpc_id = var.vpc_id 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/lambda-account-request-audit-trigger.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version" : "2012-10-17", 3 | "Statement" : [ 4 | { 5 | "Effect" : "Allow", 6 | "Action" : [ 7 | "dynamodb:GetShardIterator", 8 | "dynamodb:DescribeStream", 9 | "dynamodb:GetRecords", 10 | "dynamodb:ListShards", 11 | "dynamodb:ListStreams" 12 | ], 13 | "Resource" : [ 14 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:table/${aws_dynamodb_table_aft-request_name}/stream/*" 15 | ] 16 | }, 17 | { 18 | "Effect": "Allow", 19 | "Action": [ 20 | "dynamodb:Query", 21 | "dynamodb:PutItem" 22 | ], 23 | "Resource": "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:table/${aws_dynamodb_table_aft-request-audit_name}" 24 | }, 25 | { 26 | "Effect" : "Allow", 27 | "Action" : "dynamodb:ListStreams", 28 | "Resource" : "*" 29 | }, 30 | { 31 | "Effect" : "Allow", 32 | "Action" : "sqs:SendMessage", 33 | "Resource" : "${aws_sqs_queue_aft_account_request_arn}" 34 | }, 35 | { 36 | "Effect" : "Allow", 37 | "Action" : "ssm:GetParameter", 38 | "Resource" : [ 39 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:parameter/aft/*" 40 | ] 41 | }, 42 | { 43 | "Effect" : "Allow", 44 | "Action" : [ 45 | "kms:GenerateDataKey", 46 | "kms:Encrypt", 47 | "kms:Decrypt" 48 | ], 49 | "Resource" : [ 50 | "${aws_kms_key_aft_arn}", 51 | "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:alias/aws/sns" 52 | ] 53 | }, 54 | { 55 | "Effect" : "Allow", 56 | "Action" : [ 57 | "sns:Publish" 58 | ], 59 | "Resource" : [ 60 | "${aws_sns_topic_aft_notifications_arn}", 61 | "${aws_sns_topic_aft_failure_notifications_arn}" 62 | ] 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /modules/aft-feature-options/s3/bucket-policies/aft_logging_bucket.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "AWSBucketPermissionsCheck", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": [ 9 | "cloudtrail.amazonaws.com", 10 | "vpc-flow-logs.amazonaws.com", 11 | "delivery.logs.amazonaws.com" 12 | ] 13 | }, 14 | "Action": [ 15 | "s3:ListBucket", 16 | "s3:GetBucketLocation", 17 | "s3:GetBucketAcl" 18 | ], 19 | "Resource": "${aws_s3_bucket_aft_logging_bucket_arn}" 20 | }, 21 | { 22 | "Sid": "AWSBucketPermissionsCheck-AuditAcct", 23 | "Effect": "Allow", 24 | "Principal": "*", 25 | "Action": [ 26 | "s3:ListBucket", 27 | "s3:GetBucketLocation", 28 | "s3:GetBucketAcl" 29 | ], 30 | "Resource": "${aws_s3_bucket_aft_logging_bucket_arn}", 31 | "Condition": { 32 | "StringEquals": { 33 | "aws:PrincipalOrgID": "${data_aws_organizations_organization_aft_organization_id}" 34 | } 35 | } 36 | }, 37 | { 38 | "Sid": "Allow PutObject", 39 | "Effect": "Allow", 40 | "Principal": { 41 | "Service": [ 42 | "cloudtrail.amazonaws.com", 43 | "delivery.logs.amazonaws.com", 44 | "vpc-flow-logs.amazonaws.com" 45 | ] 46 | }, 47 | "Action": "s3:PutObject", 48 | "Resource": [ 49 | "${aws_s3_bucket_aft_logging_bucket_arn}/AWSLogs*", 50 | "${aws_s3_bucket_aft_logging_bucket_arn}/*", 51 | "${aws_s3_bucket_aft_logging_bucket_arn}" 52 | ] 53 | }, 54 | { 55 | "Sid": "Deny non-HTTPS access", 56 | "Effect": "Deny", 57 | "Principal": "*", 58 | "Action": "s3:*", 59 | "Resource": "${aws_s3_bucket_aft_logging_bucket_arn}/*", 60 | "Condition": { 61 | "Bool": { 62 | "aws:SecureTransport": "false" 63 | } 64 | } 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_customizations/aft_customizations_execute_pipeline.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import inspect 5 | import logging 6 | from typing import TYPE_CHECKING, Any, Dict 7 | 8 | import aft_common.ssm 9 | from aft_common import constants as utils 10 | from aft_common import notifications 11 | from aft_common.aft_utils import sanitize_input_for_logging 12 | from aft_common.codepipeline import execute_pipeline 13 | from aft_common.logger import configure_aft_logger, customization_request_logger 14 | from boto3.session import Session 15 | 16 | if TYPE_CHECKING: 17 | from aws_lambda_powertools.utilities.typing import LambdaContext 18 | else: 19 | LambdaContext = object 20 | 21 | configure_aft_logger() 22 | logger = logging.getLogger("aft") 23 | 24 | 25 | def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict[str, Any]: 26 | session = Session() 27 | try: 28 | maximum_concurrent_pipelines = int( 29 | aft_common.ssm.get_ssm_parameter_value( 30 | session, utils.SSM_PARAM_AFT_MAXIMUM_CONCURRENT_CUSTOMIZATIONS 31 | ) 32 | ) 33 | 34 | running_pipelines = int(event["running_executions"]["running_pipelines"]) 35 | pipelines_to_run = maximum_concurrent_pipelines - running_pipelines 36 | accounts = event["targets"]["pending_accounts"] 37 | logger.info("Accounts submitted for execution: " + str(len(accounts))) 38 | for account_id in accounts[:pipelines_to_run]: 39 | execute_pipeline(session, str(account_id)) 40 | accounts.remove(account_id) 41 | logger.info("Accounts remaining to be executed - ") 42 | sanitized_accounts = sanitize_input_for_logging(accounts) 43 | logger.info(sanitized_accounts) 44 | return {"number_pending_accounts": len(accounts), "pending_accounts": accounts} 45 | 46 | except Exception as error: 47 | notifications.send_lambda_failure_sns_message( 48 | session=session, 49 | message=str(error), 50 | context=context, 51 | subject="Failed to trigger one or more AFT account customization pipelines", 52 | ) 53 | message = { 54 | "FILE": __file__.split("/")[-1], 55 | "METHOD": inspect.stack()[0][3], 56 | "EXCEPTION": str(error), 57 | } 58 | logger.exception(message) 59 | raise 60 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/buildspecs/aft-lambda-layer.yml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | version: 0.2 5 | 6 | phases: 7 | install: 8 | runtime-versions: 9 | python: $PYTHON_VERSION 10 | commands: 11 | - DEFAULT_PATH=$(pwd) 12 | - AWS_MODULE_SOURCE=$(aws ssm get-parameter --name $SSM_AWS_MODULE_SOURCE --query "Parameter.Value" --output text) 13 | - AWS_MODULE_GIT_REF=$(aws ssm get-parameter --name $SSM_AWS_MODULE_GIT_REF --query "Parameter.Value" --output text) 14 | # URL Without Access ID 15 | - URL=$(echo "$AWS_MODULE_SOURCE" | awk '{split($0,a,"@"); print a[2]}') 16 | - | 17 | ssh_key_parameter=$(aws ssm get-parameter --name /aft/config/aft-ssh-key --with-decryption 2> /dev/null || echo "None") 18 | if [[ $ssh_key_parameter != "None" ]]; then 19 | ssh_key=$(jq --raw-output ".Parameter.Value" <<< $ssh_key_parameter) 20 | mkdir -p ~/.ssh 21 | echo "Host *" >> ~/.ssh/config 22 | echo "StrictHostKeyChecking no" >> ~/.ssh/config 23 | echo "UserKnownHostsFile=/dev/null" >> ~/.ssh/config 24 | echo "$ssh_key" > ~/.ssh/ssh_key 25 | echo -e "\n\n" >> ~/.ssh/ssh_key 26 | chmod 600 ~/.ssh/ssh_key 27 | eval "$(ssh-agent -s)" 28 | ssh-add ~/.ssh/ssh_key 29 | fi 30 | - git config --global credential.helper '!aws codecommit credential-helper $@' 31 | - git config --global credential.UseHttpPath true 32 | - echo "Building aft_common from ${URL}:${AWS_MODULE_GIT_REF}" 33 | - git clone -b $AWS_MODULE_GIT_REF $AWS_MODULE_SOURCE aws-aft-core-framework 34 | - python3 -m pip install virtualenv 35 | - python3 -m venv .venv 36 | - . .venv/bin/activate 37 | - python3 -m pip install -U pip 38 | - python3 -m pip install --upgrade setuptools 39 | - python3 -m pip install ./aws-aft-core-framework/sources/aft-lambda-layer 40 | - PIP_VERSION=$(python3 -m pip --version | awk '{print $2}') 41 | build: 42 | commands: 43 | - mkdir -p python 44 | - ls 45 | - mv -v ./.venv/lib/ ./python/ 46 | - echo "Removing pip version ${PIP_VERSION} package creating zip" 47 | - rm -rf ./python/lib/python*/site-packages/pip-${PIP_VERSION}.dist-info 48 | - rm -rf ./python/lib/python*/site-packages/pip 49 | - zip -r layer.zip python 50 | - aws s3 cp layer.zip s3://${BUCKET_NAME}/layer.zip 51 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_account_provisioning_framework/aft_account_provisioning_framework_tag_account.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import inspect 5 | from typing import TYPE_CHECKING, Any, Dict 6 | 7 | from aft_common import notifications 8 | from aft_common.account_provisioning_framework import ProvisionRoles, tag_account 9 | from aft_common.auth import AuthClient 10 | from aft_common.logger import customization_request_logger 11 | from boto3.session import Session 12 | 13 | if TYPE_CHECKING: 14 | from aws_lambda_powertools.utilities.typing import LambdaContext 15 | else: 16 | LambdaContext = object 17 | 18 | 19 | def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None: 20 | action = event["action"] 21 | event_payload = event["payload"] 22 | request_id = event_payload["customization_request_id"] 23 | account_info = event_payload["account_info"]["account"] 24 | target_account_id = event_payload["account_info"]["account"]["id"] 25 | 26 | logger = customization_request_logger( 27 | aws_account_id=target_account_id, customization_request_id=request_id 28 | ) 29 | 30 | aft_management_session = Session() 31 | auth = AuthClient() 32 | 33 | try: 34 | rollback = False 35 | try: 36 | if event["rollback"]: 37 | rollback = True 38 | except KeyError: 39 | pass 40 | 41 | ct_management_session = auth.get_ct_management_session( 42 | role_name=ProvisionRoles.SERVICE_ROLE_NAME 43 | ) 44 | 45 | if action == "tag_account": 46 | logger.info("Tag account Organization resource") 47 | tag_account(event_payload, account_info, ct_management_session, rollback) 48 | else: 49 | raise Exception( 50 | f"Incorrect Command Passed to Lambda Function. Input action: {action}. Expected: 'tag_account'" 51 | ) 52 | 53 | except Exception as error: 54 | notifications.send_lambda_failure_sns_message( 55 | session=aft_management_session, 56 | message=str(error), 57 | context=context, 58 | subject="AFT account provisioning failed", 59 | ) 60 | message = { 61 | "FILE": __file__.split("/")[-1], 62 | "METHOD": inspect.stack()[0][3], 63 | "EXCEPTION": str(error), 64 | } 65 | logger.exception(message) 66 | raise 67 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/codebuild.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | resource "aws_cloudwatch_log_group" "codebuild_loggroup" { 6 | name = "/aws/codebuild/${local.common_name}" 7 | retention_in_days = var.cloudwatch_log_group_retention 8 | kms_key_id = var.cloudwatch_log_group_enable_cmk_encryption ? var.aft_kms_key_arn : null 9 | } 10 | 11 | resource "aws_codebuild_project" "codebuild" { 12 | name = local.common_name 13 | description = "Codebuild project to create lambda layer ${var.lambda_layer_name}" 14 | build_timeout = "10" 15 | service_role = aws_iam_role.codebuild.arn 16 | encryption_key = var.aft_kms_key_arn 17 | 18 | artifacts { 19 | type = "NO_ARTIFACTS" 20 | } 21 | 22 | environment { 23 | compute_type = var.codebuild_compute_type 24 | image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0" 25 | type = "LINUX_CONTAINER" 26 | image_pull_credentials_type = "CODEBUILD" 27 | 28 | environment_variable { 29 | name = "PYTHON_VERSION" 30 | value = var.lambda_layer_python_version 31 | } 32 | environment_variable { 33 | name = "BUCKET_NAME" 34 | value = var.s3_bucket_name 35 | } 36 | environment_variable { 37 | name = "SSM_AWS_MODULE_SOURCE" 38 | value = var.aft_tf_aws_customizations_module_url_ssm_path 39 | type = "PLAINTEXT" 40 | } 41 | environment_variable { 42 | name = "SSM_AWS_MODULE_GIT_REF" 43 | value = var.aft_tf_aws_customizations_module_git_ref_ssm_path 44 | type = "PLAINTEXT" 45 | } 46 | 47 | } 48 | 49 | logs_config { 50 | cloudwatch_logs { 51 | group_name = aws_cloudwatch_log_group.codebuild_loggroup.name 52 | stream_name = "build-logs" 53 | } 54 | 55 | s3_logs { 56 | status = "ENABLED" 57 | location = "${var.s3_bucket_name}/aft-lambda-layer-builder-logs" 58 | } 59 | } 60 | 61 | source { 62 | type = "NO_SOURCE" 63 | buildspec = data.local_file.aft_lambda_layer.content 64 | } 65 | 66 | dynamic "vpc_config" { 67 | for_each = var.aft_enable_vpc ? [1] : [] 68 | content { 69 | vpc_id = var.aft_vpc_id 70 | subnets = var.aft_vpc_private_subnets 71 | security_group_ids = var.aft_vpc_default_sg 72 | } 73 | } 74 | 75 | lifecycle { 76 | ignore_changes = [project_visibility] 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_account_provisioning_framework/aft_account_provisioning_framework_persist_metadata.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import inspect 5 | from typing import TYPE_CHECKING, Any, Dict 6 | 7 | from aft_common import notifications 8 | from aft_common.account_provisioning_framework import persist_metadata 9 | from aft_common.logger import customization_request_logger 10 | from boto3.session import Session 11 | 12 | if TYPE_CHECKING: 13 | from aws_lambda_powertools.utilities.typing import LambdaContext 14 | from mypy_boto3_dynamodb.type_defs import PutItemOutputTableTypeDef 15 | else: 16 | PutItemOutputTableTypeDef = object 17 | LambdaContext = object 18 | 19 | 20 | def lambda_handler( 21 | event: Dict[str, Any], context: LambdaContext 22 | ) -> PutItemOutputTableTypeDef: 23 | action = event["action"] 24 | event_payload = event["payload"] 25 | request_id = event_payload["customization_request_id"] 26 | account_info = event_payload["account_info"]["account"] 27 | target_account_id = event_payload["account_info"]["account"]["id"] 28 | logger = customization_request_logger( 29 | aws_account_id=target_account_id, customization_request_id=request_id 30 | ) 31 | 32 | aft_management_session = Session() 33 | try: 34 | rollback = None 35 | 36 | try: 37 | if event["rollback"]: 38 | rollback = True 39 | except KeyError: 40 | pass 41 | 42 | if action == "persist_metadata": 43 | logger.info(f"Managing AFT metadata table entry for target account") 44 | update_metadata = persist_metadata( 45 | event_payload, account_info, aft_management_session 46 | ) 47 | return update_metadata 48 | else: 49 | raise Exception( 50 | f"Incorrect Command Passed to Lambda Function. Input action: {action}. Expected: 'persist_metadata'" 51 | ) 52 | 53 | except Exception as error: 54 | notifications.send_lambda_failure_sns_message( 55 | session=aft_management_session, 56 | message=str(error), 57 | context=context, 58 | subject="AFT account provisioning failed", 59 | ) 60 | message = { 61 | "FILE": __file__.split("/")[-1], 62 | "METHOD": inspect.stack()[0][3], 63 | "EXCEPTION": str(error), 64 | } 65 | logger.exception(message) 66 | raise 67 | -------------------------------------------------------------------------------- /sources/aft-lambda-layer/aft_common/ddb.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import logging 5 | from typing import TYPE_CHECKING, Any, Dict, Optional 6 | 7 | from aft_common.aft_utils import sanitize_input_for_logging 8 | from boto3.dynamodb.types import TypeDeserializer 9 | from boto3.session import Session 10 | 11 | if TYPE_CHECKING: 12 | from mypy_boto3_dynamodb.type_defs import ( 13 | AttributeValueTypeDef, 14 | DeleteItemOutputTableTypeDef, 15 | GetItemOutputTableTypeDef, 16 | PutItemOutputTableTypeDef, 17 | ) 18 | else: 19 | AttributeValueTypeDef = object 20 | GetItemOutputTableTypeDef = object 21 | PutItemOutputTableTypeDef = object 22 | DeleteItemOutputTableTypeDef = object 23 | 24 | logger = logging.getLogger("aft") 25 | 26 | 27 | def get_ddb_item( 28 | session: Session, table_name: str, primary_key: Dict[str, Any] 29 | ) -> Optional[Dict[str, Any]]: 30 | dynamodb = session.resource("dynamodb") 31 | table = dynamodb.Table(table_name) 32 | 33 | logger.info(f"Getting item with key: {primary_key} from table: {table_name}") 34 | response = table.get_item(Key=primary_key) 35 | return response.get("Item", None) 36 | 37 | 38 | def put_ddb_item( 39 | session: Session, table_name: str, item: Dict[str, str] 40 | ) -> PutItemOutputTableTypeDef: 41 | dynamodb = session.resource("dynamodb") 42 | table = dynamodb.Table(table_name) 43 | 44 | logger.info(f"Inserting item into {table_name} table: {str(item)}") 45 | response = table.put_item(Item=item) 46 | sanitized_response = sanitize_input_for_logging(response) 47 | logger.info(sanitized_response) 48 | return response 49 | 50 | 51 | def delete_ddb_item( 52 | session: Session, table_name: str, primary_key: Dict[str, Any] 53 | ) -> DeleteItemOutputTableTypeDef: 54 | dynamodb = session.resource("dynamodb") 55 | table = dynamodb.Table(table_name) 56 | 57 | logger.info(f"Deleting item with key: {primary_key} from: {table_name} table") 58 | response = table.delete_item(Key=primary_key) 59 | sanitized_response = sanitize_input_for_logging(response) 60 | logger.info(sanitized_response) 61 | return response 62 | 63 | 64 | def unmarshal_ddb_item( 65 | low_level_data: Dict[str, AttributeValueTypeDef], 66 | ) -> Dict[str, Any]: 67 | # To go from low-level format to python 68 | 69 | deserializer = TypeDeserializer() 70 | python_data = {k: deserializer.deserialize(v) for k, v in low_level_data.items()} 71 | return python_data 72 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_feature_options/aft_enroll_support.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import inspect 5 | from typing import TYPE_CHECKING, Any, Dict 6 | 7 | import aft_common.ssm 8 | from aft_common import constants as utils 9 | from aft_common import feature_options, notifications 10 | from aft_common.account_provisioning_framework import ProvisionRoles 11 | from aft_common.auth import AuthClient 12 | from aft_common.logger import customization_request_logger 13 | from aft_common.premium_support import account_enrollment_requested, generate_case 14 | from boto3.session import Session 15 | 16 | if TYPE_CHECKING: 17 | from aws_lambda_powertools.utilities.typing import LambdaContext 18 | else: 19 | LambdaContext = object 20 | 21 | 22 | def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None: 23 | request_id = event["customization_request_id"] 24 | target_account_id = event["account_info"]["account"]["id"] 25 | 26 | request_id, target_account_id = ( 27 | feature_options.get_target_account_and_customization_id_from_event(event=event) 28 | ) 29 | 30 | logger = customization_request_logger( 31 | aws_account_id=target_account_id, customization_request_id=request_id 32 | ) 33 | 34 | auth = AuthClient() 35 | aft_session = Session() 36 | try: 37 | ct_mgmt_session = auth.get_ct_management_session( 38 | role_name=ProvisionRoles.SERVICE_ROLE_NAME 39 | ) 40 | 41 | if ( 42 | aft_common.ssm.get_ssm_parameter_value( 43 | aft_session, utils.SSM_PARAM_FEATURE_ENTERPRISE_SUPPORT_ENABLED 44 | ).lower() 45 | == "true" 46 | ): 47 | if not account_enrollment_requested(ct_mgmt_session, target_account_id): 48 | logger.info( 49 | "Generating support case for enrolling target account into AWS Enterprise Support" 50 | ) 51 | generate_case(ct_mgmt_session, target_account_id) 52 | 53 | except Exception as error: 54 | notifications.send_lambda_failure_sns_message( 55 | session=aft_session, 56 | message=str(error), 57 | context=context, 58 | subject="AFT: Failed to enroll into Enterprise Support", 59 | ) 60 | message = { 61 | "FILE": __file__.split("/")[-1], 62 | "METHOD": inspect.stack()[0][3], 63 | "EXCEPTION": str(error), 64 | } 65 | logger.exception(message) 66 | raise 67 | -------------------------------------------------------------------------------- /modules/aft-lambda-layer/iam/role-policies/codebuild.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Action": [ 6 | "lambda:ListLayerVersions", 7 | "lambda:GetLayerVersion", 8 | "lambda:PublishLayerVersion" 9 | ], 10 | "Effect": "Allow", 11 | "Resource": "arn:${data_aws_partition_current_partition}:lambda:${aws_region}:${account_id}:layer:${layer_name}:*" 12 | }, 13 | { 14 | "Effect": "Allow", 15 | "Resource": [ 16 | "*" 17 | ], 18 | "Action": [ 19 | "logs:CreateLogGroup", 20 | "logs:CreateLogStream", 21 | "logs:PutLogEvents", 22 | "events:DisableRule" 23 | ] 24 | }, 25 | { 26 | "Action": [ 27 | "codebuild:StartBuild", 28 | "codebuild:StopBuild", 29 | "codebuild:BatchGet*", 30 | "codebuild:Get*", 31 | "codebuild:List*", 32 | "codecommit:GetBranch", 33 | "codecommit:GetCommit", 34 | "codecommit:GetRepository", 35 | "codecommit:ListBranches" 36 | ], 37 | "Effect": "Allow", 38 | "Resource": "*" 39 | }, 40 | { 41 | "Effect": "Allow", 42 | "Action": [ 43 | "s3:*" 44 | ], 45 | "Resource": [ 46 | "arn:${data_aws_partition_current_partition}:s3:::${s3_bucket_name}", 47 | "arn:${data_aws_partition_current_partition}:s3:::${s3_bucket_name}/*" 48 | ] 49 | }, 50 | { 51 | "Effect": "Allow", 52 | "Action": [ 53 | "ec2:CreateNetworkInterface", 54 | "ec2:DescribeDhcpOptions", 55 | "ec2:DescribeNetworkInterfaces", 56 | "ec2:DeleteNetworkInterface", 57 | "ec2:DescribeSubnets", 58 | "ec2:DescribeSecurityGroups", 59 | "ec2:DescribeVpcs" 60 | ], 61 | "Resource": "*" 62 | }, 63 | { 64 | "Effect": "Allow", 65 | "Action": [ 66 | "ec2:CreateNetworkInterfacePermission" 67 | ], 68 | "Resource": [ 69 | "arn:${data_aws_partition_current_partition}:ec2:${aws_region}:${account_id}:network-interface/*" 70 | ] 71 | }, 72 | { 73 | "Effect": "Allow", 74 | "Action": [ 75 | "ssm:GetParameters", 76 | "ssm:GetParameter" 77 | ], 78 | "Resource": [ 79 | "arn:${data_aws_partition_current_partition}:ssm:${aws_region}:${account_id}:parameter/aft/*" 80 | ] 81 | }, 82 | { 83 | "Effect": "Allow", 84 | "Action": [ 85 | "kms:Decrypt", 86 | "kms:GenerateDataKey" 87 | ], 88 | "Resource": "${data_aws_kms_alias_aft_key_target_key_arn}" 89 | } 90 | ] 91 | } 92 | -------------------------------------------------------------------------------- /sources/aft-customizations-repos/aft-global-customizations/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | This repo stores the Terraform and API helpers for the Global Customizations. Global Customizations are used to customize all provisioned accounts with customer defined resources. The resources can be created through Terraform or through Python, leveraging the API helpers. The customization run is parameterized at runtime. 3 | 4 | # Usage 5 | To leverage Global Customizations, populate this repo as per the instructions below. 6 | 7 | ## Terraform 8 | AFT provides Jinja templates for Terraform backend and providers. These render at the time Terraform is applied. If needed, additional providers can be defined by creating a providers.tf file. 9 | 10 | To create Terraform resources, provide your own Terraform files (ex. main.tf, variables.tf, etc) with the resources you would like to create, placing them in the 'terraform' directory. 11 | 12 | ## API Helpers 13 | The purpose of API helpers is to perform actions that cannot be performed within Terraform. 14 | 15 | ### Python 16 | The api_helpers/python folder contains a requirements.txt, where you can specify libraries/packages to be installed via PIP. 17 | 18 | ### Bash 19 | This is where you define what runs before/after Terraform, as well as the order the Python scripts execute, along with any command line parameters. These bash scripts can be extended to perform other actions, such as leveraging the AWS CLI or performing additional/custom Bash scripting. 20 | 21 | - pre-api-helpers.sh - Actions to execute prior to running Terraform. 22 | - post-api-helpers.sh - Actions to execute after running Terraform. 23 | 24 | ### Sample api-helpers.sh 25 | 26 | Sample #1 - Using AWS CLI to query for resources, save to a variable, and then pass to a script. In the example below, all running instances are queried, stopped, and started using AWS CLI and custom Python scritpts. 27 | ``` 28 | instances=$(aws ec2 describe-instances --filters "Name=instance-state-name,Values=running") 29 | python ./python/source/stop_instances.py --instances $instances 30 | sleep 10s 31 | python ./python/source/start_instances.py --instances $instances 32 | ``` 33 | 34 | Sample #2 - Query a 3rd party IPAM solution, and save the given CIDR to AWS Parameter Store. This SSM parameter could be leveraged from Terraform using a data object to create a VPC. 35 | ``` 36 | account = $(aws sts get-caller-identity --query Account --output text) 37 | region = $(aws ec2 describe-availability-zones --query 'AvailabilityZones[0].[RegionName]' --output text) 38 | cidr = $(python ./python/source/get_cidr_range.py) 39 | aws ssm put-parameter --name /$account/$region/vpc/cidr --value $cidr 40 | ``` 41 | -------------------------------------------------------------------------------- /modules/aft-customizations/iam/role-policies/aft_states_invoke_customizations_policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "lambda:InvokeFunction" 8 | ], 9 | "Resource": [ 10 | "arn:${data_aws_partition_current_partition}:lambda:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:function:aft-*" 11 | ] 12 | }, 13 | { 14 | "Effect": "Allow", 15 | "Action": [ 16 | "sns:Publish" 17 | ], 18 | "Resource": [ 19 | "arn:${data_aws_partition_current_partition}:sns:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:aft-*" 20 | ] 21 | }, 22 | { 23 | "Effect": "Allow", 24 | "Action": [ 25 | "kms:GenerateDataKey*", 26 | "kms:Encrypt", 27 | "kms:Decrypt" 28 | ], 29 | "Resource": [ 30 | "${aws_kms_key_aft_arn}" 31 | ] 32 | }, 33 | { 34 | "Effect": "Allow", 35 | "Action": [ 36 | "events:PutTargets", 37 | "events:PutRule", 38 | "events:DescribeRule" 39 | ], 40 | "Resource": [ 41 | "arn:${data_aws_partition_current_partition}:events:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:*" 42 | ] 43 | }, 44 | { 45 | "Effect": "Allow", 46 | "Action": [ 47 | "states:StartExecution" 48 | ], 49 | "Resource": [ 50 | "arn:${data_aws_partition_current_partition}:states:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:stateMachine:aft-*" 51 | ] 52 | }, 53 | { 54 | "Effect": "Allow", 55 | "Action": [ 56 | "states:DescribeExecution", 57 | "states:StopExecution" 58 | ], 59 | "Resource": [ 60 | "arn:${data_aws_partition_current_partition}:states:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:execution:aft-*" 61 | ] 62 | }, 63 | { 64 | "Effect": "Allow", 65 | "Action": [ 66 | "s3:GetObject*" 67 | ], 68 | "Resource": [ 69 | "${aws_s3_bucket_aft_codepipeline_customizations_bucket_arn}/sfn/*" 70 | ] 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /src/aft_lambda/aft_builder/codebuild_trigger.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | import datetime 5 | import inspect 6 | import logging 7 | import re 8 | import time 9 | from typing import Any, Dict, TypedDict 10 | 11 | from boto3.session import Session 12 | 13 | logger = logging.getLogger() 14 | logger.setLevel(level=logging.INFO) 15 | 16 | 17 | class LayerBuildStatus(TypedDict): 18 | Status: int 19 | 20 | 21 | # This function is directly responsible for building `aft_common` library 22 | # Do not import `aft_common` into this handler! 23 | def lambda_handler(event: Dict[str, Any], context: Dict[str, Any]) -> LayerBuildStatus: 24 | session = Session() 25 | try: 26 | client = session.client("codebuild") 27 | 28 | codebuild_project_name = event["codebuild_project_name"] 29 | job_id = client.start_build(projectName=codebuild_project_name)["build"]["id"] 30 | sanitized_codebuild_project_name = re.sub( 31 | r"[^a-zA-Z0-9-_]", "", codebuild_project_name 32 | ) 33 | sanitized_job_id = re.sub(r"[^a-zA-Z0-9-_]", "", job_id) 34 | logger.info( 35 | f"Started build project {sanitized_codebuild_project_name} job {sanitized_job_id}" 36 | ) 37 | # Wait at least 30 seconds for the build to initialize 38 | time.sleep(30) 39 | 40 | # 15min Lambda hard-timeout, soft-timeout at 14min 41 | end_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=14) 42 | while datetime.datetime.utcnow() <= end_time: 43 | # We pass exactly 1 job ID, so the build list should contain exactly 1 object 44 | job_status = client.batch_get_builds(ids=[job_id])["builds"][0][ 45 | "buildStatus" 46 | ] 47 | if job_status == "IN_PROGRESS": 48 | time.sleep(10) 49 | continue 50 | elif job_status == "SUCCEEDED": 51 | logger.info(f"Build job {sanitized_job_id} completed successfully") 52 | return {"Status": 200} 53 | else: 54 | logger.info( 55 | f"Build {sanitized_job_id} failed - non-success terminal status" 56 | ) 57 | raise Exception(f"Build {job_id} failed - non-success terminal status") 58 | logger.info(f"Build {sanitized_job_id} failed - time out") 59 | raise Exception(f"Build {job_id} failed - time out") 60 | 61 | except Exception as error: 62 | message = { 63 | "FILE": __file__.split("/")[-1], 64 | "METHOD": inspect.stack()[0][3], 65 | "EXCEPTION": str(error), 66 | } 67 | logger.exception(message) 68 | raise 69 | -------------------------------------------------------------------------------- /modules/aft-account-request-framework/iam/role-policies/lambda-aft-cleanup-resources.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "dynamodb:GetItem", 8 | "dynamodb:DeleteItem" 9 | ], 10 | "Resource": [ 11 | "arn:${data_aws_partition_current_partition}:dynamodb:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:table/${aws_dynamodb_table_aft-request-metadata_name}" 12 | ] 13 | }, 14 | { 15 | "Effect": "Allow", 16 | "Action": [ 17 | "codepipeline:ListPipelineExecutions", 18 | "codepipeline:ListPipelines", 19 | "codepipeline:ListTagsForResource", 20 | "codepipeline:DeletePipeline" 21 | ], 22 | "Resource": [ 23 | "arn:${data_aws_partition_current_partition}:codepipeline:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:*" 24 | ] 25 | }, 26 | { 27 | "Effect": "Allow", 28 | "Action": "ssm:GetParameter", 29 | "Resource": [ 30 | "arn:${data_aws_partition_current_partition}:ssm:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:parameter/aft/*" 31 | ] 32 | }, 33 | { 34 | "Effect": "Allow", 35 | "Action": [ 36 | "sts:AssumeRole" 37 | ], 38 | "Resource": [ 39 | "arn:${data_aws_partition_current_partition}:iam::${data_aws_caller_identity_aft-management_account_id}:role/AWSAFTAdmin" 40 | ] 41 | }, 42 | { 43 | "Effect": "Allow", 44 | "Action": "sts:GetCallerIdentity", 45 | "Resource": "*" 46 | }, 47 | { 48 | "Effect": "Allow", 49 | "Action": [ 50 | "sns:Publish" 51 | ], 52 | "Resource": [ 53 | "${aws_sns_topic_aft_notifications_arn}", 54 | "${aws_sns_topic_aft_failure_notifications_arn}" 55 | ] 56 | }, 57 | { 58 | "Effect": "Allow", 59 | "Action": [ 60 | "kms:GenerateDataKey", 61 | "kms:Encrypt", 62 | "kms:Decrypt" 63 | ], 64 | "Resource": [ 65 | "${aws_kms_key_aft_arn}", 66 | "arn:${data_aws_partition_current_partition}:kms:${data_aws_region_aft-management_name}:${data_aws_caller_identity_aft-management_account_id}:alias/aws/sns" 67 | ] 68 | } 69 | ] 70 | } 71 | --------------------------------------------------------------------------------