├── multi-account ├── spoke-account │ ├── .header.md │ ├── outputs.tf │ ├── .terraform-docs.yaml │ ├── providers.tf │ ├── variables.tf │ ├── README.md │ └── main.tf ├── security-account │ ├── .header.md │ ├── outputs.tf │ ├── .terraform-docs.yaml │ ├── providers.tf │ ├── variables.tf │ ├── README.md │ └── main.tf ├── networking-account │ ├── .header.md │ ├── outputs.tf │ ├── .terraform-docs.yaml │ ├── providers.tf │ ├── variables.tf │ ├── README.md │ └── main.tf ├── modules │ └── compute │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── variables.tf │ │ └── main.tf └── README.md ├── images ├── diagrams.pptx ├── multi_account.png ├── single_account_eastwest.png ├── single_account_centralizedegress.png ├── .$diagrams.drawio.bkp └── diagrams.drawio ├── CODE_OF_CONDUCT.md ├── single-account ├── modules │ ├── compute │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── variables.tf │ │ └── main.tf │ └── iam_kms │ │ ├── providers.tf │ │ ├── variables.tf │ │ ├── outputs.tf │ │ └── main.tf ├── east_west │ ├── .terraform-docs.yaml │ ├── providers.tf │ ├── outputs.tf │ ├── .header.md │ ├── variables.tf │ ├── policy.tf │ ├── main.tf │ └── README.md ├── centralized_egress │ ├── .terraform-docs.yaml │ ├── providers.tf │ ├── outputs.tf │ ├── .header.md │ ├── variables.tf │ ├── policy.tf │ ├── README.md │ └── main.tf └── README.md ├── LICENSE ├── README.md ├── .gitignore └── CONTRIBUTING.md /multi-account/spoke-account/.header.md: -------------------------------------------------------------------------------- 1 | # Spoke AWS Account -------------------------------------------------------------------------------- /multi-account/security-account/.header.md: -------------------------------------------------------------------------------- 1 | # Security AWS Account -------------------------------------------------------------------------------- /multi-account/networking-account/.header.md: -------------------------------------------------------------------------------- 1 | # Networking AWS Account -------------------------------------------------------------------------------- /images/diagrams.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/hub-and-spoke-with-inspection-vpc-terraform/HEAD/images/diagrams.pptx -------------------------------------------------------------------------------- /images/multi_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/hub-and-spoke-with-inspection-vpc-terraform/HEAD/images/multi_account.png -------------------------------------------------------------------------------- /images/single_account_eastwest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/hub-and-spoke-with-inspection-vpc-terraform/HEAD/images/single_account_eastwest.png -------------------------------------------------------------------------------- /images/single_account_centralizedegress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/hub-and-spoke-with-inspection-vpc-terraform/HEAD/images/single_account_centralizedegress.png -------------------------------------------------------------------------------- /multi-account/spoke-account/outputs.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- spoke-account/outputs.tf --- -------------------------------------------------------------------------------- /multi-account/security-account/outputs.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- security-account/outputs.tf --- -------------------------------------------------------------------------------- /multi-account/networking-account/outputs.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- networking-account/outputs.tf --- 5 | 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /multi-account/modules/compute/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- multi-account/modules/compute/outputs.tf --- 5 | 6 | output "ec2_instances" { 7 | value = aws_instance.ec2_instance 8 | description = "List of instances created." 9 | } -------------------------------------------------------------------------------- /single-account/modules/compute/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/compute/outputs.tf --- 5 | 6 | output "ec2_instances" { 7 | value = aws_instance.ec2_instance 8 | description = "List of instances created." 9 | } -------------------------------------------------------------------------------- /single-account/east_west/.terraform-docs.yaml: -------------------------------------------------------------------------------- 1 | formatter: markdown 2 | header-from: .header.md 3 | settings: 4 | anchor: true 5 | color: true 6 | default: true 7 | escape: true 8 | html: true 9 | indent: 2 10 | required: true 11 | sensitive: true 12 | type: true 13 | 14 | sort: 15 | enabled: true 16 | by: required 17 | 18 | output: 19 | file: README.md 20 | mode: replace -------------------------------------------------------------------------------- /multi-account/spoke-account/.terraform-docs.yaml: -------------------------------------------------------------------------------- 1 | formatter: markdown 2 | header-from: .header.md 3 | settings: 4 | anchor: true 5 | color: true 6 | default: true 7 | escape: true 8 | html: true 9 | indent: 2 10 | required: true 11 | sensitive: true 12 | type: true 13 | 14 | sort: 15 | enabled: true 16 | by: required 17 | 18 | output: 19 | file: README.md 20 | mode: replace -------------------------------------------------------------------------------- /multi-account/modules/compute/providers.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- multi-account/modules/compute/providers.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = ">= 3.73.0" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /multi-account/networking-account/.terraform-docs.yaml: -------------------------------------------------------------------------------- 1 | formatter: markdown 2 | header-from: .header.md 3 | settings: 4 | anchor: true 5 | color: true 6 | default: true 7 | escape: true 8 | html: true 9 | indent: 2 10 | required: true 11 | sensitive: true 12 | type: true 13 | 14 | sort: 15 | enabled: true 16 | by: required 17 | 18 | output: 19 | file: README.md 20 | mode: replace -------------------------------------------------------------------------------- /multi-account/security-account/.terraform-docs.yaml: -------------------------------------------------------------------------------- 1 | formatter: markdown 2 | header-from: .header.md 3 | settings: 4 | anchor: true 5 | color: true 6 | default: true 7 | escape: true 8 | html: true 9 | indent: 2 10 | required: true 11 | sensitive: true 12 | type: true 13 | 14 | sort: 15 | enabled: true 16 | by: required 17 | 18 | output: 19 | file: README.md 20 | mode: replace -------------------------------------------------------------------------------- /single-account/centralized_egress/.terraform-docs.yaml: -------------------------------------------------------------------------------- 1 | formatter: markdown 2 | header-from: .header.md 3 | settings: 4 | anchor: true 5 | color: true 6 | default: true 7 | escape: true 8 | html: true 9 | indent: 2 10 | required: true 11 | sensitive: true 12 | type: true 13 | 14 | sort: 15 | enabled: true 16 | by: required 17 | 18 | output: 19 | file: README.md 20 | mode: replace -------------------------------------------------------------------------------- /single-account/modules/compute/providers.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/compute/providers.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = ">= 3.73.0" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /single-account/modules/iam_kms/providers.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/iam_kms/outputs.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = ">= 3.73.0" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /single-account/modules/iam_kms/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/iam_kms/outputs.tf --- 5 | 6 | variable "identifier" { 7 | type = string 8 | description = "Project identifier." 9 | } 10 | 11 | variable "aws_region" { 12 | type = string 13 | description = "AWS Region." 14 | } -------------------------------------------------------------------------------- /single-account/modules/iam_kms/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/iam_kms/outputs.tf --- 5 | 6 | output "vpc_flowlogs_role" { 7 | value = aws_iam_role.vpc_flowlogs_role.id 8 | description = "VPC Flow Logs IAM Role." 9 | } 10 | 11 | output "kms_key" { 12 | value = aws_kms_key.log_key.arn 13 | description = "KMS Key - to encrypt VPC Flow Logs." 14 | } -------------------------------------------------------------------------------- /multi-account/spoke-account/providers.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- spoke-account/providers.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "= 5.16.2" 12 | } 13 | } 14 | } 15 | 16 | # Provider definition for Spoke AWS Account 17 | provider "aws" { 18 | region = var.aws_region 19 | } -------------------------------------------------------------------------------- /multi-account/security-account/providers.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- security-account/providers.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "= 5.16.2" 12 | } 13 | } 14 | } 15 | 16 | # Provider definition for Security AWS Account 17 | provider "aws" { 18 | region = var.aws_region 19 | } -------------------------------------------------------------------------------- /multi-account/networking-account/providers.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- networking-account/providers.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "= 5.16.2" 12 | } 13 | } 14 | } 15 | 16 | # Provider definition for Networking AWS Account 17 | provider "aws" { 18 | region = var.aws_region 19 | } -------------------------------------------------------------------------------- /single-account/east_west/providers.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/east_west/providers.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "> 5.0.0" 12 | } 13 | } 14 | } 15 | 16 | # AWS Provider configuration 17 | provider "aws" { 18 | region = var.aws_region 19 | } 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /single-account/centralized_egress/providers.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/centralized_egress/providers.tf --- 5 | 6 | terraform { 7 | required_version = ">= 1.3.0" 8 | required_providers { 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "> 5.0.0" 12 | } 13 | } 14 | } 15 | 16 | # AWS Provider configuration 17 | provider "aws" { 18 | region = var.aws_region 19 | } 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /images/.$diagrams.drawio.bkp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /multi-account/modules/compute/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- multi-account/modules/compute/variables.tf --- 5 | 6 | variable "identifier" { 7 | type = string 8 | description = "Project identifier." 9 | } 10 | 11 | variable "vpc_name" { 12 | type = string 13 | description = "Name of the VPC where the EC2 instance(s) are created." 14 | } 15 | 16 | variable "vpc" { 17 | type = any 18 | description = "VPC resources." 19 | } 20 | 21 | variable "vpc_information" { 22 | type = any 23 | description = "VPC information (defined in root variables.tf file)." 24 | } -------------------------------------------------------------------------------- /single-account/modules/compute/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/compute/variables.tf --- 5 | 6 | variable "identifier" { 7 | type = string 8 | description = "Project identifier." 9 | } 10 | 11 | variable "vpc_name" { 12 | type = string 13 | description = "Name of the VPC where the EC2 instance(s) are created." 14 | } 15 | 16 | variable "vpc" { 17 | type = any 18 | description = "VPC resources." 19 | } 20 | 21 | variable "vpc_information" { 22 | type = any 23 | description = "VPC information (defined in root variables.tf file)." 24 | } -------------------------------------------------------------------------------- /multi-account/security-account/variables.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- security-account/variables.tf --- 5 | 6 | variable "identifier" { 7 | type = string 8 | description = "Account Identifier." 9 | 10 | default = "security-account" 11 | } 12 | 13 | variable "aws_region" { 14 | type = string 15 | description = "AWS Region." 16 | 17 | default = "eu-west-1" 18 | } 19 | 20 | variable "network_supernet" { 21 | type = string 22 | description = "Network supernet." 23 | 24 | default = "10.0.0.0/16" 25 | } 26 | 27 | variable "secret_name" { 28 | type = string 29 | description = "AWS Secrets Manager secret name." 30 | 31 | default = "security-account-firewall-policy-arn" 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # AWS Hub and Spoke Architecture with an Inspection VPC - Terraform 3 | 4 | This repository contains terraform code to deploy a sample AWS Hub and Spoke architecture with an Inspection VPC using AWS Network Firewall. The resources deployed and the architectural pattern they follow is purely for demonstration/testing purposes. 5 | 6 | You will find two examples: the architecture built in a single AWS Account, and in a multi-Account environment. 7 | 8 | * [Single AWS Account](./single-account/) 9 | 10 | ![Single-Account centralized egress diagram](./images/single_account_centralizedegress.png) 11 | 12 | ![Single-Account east-west diagram](./images/single_account_eastwest.png) 13 | 14 | * [Multi-AWS Account](./multi-account/) 15 | 16 | ![Architecture diagram](./images/multi_account.png) 17 | 18 | ## Security 19 | 20 | See [CONTRIBUTING](../CONTRIBUTING.md) for more information. 21 | 22 | ## License 23 | 24 | This library is licensed under the MIT-0 License. See the [LICENSE](../LICENSE) file. -------------------------------------------------------------------------------- /multi-account/networking-account/variables.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- networking-account/variables.tf --- 5 | 6 | variable "identifier" { 7 | type = string 8 | description = "Account Identifier." 9 | 10 | default = "networking-account" 11 | } 12 | 13 | variable "aws_region" { 14 | type = string 15 | description = "AWS Region." 16 | 17 | default = "eu-west-1" 18 | } 19 | 20 | variable "secrets_names" { 21 | type = map(string) 22 | description = "AWS Secrets Manager secrets name." 23 | 24 | default = { 25 | security_account = "security-account-firewall-policy-arn" 26 | networking_account_tgw = "networking-account-transit-gateway-id" 27 | networking_account_ipam = "networking-account-ipam-pool-id" 28 | networking_account_attachments = "network-account-vpc-attachments" 29 | } 30 | } 31 | 32 | variable "security_account" { 33 | type = string 34 | description = "Security Account ID." 35 | } -------------------------------------------------------------------------------- /single-account/east_west/outputs.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/east_west/outputs.tf --- 5 | 6 | output "vpcs" { 7 | description = "VPCs created." 8 | value = { 9 | spokes = { for k, v in module.spoke_vpcs : k => v.vpc_attributes.id } 10 | } 11 | } 12 | 13 | output "transit_gateway_id" { 14 | description = "AWS Transit Gateway ID." 15 | value = aws_ec2_transit_gateway.tgw.id 16 | } 17 | 18 | output "transit_gateway_route_tables" { 19 | description = "Transit Gateway Route Table." 20 | value = { 21 | inspection = aws_ec2_transit_gateway_route_table.tgw_route_table_inspection.id 22 | spoke = aws_ec2_transit_gateway_route_table.tgw_route_table_spoke.id 23 | } 24 | } 25 | 26 | output "instances" { 27 | description = "EC2 instances created." 28 | value = { for k, v in module.compute : k => v.ec2_instances.*.id } 29 | } 30 | 31 | output "network_firewall" { 32 | description = "AWS Network Firewall ID." 33 | value = module.network_firewall.aws_network_firewall.id 34 | } 35 | -------------------------------------------------------------------------------- /single-account/centralized_egress/outputs.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/centralized_egress/outputs.tf --- 5 | 6 | output "vpcs" { 7 | description = "VPCs created." 8 | value = { 9 | spokes = { for k, v in module.spoke_vpcs : k => v.vpc_attributes.id } 10 | inspection = module.inspection_vpc.vpc_attributes.id 11 | } 12 | } 13 | 14 | output "transit_gateway_id" { 15 | description = "AWS Transit Gateway ID." 16 | value = aws_ec2_transit_gateway.tgw.id 17 | } 18 | 19 | output "transit_gateway_route_tables" { 20 | description = "Transit Gateway Route Table." 21 | value = { 22 | inspection = aws_ec2_transit_gateway_route_table.tgw_route_table_inspection.id 23 | spoke = aws_ec2_transit_gateway_route_table.tgw_route_table_spoke.id 24 | } 25 | } 26 | 27 | output "instances" { 28 | description = "EC2 instances created." 29 | value = { for k, v in module.compute : k => v.ec2_instances.*.id } 30 | } 31 | 32 | output "network_firewall" { 33 | description = "AWS Network Firewall ID." 34 | value = module.network_firewall.aws_network_firewall.id 35 | } 36 | -------------------------------------------------------------------------------- /single-account/centralized_egress/.header.md: -------------------------------------------------------------------------------- 1 | # AWS Hub and Spoke Architecture with an Inspection VPC - Single AWS Account (Centralized Egress) 2 | 3 | ## Prerequisites 4 | * An AWS account with an IAM user with the appropriate permissions 5 | * Terraform installed 6 | 7 | ## Code Principles: 8 | * Writing DRY (Do No Repeat Yourself) code using a modular design pattern 9 | 10 | ## Usage 11 | * Clone the repository 12 | * Edit the variables.tf file in the project root directory. This file contains the information used to configure the Terraform code. 13 | 14 | **Note** EC2 instances, and AWS Network Firewall endpoints will be deployed in all the Availability Zones configured for each VPC. Keep this in mind when testing this environment from a cost perspective - for production environments, we recommend the use of at least 2 AZs for high-availability. 15 | 16 | ## Target Architecture 17 | 18 | ![Architecture diagram](../../images/single_account_centralizedegress.png) 19 | 20 | ## Deployment 21 | 22 | * `terraform init` to initialize the environment. 23 | * `terraform plan` to check the resources to create 24 | * `terraform apply` to build the architecture. 25 | 26 | ## Clean-up 27 | 28 | * `terraform destroy` will clean-up the resources created. -------------------------------------------------------------------------------- /single-account/east_west/.header.md: -------------------------------------------------------------------------------- 1 | # AWS Hub and Spoke Architecture with an Inspection VPC - Single AWS Account (East-West) 2 | 3 | ## Prerequisites 4 | * An AWS account with an IAM user with the appropriate permissions 5 | * Terraform installed 6 | 7 | ## Code Principles: 8 | * Writing DRY (Do No Repeat Yourself) code using a modular design pattern 9 | 10 | ## Usage 11 | * Clone the repository 12 | * Edit the variables.tf file in the project root directory. This file contains the information used to configure the Terraform code. 13 | 14 | **Note** EC2 instances will be deployed in all the Availability Zones configured for each VPC, and AWS Network Firewall will be deployed in all the AWS Region's Availability Zones. Keep this in mind when testing this environment from a cost perspective - for production environments, we recommend the use of at least 2 AZs for high-availability. 15 | 16 | ## Target Architecture 17 | 18 | ![Architecture diagram](../../images/single_account_eastwest.png) 19 | 20 | ## Deployment 21 | 22 | * `terraform init` to initialize the environment. 23 | * `terraform plan` to check the resources to create 24 | * `terraform apply` to build the architecture. 25 | 26 | ## Clean-up 27 | 28 | * `terraform destroy` will clean-up the resources created. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/terraform 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=terraform 4 | 5 | ### Terraform ### 6 | # Local .terraform directories 7 | **/.terraform/* 8 | 9 | # .tfstate files 10 | *.tfstate 11 | *.tfstate.* 12 | *.lock.hcl 13 | # Crash log files 14 | crash.log 15 | 16 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 17 | # password, private keys, and other secrets. These should not be part of version 18 | # control as they are data points which are potentially sensitive and subject 19 | # to change depending on the environment. 20 | # 21 | *.tfvars 22 | 23 | # Ignore override files as they are usually used to override resources locally and so 24 | # are not checked in 25 | override.tf 26 | override.tf.json 27 | *_override.tf 28 | *_override.tf.json 29 | 30 | # Include override files you do wish to add to version control using negated pattern 31 | # !example_override.tf 32 | 33 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 34 | # example: *tfplan* 35 | 36 | # Ignore CLI configuration files 37 | .terraformrc 38 | terraform.rc 39 | 40 | # End of https://www.toptal.com/developers/gitignore/api/terraform 41 | 42 | # macOs 43 | .DS_Store 44 | 45 | # Project 46 | keys/ 47 | 48 | gcm-diagnose.log 49 | 50 | -------------------------------------------------------------------------------- /multi-account/spoke-account/variables.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- spoke-account/variables.tf --- 5 | 6 | variable "identifier" { 7 | type = string 8 | description = "Account Identifier." 9 | 10 | default = "spoke-account" 11 | } 12 | 13 | variable "aws_region" { 14 | type = string 15 | description = "AWS Region." 16 | 17 | default = "eu-west-1" 18 | } 19 | 20 | variable "secrets_names" { 21 | type = map(string) 22 | description = "AWS Secrets Manager secrets name." 23 | 24 | default = { 25 | networking_account_tgw = "networking-account-transit-gateway-id" 26 | networking_account_ipam = "networking-account-ipam-pool-id" 27 | networking_account_attachments = "network-account-vpc-attachments" 28 | } 29 | } 30 | 31 | variable "vpcs" { 32 | type = any 33 | description = "VPC information." 34 | default = { 35 | vpc1 = { 36 | routing_domain = "prod" 37 | number_azs = 2 38 | instance_type = "t3.micro" 39 | } 40 | vpc2 = { 41 | routing_domain = "nonprod" 42 | number_azs = 2 43 | instance_type = "t3.micro" 44 | } 45 | } 46 | } 47 | 48 | variable "networking_account" { 49 | type = string 50 | description = "Networking Account ID." 51 | } -------------------------------------------------------------------------------- /single-account/east_west/variables.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/east_west/variables.tf --- 5 | 6 | # Variables that define project configuration 7 | variable "aws_region" { 8 | description = "AWS Region." 9 | type = string 10 | default = "eu-west-1" 11 | } 12 | 13 | variable "identifier" { 14 | description = "Name of the project." 15 | type = string 16 | default = "singleaccount-eastwest" 17 | } 18 | 19 | # Spoke VPCs 20 | variable "spoke_vpcs" { 21 | description = "Spoke VPCs definition." 22 | type = any 23 | 24 | default = { 25 | "spoke-vpc-1" = { 26 | cidr_block = "10.0.0.0/24" 27 | workload_subnet_netmask = 28 28 | endpoint_subnet_netmask = 28 29 | tgw_subnet_netmask = 28 30 | number_azs = 2 31 | instance_type = "t2.micro" 32 | 33 | flow_log_config = { 34 | log_destination_type = "cloud-watch-logs" 35 | retention_in_days = 7 36 | } 37 | } 38 | 39 | "spoke-vpc-2" = { 40 | cidr_block = "10.0.1.0/24" 41 | workload_subnet_netmask = 28 42 | endpoint_subnet_netmask = 28 43 | tgw_subnet_netmask = 28 44 | number_azs = 2 45 | instance_type = "t2.micro" 46 | 47 | flow_log_config = { 48 | log_destination_type = "cloud-watch-logs" 49 | retention_in_days = 7 50 | } 51 | } 52 | } 53 | } 54 | 55 | # Inspection VPC 56 | variable "inspection_vpc" { 57 | description = "Inspection VPC definition." 58 | type = any 59 | 60 | default = { 61 | cidr_block = "10.129.0.0/24" 62 | public_subnet_netmask = 28 63 | private_subnet_netmask = 28 64 | tgw_subnet_netmask = 28 65 | number_azs = 2 66 | } 67 | } -------------------------------------------------------------------------------- /single-account/centralized_egress/variables.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/centralized_egress/variables.tf --- 5 | 6 | # Variables that define project configuration 7 | variable "aws_region" { 8 | description = "AWS Region." 9 | type = string 10 | default = "eu-west-1" 11 | } 12 | 13 | variable "identifier" { 14 | description = "Name of the project." 15 | type = string 16 | default = "singleaccount-centralizedegress" 17 | } 18 | 19 | # Spoke VPCs 20 | variable "spoke_vpcs" { 21 | description = "Spoke VPCs definition." 22 | type = any 23 | 24 | default = { 25 | "spoke-vpc-1" = { 26 | cidr_block = "10.0.0.0/24" 27 | workload_subnet_netmask = 28 28 | endpoint_subnet_netmask = 28 29 | tgw_subnet_netmask = 28 30 | number_azs = 2 31 | instance_type = "t2.micro" 32 | 33 | flow_log_config = { 34 | log_destination_type = "cloud-watch-logs" 35 | retention_in_days = 7 36 | } 37 | } 38 | 39 | "spoke-vpc-2" = { 40 | cidr_block = "10.0.1.0/24" 41 | workload_subnet_netmask = 28 42 | endpoint_subnet_netmask = 28 43 | tgw_subnet_netmask = 28 44 | number_azs = 2 45 | instance_type = "t2.micro" 46 | 47 | flow_log_config = { 48 | log_destination_type = "cloud-watch-logs" 49 | retention_in_days = 7 50 | } 51 | } 52 | } 53 | } 54 | 55 | # Inspection VPC 56 | variable "inspection_vpc" { 57 | description = "Inspection VPC definition." 58 | type = any 59 | 60 | default = { 61 | cidr_block = "10.129.0.0/24" 62 | public_subnet_netmask = 28 63 | private_subnet_netmask = 28 64 | tgw_subnet_netmask = 28 65 | number_azs = 2 66 | } 67 | } -------------------------------------------------------------------------------- /single-account/README.md: -------------------------------------------------------------------------------- 1 | # AWS Hub and Spoke Architecture with an Inspection VPC - Single AWS Account 2 | 3 | This repository contains terraform code to deploy a sample AWS Hub and Spoke architecture with an Inspection VPC using AWS Network Firewall. The resources deployed and the architectural pattern they follow is purely for demonstration/testing purposes. You can find two different examples: 4 | 5 | 1. [Centralized egress](./centralized_egress/), where there's a single Inspection VPC attached to the Transit Gateway with both AWS Network Firewall endpoints and NAT gateways. This pattern is also recommended when wanting to use the firewall both for egress and east-west traffic inspection. 6 | 7 | ![Architecture diagram - Centralized egress](../images/single_account_centralizedegress.png) 8 | 9 | 2. [East-West](./east_west/), where we use the [native TGW attachment](https://docs.aws.amazon.com/network-firewall/latest/developerguide/tgw-firewall.html) in Network Firewall to reduce the operational overhead creating a central Inspection VPC. 10 | 11 | ![Architecture diagram - Centralized egress](../images/single_account_eastwest.png) 12 | 13 | ## Infrastructure configuration 14 | 15 | ### AWS Network Firewall Policy 16 | 17 | The AWS Network Firewall Policy is defined in the *policy.tf* file in the network_firewall module directory. The policy configuration will vary depending the example: 18 | 19 | * All the SSH and RDP traffic is blocked by the Stateless engine. 20 | * The Stateful engine follows Strict Rule Ordering. 21 | * For **centralized egress**, all traffic is blocked except HTTPS traffic to *aws.amazon.com*. 22 | * For **east-west**, all traffic is blocked except ICMP traffic between the spoke VPCs attached to the Transit Gateway. 23 | 24 | ### VPC Flow Logs configuration 25 | 26 | This project configures both the alert and flow logs to respective AWS Cloudwatch Log Groups (both for the VPC Flow logs and AWS Network Firewall logs). In VPC Flow logs, you can also use Amazon S3. In Network Firewall, you can also use Amazon S3, or Amazon Kinesis Firehose. 27 | 28 | To follow best practices, all the logs are encrypted at rest using AWS KMS. The KMS key (alongside the IAM roles needed) is created using the *iam\_kms* module. 29 | 30 | ## Security 31 | 32 | See [CONTRIBUTING](../CONTRIBUTING.md) for more information. 33 | 34 | ## License 35 | 36 | This library is licensed under the MIT-0 License. See the [LICENSE](../LICENSE) file. 37 | -------------------------------------------------------------------------------- /single-account/modules/iam_kms/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/iam_kms/main.tf --- 5 | 6 | # DATA SOURCE: AWS CALLER IDENTITY - Used to get the Account ID 7 | data "aws_caller_identity" "current" {} 8 | 9 | # ---------- VPC FLOW LOG - IAM ROLE AND KMS KEY ---------- 10 | # IAM Role 11 | resource "aws_iam_role" "vpc_flowlogs_role" { 12 | name = "vpc-flowlog-role-${var.identifier}" 13 | assume_role_policy = data.aws_iam_policy_document.policy_role_document.json 14 | } 15 | 16 | data "aws_iam_policy_document" "policy_role_document" { 17 | statement { 18 | sid = "1" 19 | actions = ["sts:AssumeRole"] 20 | 21 | principals { 22 | type = "Service" 23 | identifiers = ["vpc-flow-logs.amazonaws.com"] 24 | } 25 | } 26 | } 27 | 28 | # IAM Role Policy 29 | resource "aws_iam_role_policy" "vpc_flowlogs_role_policy" { 30 | name = "vpc-flowlog-role-policy-${var.identifier}" 31 | role = aws_iam_role.vpc_flowlogs_role.id 32 | policy = data.aws_iam_policy_document.policy_rolepolicy_document.json 33 | } 34 | 35 | data "aws_iam_policy_document" "policy_rolepolicy_document" { 36 | statement { 37 | sid = "2" 38 | actions = [ 39 | "logs:CreateLogGroup", 40 | "logs:CreateLogStream", 41 | "logs:PutLogEvents", 42 | "logs:DescribeLogGroup", 43 | "logs:DescribeLogStreams" 44 | ] 45 | resources = ["arn:aws:logs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"] 46 | } 47 | } 48 | 49 | # KMS Key 50 | resource "aws_kms_key" "log_key" { 51 | description = "KMS Logs Key" 52 | deletion_window_in_days = 7 53 | enable_key_rotation = true 54 | policy = data.aws_iam_policy_document.policy_kms_logs_document.json 55 | 56 | tags = { 57 | Name = "kms-key-${var.identifier}" 58 | } 59 | } 60 | 61 | # KMS Policy - it allows the use of the Key by the CloudWatch log groups created in this sample 62 | data "aws_iam_policy_document" "policy_kms_logs_document" { 63 | statement { 64 | sid = "Enable IAM User Permissions" 65 | actions = ["kms:*"] 66 | resources = ["arn:aws:kms:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"] 67 | 68 | principals { 69 | type = "AWS" 70 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 71 | } 72 | } 73 | 74 | statement { 75 | sid = "Enable KMS to be used by CloudWatch Logs" 76 | actions = [ 77 | "kms:Encrypt*", 78 | "kms:Decrypt*", 79 | "kms:ReEncrypt*", 80 | "kms:GenerateDataKey*", 81 | "kms:Describe*" 82 | ] 83 | resources = ["arn:aws:kms:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"] 84 | 85 | principals { 86 | type = "Service" 87 | identifiers = ["logs.${var.aws_region}.amazonaws.com"] 88 | } 89 | 90 | condition { 91 | test = "ArnLike" 92 | variable = "kms:EncryptionContext:aws:logs:arn" 93 | values = ["arn:aws:logs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"] 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /multi-account/spoke-account/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Spoke AWS Account 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.3.0 | 9 | | [aws](#requirement\_aws) | = 5.16.2 | 10 | 11 | ## Providers 12 | 13 | | Name | Version | 14 | |------|---------| 15 | | [aws](#provider\_aws) | = 5.16.2 | 16 | 17 | ## Modules 18 | 19 | | Name | Source | Version | 20 | |------|--------|---------| 21 | | [compute](#module\_compute) | ../modules/compute | n/a | 22 | | [vpcs](#module\_vpcs) | aws-ia/vpc/aws | 4.4.2 | 23 | 24 | ## Resources 25 | 26 | | Name | Type | 27 | |------|------| 28 | | [aws_secretsmanager_secret_version.vpc_information](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret_version) | resource | 29 | | [aws_caller_identity.aws_spoke_account](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/caller_identity) | data source | 30 | | [aws_secretsmanager_secret.ipam_pool_id](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/secretsmanager_secret) | data source | 31 | | [aws_secretsmanager_secret.tgw_id](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/secretsmanager_secret) | data source | 32 | | [aws_secretsmanager_secret.vpc_information](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/secretsmanager_secret) | data source | 33 | | [aws_secretsmanager_secret_version.ipam_pool_id](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/secretsmanager_secret_version) | data source | 34 | | [aws_secretsmanager_secret_version.tgw_id](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/secretsmanager_secret_version) | data source | 35 | 36 | ## Inputs 37 | 38 | | Name | Description | Type | Default | Required | 39 | |------|-------------|------|---------|:--------:| 40 | | [networking\_account](#input\_networking\_account) | Networking Account ID. | `string` | n/a | yes | 41 | | [aws\_region](#input\_aws\_region) | AWS Region. | `string` | `"eu-west-1"` | no | 42 | | [identifier](#input\_identifier) | Account Identifier. | `string` | `"spoke-account"` | no | 43 | | [secrets\_names](#input\_secrets\_names) | AWS Secrets Manager secrets name. | `map(string)` |
{
"networking_account_attachments": "network-account-vpc-attachments",
"networking_account_ipam": "networking-account-ipam-pool-id",
"networking_account_tgw": "networking-account-transit-gateway-id"
}
| no | 44 | | [vpcs](#input\_vpcs) | VPC information. | `any` |
{
"vpc1": {
"instance_type": "t3.micro",
"number_azs": 2,
"routing_domain": "prod"
},
"vpc2": {
"instance_type": "t3.micro",
"number_azs": 2,
"routing_domain": "nonprod"
}
}
| no | 45 | 46 | ## Outputs 47 | 48 | No outputs. 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /single-account/east_west/policy.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/east_west/policy.tf --- 5 | 6 | resource "aws_networkfirewall_firewall_policy" "anfw_policy" { 7 | name = "firewall-policy-${var.identifier}" 8 | 9 | firewall_policy { 10 | # Stateless configuration 11 | stateless_default_actions = ["aws:forward_to_sfe"] 12 | stateless_fragment_default_actions = ["aws:forward_to_sfe"] 13 | 14 | stateless_rule_group_reference { 15 | priority = 10 16 | resource_arn = aws_networkfirewall_rule_group.drop_remote.arn 17 | } 18 | 19 | # Stateful configuration 20 | stateful_engine_options { 21 | rule_order = "STRICT_ORDER" 22 | } 23 | stateful_default_actions = ["aws:drop_strict", "aws:alert_strict"] 24 | stateful_rule_group_reference { 25 | priority = 10 26 | resource_arn = aws_networkfirewall_rule_group.allow_icmp.arn 27 | } 28 | } 29 | } 30 | 31 | # Stateless Rule Group - Dropping any SSH connection 32 | resource "aws_networkfirewall_rule_group" "drop_remote" { 33 | capacity = 2 34 | name = "drop-remote-${var.identifier}" 35 | type = "STATELESS" 36 | rule_group { 37 | rules_source { 38 | stateless_rules_and_custom_actions { 39 | # Block SSH (port 22) 40 | stateless_rule { 41 | priority = 1 42 | rule_definition { 43 | actions = ["aws:drop"] 44 | match_attributes { 45 | protocols = [6] 46 | source { 47 | address_definition = "0.0.0.0/0" 48 | } 49 | destination { 50 | address_definition = "0.0.0.0/0" 51 | } 52 | destination_port { 53 | from_port = 22 54 | to_port = 22 55 | } 56 | } 57 | } 58 | } 59 | 60 | # Block RDP (port 3389) 61 | stateless_rule { 62 | priority = 2 63 | rule_definition { 64 | actions = ["aws:drop"] 65 | match_attributes { 66 | protocols = [6] 67 | source { 68 | address_definition = "0.0.0.0/0" 69 | } 70 | destination { 71 | address_definition = "0.0.0.0/0" 72 | } 73 | destination_port { 74 | from_port = 3389 75 | to_port = 3389 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | # Stateful Rule Group - Allowing ICMP traffic 86 | resource "aws_networkfirewall_rule_group" "allow_icmp" { 87 | capacity = 1 88 | name = "allow-icmp-${var.identifier}" 89 | type = "STATEFUL" 90 | rule_group { 91 | rule_variables { 92 | ip_sets { 93 | key = "SUPERNET" 94 | ip_set { 95 | definition = ["10.0.0.0/16"] 96 | } 97 | } 98 | } 99 | rules_source { 100 | rules_string = < $SUPERNET any (msg: "Allowing ICMP packets"; sid:2; rev:1;) 102 | EOF 103 | } 104 | stateful_rule_options { 105 | rule_order = "STRICT_ORDER" 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /multi-account/modules/compute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- multi-account/modules/compute/main.tf --- 5 | 6 | # ---------- EC2 INSTANCES ---------- 7 | # Security Group 8 | resource "aws_security_group" "instance_sg" { 9 | name = "${var.vpc_name}-instance-security-group-${var.identifier}" 10 | description = "EC2 Instance Security Group" 11 | vpc_id = var.vpc.vpc_attributes.id 12 | } 13 | 14 | resource "aws_vpc_security_group_ingress_rule" "allowing_ingress_icmp" { 15 | security_group_id = aws_security_group.instance_sg.id 16 | 17 | from_port = -1 18 | to_port = -1 19 | ip_protocol = "icmp" 20 | cidr_ipv4 = "0.0.0.0/0" 21 | } 22 | 23 | resource "aws_vpc_security_group_ingress_rule" "allowing_ingress_eic" { 24 | security_group_id = aws_security_group.instance_sg.id 25 | 26 | from_port = 22 27 | to_port = 22 28 | ip_protocol = "tcp" 29 | referenced_security_group_id = aws_security_group.eic_sg.id 30 | } 31 | 32 | resource "aws_vpc_security_group_egress_rule" "allowing_egress_any" { 33 | security_group_id = aws_security_group.instance_sg.id 34 | 35 | ip_protocol = "-1" 36 | cidr_ipv4 = "0.0.0.0/0" 37 | } 38 | 39 | # Data resource to determine the latest Amazon Linux 2023 AMI 40 | data "aws_ami" "amazon_linux" { 41 | most_recent = true 42 | owners = ["amazon"] 43 | 44 | filter { 45 | name = "name" 46 | values = ["al2023-ami-2023.*-x86_64"] 47 | } 48 | } 49 | 50 | # EC2 instances 51 | resource "aws_instance" "ec2_instance" { 52 | count = var.vpc_information.number_azs 53 | 54 | ami = data.aws_ami.amazon_linux.id 55 | associate_public_ip_address = false 56 | instance_type = var.vpc_information.instance_type 57 | vpc_security_group_ids = [aws_security_group.instance_sg.id] 58 | subnet_id = values({ for k, v in var.vpc.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "private" })[count.index] 59 | 60 | metadata_options { 61 | http_endpoint = "enabled" 62 | http_tokens = "required" 63 | } 64 | 65 | root_block_device { 66 | encrypted = true 67 | } 68 | 69 | tags = { 70 | Name = "${var.vpc_name}-instance-${count.index + 1}-${var.identifier}" 71 | } 72 | } 73 | 74 | # ---------- EC2 INSTANCE CONNECT ---------- 75 | # Security Group 76 | resource "aws_security_group" "eic_sg" { 77 | name = "${var.vpc_name}-eic-security-group-${var.identifier}" 78 | description = "EC2 Instance Connect Security Group" 79 | vpc_id = var.vpc.vpc_attributes.id 80 | } 81 | 82 | resource "aws_vpc_security_group_egress_rule" "allowing_egress_ec2_instances" { 83 | security_group_id = aws_security_group.eic_sg.id 84 | 85 | from_port = 22 86 | to_port = 22 87 | ip_protocol = "tcp" 88 | referenced_security_group_id = aws_security_group.instance_sg.id 89 | } 90 | 91 | # EC2 Instance Connect endpoint 92 | resource "aws_ec2_instance_connect_endpoint" "eic_endpoint" { 93 | subnet_id = values({ for k, v in var.vpc.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "endpoints" })[0] 94 | preserve_client_ip = false 95 | security_group_ids = [aws_security_group.eic_sg.id] 96 | } -------------------------------------------------------------------------------- /multi-account/spoke-account/main.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- spoke-account/main.tf --- 5 | 6 | # ---------- AWS ORGANIZATIONS AND ACCOUNT INFORMATION ---------- 7 | data "aws_caller_identity" "aws_spoke_account" {} 8 | 9 | # ---------- AMAZON VPCs ---------- 10 | # We obtain the Transit Gateway ID and IPAM Pool ID information 11 | data "aws_secretsmanager_secret" "tgw_id" { 12 | arn = "arn:aws:secretsmanager:${var.aws_region}:${var.networking_account}:secret:${var.secrets_names.networking_account_tgw}" 13 | } 14 | 15 | data "aws_secretsmanager_secret_version" "tgw_id" { 16 | secret_id = data.aws_secretsmanager_secret.tgw_id.id 17 | } 18 | 19 | data "aws_secretsmanager_secret" "ipam_pool_id" { 20 | arn = "arn:aws:secretsmanager:${var.aws_region}:${var.networking_account}:secret:${var.secrets_names.networking_account_ipam}" 21 | } 22 | 23 | data "aws_secretsmanager_secret_version" "ipam_pool_id" { 24 | secret_id = data.aws_secretsmanager_secret.ipam_pool_id.id 25 | } 26 | 27 | # VPCs 28 | module "vpcs" { 29 | source = "aws-ia/vpc/aws" 30 | version = "4.4.2" 31 | for_each = var.vpcs 32 | 33 | name = each.key 34 | az_count = 3 35 | vpc_ipv4_ipam_pool_id = data.aws_secretsmanager_secret_version.ipam_pool_id.secret_string 36 | vpc_ipv4_netmask_length = 24 37 | 38 | transit_gateway_id = data.aws_secretsmanager_secret_version.tgw_id.secret_string 39 | transit_gateway_routes = { 40 | private = "0.0.0.0/0" 41 | } 42 | 43 | subnets = { 44 | endpoints = { netmask = 28 } 45 | private = { netmask = 28 } 46 | transit_gateway = { 47 | netmask = 28 48 | 49 | tags = { domain = var.vpcs[each.key].routing_domain } 50 | } 51 | } 52 | 53 | tags = { 54 | domain = each.value.routing_domain 55 | } 56 | } 57 | 58 | # ---------- INCLUDE VPC INFORMATION IN SECRET ---------- 59 | # We retrieve the Secrets Manager secret created by the Central Account 60 | data "aws_secretsmanager_secret" "vpc_information" { 61 | arn = "arn:aws:secretsmanager:${var.aws_region}:${var.networking_account}:secret:${var.secrets_names.networking_account_attachments}" 62 | } 63 | 64 | # We generate the secret we want to pass - with the Spoke VPCs information 65 | locals { 66 | vpc_information = { 67 | spoke_account = { 68 | id = data.aws_caller_identity.aws_spoke_account.id 69 | number_spoke_vpcs = length(var.vpcs) 70 | vpc_information = { for k, v in module.vpcs : k => { 71 | vpc_id = v.vpc_attributes.id 72 | transit_gateway_attachment_id = v.transit_gateway_attachment_id 73 | routing_domain = var.vpcs[k].routing_domain 74 | } } 75 | } 76 | } 77 | } 78 | 79 | # We add the secret value to the secret 80 | resource "aws_secretsmanager_secret_version" "vpc_information" { 81 | secret_id = data.aws_secretsmanager_secret.vpc_information.id 82 | secret_string = jsonencode(local.vpc_information) 83 | } 84 | 85 | # ---------- EC2 INSTANCES ---------- 86 | module "compute" { 87 | source = "../modules/compute" 88 | for_each = module.vpcs 89 | 90 | identifier = var.identifier 91 | vpc_name = each.key 92 | vpc = each.value 93 | vpc_information = var.vpcs[each.key] 94 | } -------------------------------------------------------------------------------- /single-account/modules/compute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | # --- single-account/modules/compute/main.tf --- 5 | 6 | # ---------- EC2 INSTANCES ---------- 7 | # Security Group 8 | resource "aws_security_group" "instance_sg" { 9 | name = "${var.vpc_name}-instance-security-group-${var.identifier}" 10 | description = "EC2 Instance Security Group" 11 | vpc_id = var.vpc.vpc_attributes.id 12 | } 13 | 14 | resource "aws_vpc_security_group_ingress_rule" "allowing_ingress_icmp" { 15 | security_group_id = aws_security_group.instance_sg.id 16 | 17 | from_port = -1 18 | to_port = -1 19 | ip_protocol = "icmp" 20 | cidr_ipv4 = "0.0.0.0/0" 21 | } 22 | 23 | resource "aws_vpc_security_group_ingress_rule" "allowing_ingress_eic" { 24 | security_group_id = aws_security_group.instance_sg.id 25 | 26 | from_port = 22 27 | to_port = 22 28 | ip_protocol = "tcp" 29 | referenced_security_group_id = aws_security_group.eic_sg.id 30 | } 31 | 32 | resource "aws_vpc_security_group_egress_rule" "allowing_egress_any" { 33 | security_group_id = aws_security_group.instance_sg.id 34 | 35 | ip_protocol = "-1" 36 | cidr_ipv4 = "0.0.0.0/0" 37 | } 38 | 39 | # Data resource to determine the latest Amazon Linux 2023 AMI 40 | data "aws_ami" "amazon_linux" { 41 | most_recent = true 42 | owners = ["amazon"] 43 | 44 | filter { 45 | name = "name" 46 | values = ["al2023-ami-2023.*-x86_64"] 47 | } 48 | } 49 | 50 | # EC2 instances 51 | resource "aws_instance" "ec2_instance" { 52 | count = var.vpc_information.number_azs 53 | 54 | ami = data.aws_ami.amazon_linux.id 55 | associate_public_ip_address = false 56 | instance_type = var.vpc_information.instance_type 57 | vpc_security_group_ids = [aws_security_group.instance_sg.id] 58 | subnet_id = values({ for k, v in var.vpc.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "workload" })[count.index] 59 | 60 | metadata_options { 61 | http_endpoint = "enabled" 62 | http_tokens = "required" 63 | } 64 | 65 | root_block_device { 66 | encrypted = true 67 | } 68 | 69 | tags = { 70 | Name = "${var.vpc_name}-instance-${count.index + 1}-${var.identifier}" 71 | } 72 | } 73 | 74 | # ---------- EC2 INSTANCE CONNECT ---------- 75 | # Security Group 76 | resource "aws_security_group" "eic_sg" { 77 | name = "${var.vpc_name}-eic-security-group-${var.identifier}" 78 | description = "EC2 Instance Connect Security Group" 79 | vpc_id = var.vpc.vpc_attributes.id 80 | } 81 | 82 | resource "aws_vpc_security_group_egress_rule" "allowing_egress_ec2_instances" { 83 | security_group_id = aws_security_group.eic_sg.id 84 | 85 | from_port = 22 86 | to_port = 22 87 | ip_protocol = "tcp" 88 | referenced_security_group_id = aws_security_group.instance_sg.id 89 | } 90 | 91 | # EC2 Instance Connect endpoint 92 | resource "aws_ec2_instance_connect_endpoint" "eic_endpoint" { 93 | subnet_id = values({ for k, v in var.vpc.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "endpoints" })[0] 94 | preserve_client_ip = false 95 | security_group_ids = [aws_security_group.eic_sg.id] 96 | } -------------------------------------------------------------------------------- /single-account/centralized_egress/policy.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/centralized_egress/policy.tf --- 5 | 6 | resource "aws_networkfirewall_firewall_policy" "anfw_policy" { 7 | name = "firewall-policy-${var.identifier}" 8 | 9 | firewall_policy { 10 | # Stateless configuration 11 | stateless_default_actions = ["aws:forward_to_sfe"] 12 | stateless_fragment_default_actions = ["aws:forward_to_sfe"] 13 | 14 | stateless_rule_group_reference { 15 | priority = 10 16 | resource_arn = aws_networkfirewall_rule_group.drop_remote.arn 17 | } 18 | 19 | # Stateful configuration 20 | stateful_engine_options { 21 | rule_order = "STRICT_ORDER" 22 | } 23 | stateful_default_actions = ["aws:drop_established", "aws:alert_established"] 24 | stateful_rule_group_reference { 25 | priority = 10 26 | resource_arn = aws_networkfirewall_rule_group.allow_domains.arn 27 | } 28 | } 29 | } 30 | 31 | # Stateless Rule Group - Dropping any SSH connection 32 | resource "aws_networkfirewall_rule_group" "drop_remote" { 33 | capacity = 2 34 | name = "drop-remote-${var.identifier}" 35 | type = "STATELESS" 36 | rule_group { 37 | rules_source { 38 | stateless_rules_and_custom_actions { 39 | # Block SSH (port 22) 40 | stateless_rule { 41 | priority = 1 42 | rule_definition { 43 | actions = ["aws:drop"] 44 | match_attributes { 45 | protocols = [6] 46 | source { 47 | address_definition = "0.0.0.0/0" 48 | } 49 | destination { 50 | address_definition = "0.0.0.0/0" 51 | } 52 | destination_port { 53 | from_port = 22 54 | to_port = 22 55 | } 56 | } 57 | } 58 | } 59 | 60 | # Block RDP (port 3389) 61 | stateless_rule { 62 | priority = 2 63 | rule_definition { 64 | actions = ["aws:drop"] 65 | match_attributes { 66 | protocols = [6] 67 | source { 68 | address_definition = "0.0.0.0/0" 69 | } 70 | destination { 71 | address_definition = "0.0.0.0/0" 72 | } 73 | destination_port { 74 | from_port = 3389 75 | to_port = 3389 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | # Stateful Rule Group - Allowing access to .amazon.com (HTTPS) 86 | resource "aws_networkfirewall_rule_group" "allow_domains" { 87 | capacity = 100 88 | name = "allow-domains-${var.identifier}" 89 | type = "STATEFUL" 90 | rule_group { 91 | rule_variables { 92 | ip_sets { 93 | key = "HOME_NET" 94 | ip_set { 95 | definition = values({ for k, v in var.spoke_vpcs : k => v.cidr_block }) 96 | } 97 | } 98 | } 99 | rules_source { 100 | rules_source_list { 101 | generated_rules_type = "ALLOWLIST" 102 | target_types = ["TLS_SNI"] 103 | targets = ["aws.amazon.com"] 104 | } 105 | } 106 | stateful_rule_options { 107 | rule_order = "STRICT_ORDER" 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /multi-account/security-account/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Security AWS Account 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.3.0 | 9 | | [aws](#requirement\_aws) | = 5.16.2 | 10 | 11 | ## Providers 12 | 13 | | Name | Version | 14 | |------|---------| 15 | | [aws](#provider\_aws) | = 5.16.2 | 16 | 17 | ## Modules 18 | 19 | No modules. 20 | 21 | ## Resources 22 | 23 | | Name | Type | 24 | |------|------| 25 | | [aws_kms_key.secrets_key](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/kms_key) | resource | 26 | | [aws_networkfirewall_firewall_policy.central_inspection_policy](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/networkfirewall_firewall_policy) | resource | 27 | | [aws_networkfirewall_rule_group.allow_domains](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/networkfirewall_rule_group) | resource | 28 | | [aws_networkfirewall_rule_group.allow_tcp](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/networkfirewall_rule_group) | resource | 29 | | [aws_networkfirewall_rule_group.drop_remote](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/networkfirewall_rule_group) | resource | 30 | | [aws_ram_principal_association.principal_association](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/ram_principal_association) | resource | 31 | | [aws_ram_resource_association.firewall_policy_share](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/ram_resource_association) | resource | 32 | | [aws_ram_resource_share.resource_share](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/ram_resource_share) | resource | 33 | | [aws_secretsmanager_secret.firewall_policy_arn](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret) | resource | 34 | | [aws_secretsmanager_secret_version.firewall_policy_arn](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret_version) | resource | 35 | | [aws_caller_identity.aws_security_account](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/caller_identity) | data source | 36 | | [aws_iam_policy_document.policy_kms_document](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/iam_policy_document) | data source | 37 | | [aws_iam_policy_document.secrets_resource_policy](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/iam_policy_document) | data source | 38 | | [aws_organizations_organization.org](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/organizations_organization) | data source | 39 | 40 | ## Inputs 41 | 42 | | Name | Description | Type | Default | Required | 43 | |------|-------------|------|---------|:--------:| 44 | | [aws\_region](#input\_aws\_region) | AWS Region. | `string` | `"eu-west-1"` | no | 45 | | [identifier](#input\_identifier) | Account Identifier. | `string` | `"security-account"` | no | 46 | | [network\_supernet](#input\_network\_supernet) | Network supernet. | `string` | `"10.0.0.0/16"` | no | 47 | | [secret\_name](#input\_secret\_name) | AWS Secrets Manager secret name. | `string` | `"security-account-firewall-policy-arn"` | no | 48 | 49 | ## Outputs 50 | 51 | No outputs. 52 | -------------------------------------------------------------------------------- /multi-account/networking-account/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Networking AWS Account 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.3.0 | 9 | | [aws](#requirement\_aws) | = 5.16.2 | 10 | 11 | ## Providers 12 | 13 | | Name | Version | 14 | |------|---------| 15 | | [aws](#provider\_aws) | = 5.16.2 | 16 | 17 | ## Modules 18 | 19 | | Name | Source | Version | 20 | |------|--------|---------| 21 | | [hubspoke](#module\_hubspoke) | aws-ia/network-hubandspoke/aws | 3.2.0 | 22 | | [ipam](#module\_ipam) | aws-ia/ipam/aws | 2.0.0 | 23 | 24 | ## Resources 25 | 26 | | Name | Type | 27 | |------|------| 28 | | [aws_kms_key.secrets_key](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/kms_key) | resource | 29 | | [aws_ram_principal_association.principal_association](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/ram_principal_association) | resource | 30 | | [aws_ram_resource_association.transit_gateway_share](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/ram_resource_association) | resource | 31 | | [aws_ram_resource_share.resource_share](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/ram_resource_share) | resource | 32 | | [aws_secretsmanager_secret.ipam_pool](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret) | resource | 33 | | [aws_secretsmanager_secret.spoke_attachments](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret) | resource | 34 | | [aws_secretsmanager_secret.transit_gateway](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret) | resource | 35 | | [aws_secretsmanager_secret_version.ipam_pool](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret_version) | resource | 36 | | [aws_secretsmanager_secret_version.transit_gateway](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/resources/secretsmanager_secret_version) | resource | 37 | | [aws_caller_identity.aws_networking_account](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/caller_identity) | data source | 38 | | [aws_iam_policy_document.policy_kms_document](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/iam_policy_document) | data source | 39 | | [aws_iam_policy_document.secrets_resource_policy_read](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/iam_policy_document) | data source | 40 | | [aws_iam_policy_document.secrets_resource_policy_write](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/iam_policy_document) | data source | 41 | | [aws_organizations_organization.org](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/organizations_organization) | data source | 42 | | [aws_secretsmanager_secret.firewall_policy_arn](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/secretsmanager_secret) | data source | 43 | | [aws_secretsmanager_secret_version.firewall_policy_arn](https://registry.terraform.io/providers/hashicorp/aws/5.16.2/docs/data-sources/secretsmanager_secret_version) | data source | 44 | 45 | ## Inputs 46 | 47 | | Name | Description | Type | Default | Required | 48 | |------|-------------|------|---------|:--------:| 49 | | [security\_account](#input\_security\_account) | Security Account ID. | `string` | n/a | yes | 50 | | [aws\_region](#input\_aws\_region) | AWS Region. | `string` | `"eu-west-1"` | no | 51 | | [identifier](#input\_identifier) | Account Identifier. | `string` | `"networking-account"` | no | 52 | | [secrets\_names](#input\_secrets\_names) | AWS Secrets Manager secrets name. | `map(string)` |
{
"networking_account_attachments": "network-account-vpc-attachments",
"networking_account_ipam": "networking-account-ipam-pool-id",
"networking_account_tgw": "networking-account-transit-gateway-id",
"security_account": "security-account-firewall-policy-arn"
}
| no | 53 | 54 | ## Outputs 55 | 56 | No outputs. 57 | -------------------------------------------------------------------------------- /single-account/east_west/main.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/east_west/main.tf --- 5 | 6 | data "aws_availability_zones" "azs" { 7 | state = "available" 8 | } 9 | 10 | # ---------- HUB AND SPOKE WITH CENTRAL INSPECTION (AWS NETWORK FIREWALL) ---------- 11 | # AWS Transit Gateway 12 | resource "aws_ec2_transit_gateway" "tgw" { 13 | description = "Transit Gateway - ${var.identifier}" 14 | default_route_table_association = "disable" 15 | default_route_table_propagation = "disable" 16 | 17 | tags = { Name = "tgw-${var.identifier}" } 18 | } 19 | 20 | # AWS Network Firewall (native attachment) 21 | resource "aws_networkfirewall_firewall" "anfw" { 22 | name = "anfw-${var.identifier}" 23 | firewall_policy_arn = aws_networkfirewall_firewall_policy.anfw_policy.arn 24 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 25 | 26 | dynamic "availability_zone_mapping" { 27 | for_each = { for index, v in toset(data.aws_availability_zones.azs.zone_ids) : index => v } 28 | iterator = az 29 | 30 | content { 31 | availability_zone_id = az.value 32 | } 33 | } 34 | } 35 | 36 | # ---------- SPOKE VPCs ---------- 37 | # Amazon VPC Module - https://registry.terraform.io/modules/aws-ia/vpc/aws/latest 38 | module "spoke_vpcs" { 39 | for_each = var.spoke_vpcs 40 | source = "aws-ia/vpc/aws" 41 | version = "= 4.5.0" 42 | 43 | name = each.key 44 | cidr_block = each.value.cidr_block 45 | az_count = each.value.number_azs 46 | 47 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 48 | transit_gateway_routes = { 49 | workload = "0.0.0.0/0" 50 | } 51 | 52 | subnets = { 53 | workload = { netmask = each.value.workload_subnet_netmask } 54 | endpoints = { netmask = each.value.endpoint_subnet_netmask } 55 | transit_gateway = { 56 | netmask = each.value.tgw_subnet_netmask 57 | transit_gateway_default_route_table_association = false 58 | transit_gateway_default_route_table_propagation = false 59 | } 60 | } 61 | 62 | vpc_flow_logs = { 63 | log_destination_type = each.value.flow_log_config.log_destination_type 64 | retention_in_days = each.value.flow_log_config.retention_in_days 65 | iam_role_arn = module.iam_kms.vpc_flowlogs_role 66 | kms_key_id = module.iam_kms.kms_key 67 | } 68 | } 69 | 70 | # ---------- TRANSIT GATEWAY ROUTING ---------- 71 | # Spoke Route Table 72 | # Spoke VPCs associated 73 | # Static route: 0.0.0.0/0 --> Inspection VPC attachment 74 | resource "aws_ec2_transit_gateway_route_table" "tgw_route_table_spoke" { 75 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 76 | 77 | tags = { Name = "spoke-rt-${var.identifier}" } 78 | } 79 | 80 | resource "aws_ec2_transit_gateway_route_table_association" "tgw_spoke_association" { 81 | for_each = var.spoke_vpcs 82 | 83 | transit_gateway_attachment_id = module.spoke_vpcs[each.key].transit_gateway_attachment_id 84 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_spoke.id 85 | } 86 | 87 | resource "aws_ec2_transit_gateway_route" "tgw_route_default_to_inspection" { 88 | destination_cidr_block = "0.0.0.0/0" 89 | transit_gateway_attachment_id = aws_networkfirewall_firewall.anfw.firewall_status[0].transit_gateway_attachment_sync_states[0].attachment_id 90 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_spoke.id 91 | } 92 | 93 | # Inspection Route Table 94 | # Inspection VPC associated 95 | # Spoke VPCs propagating 96 | resource "aws_ec2_transit_gateway_route_table" "tgw_route_table_inspection" { 97 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 98 | 99 | tags = { Name = "inspection-rt-${var.identifier}" } 100 | } 101 | 102 | resource "aws_ec2_transit_gateway_route_table_association" "tgw_inspection_association" { 103 | transit_gateway_attachment_id = aws_networkfirewall_firewall.anfw.firewall_status[0].transit_gateway_attachment_sync_states[0].attachment_id 104 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_inspection.id 105 | } 106 | 107 | resource "aws_ec2_transit_gateway_route_table_propagation" "tgw_spoke_propagation_inspection" { 108 | for_each = var.spoke_vpcs 109 | 110 | transit_gateway_attachment_id = module.spoke_vpcs[each.key].transit_gateway_attachment_id 111 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_inspection.id 112 | } 113 | 114 | # ---------- EC2 INSTANCES & EC2 INSTANCE CONNECT ENDPOINT ---------- 115 | module "compute" { 116 | source = "../modules/compute" 117 | for_each = module.spoke_vpcs 118 | 119 | identifier = var.identifier 120 | vpc_name = each.key 121 | vpc = each.value 122 | vpc_information = var.spoke_vpcs[each.key] 123 | } 124 | 125 | # ---------- IAM ROLE (SSM ACCESS & VPC FLOW LOGS) AND KMS KEY (VPC FLOW LOGS) ---------- 126 | module "iam_kms" { 127 | source = "../modules/iam_kms" 128 | 129 | identifier = var.identifier 130 | aws_region = var.aws_region 131 | } -------------------------------------------------------------------------------- /multi-account/README.md: -------------------------------------------------------------------------------- 1 | # AWS Hub and Spoke Architecture with an Inspection VPC - Multi-AWS Accounts 2 | 3 | This repository contains terraform code to deploy a sample AWS Hub and Spoke architecture with an Inspection VPC using AWS Network Firewall - in a multi-Account environment. The resources deployed and the architectural pattern they follow is purely for demonstration/testing purposes. 4 | 5 | ## Prerequisites 6 | * Three AWS Accounts with an IAM user with the appropriate permissions 7 | * The AWS Accounts should be part of the same AWS Organizations 8 | * Amazon VPC IPAM should be enabled in the AWS Organization 9 | * The *Networking Account* should be configured as the delegated Account to manage VPC IPAM. 10 | * Check the [VPC IPAM documentation](https://docs.aws.amazon.com/vpc/latest/ipam/enable-integ-ipam.html) for more information about the required configuration. 11 | * Terraform installed 12 | 13 | ## Code Principles: 14 | * Writing DRY (Do No Repeat Yourself) code using a modular design pattern 15 | 16 | ## Usage 17 | * Clone the repository 18 | * Edit the variables.tf file in each of the directories (*security-account*, *networking-account*, *spoke-account*). Each file contains the information used to configure the Terraform code in each AWS Account. 19 | 20 | **Note** EC2 instances, and AWS Network Firewall endpoints will be deployed in all the Availability Zones configured for each VPC. Keep this in mind when testing this environment from a cost perspective - for production environments, we recommend the use of at least 2 AZs for high-availability. 21 | 22 | ## Target Architecture 23 | 24 | ![Architecture diagram](../images/multi_account.png) 25 | 26 | ## Deployment 27 | 28 | * `terraform init` in each folder to download Terraform provider and modules. 29 | * Step 1: Deploy the resources of the *Security Account* 30 | * Move to the specific folder - `cd security-account` 31 | * Deploy the resources using `terraform apply` 32 | * Step 2: Deploy the resources of the *Networking Account* 33 | * Move to the specific folder - `cd networking-account` 34 | * This folder needs the Account ID of the *Security Account* (var.security_account) to obtain the Secrets Manager secret containing the Network Firewall Policy ID. We recommend the use of a [.tfvars file](https://developer.hashicorp.com/terraform/language/values/variables#variable-definitions-tfvars-files:~:text=several%20different%20variables.-,Variable%20Definitions%20(.tfvars)%20Files,-To%20set%20lots) to pass this value. 35 | * Deploy the resources using `terraform apply` 36 | * The Transit Gateway routing (Spoke VPCs) will be deployed after we deploy the resources from the *Spoke Account* 37 | * Step 3: Deploy the resources of the *Spoke Account* 38 | * Move to the specific folder - `cd spoke-account` 39 | * This folder needs the Account ID of the *Networking Account* (var.networking_account) to obtain the Secrets Manager secrets containing the Transit Gateway ID and VPC IPAM pool ID. We recommend the use of a [.tfvars file](https://developer.hashicorp.com/terraform/language/values/variables#variable-definitions-tfvars-files:~:text=several%20different%20variables.-,Variable%20Definitions%20(.tfvars)%20Files,-To%20set%20lots) to pass this value. 40 | * Deploy the resources using `terraform apply` 41 | * Step 4: Creating the Spoke VPC attachments routing in the *Networking Account* 42 | * Move to the specific folder - `cd networking-account` 43 | * Uncomment lines 48 - 54 & 95 - 99 in **main.tf** file. 44 | * These lines will get the information of the Spoke VPC attachments - shared via Secrets Manager by the *Spoke Account* - and create the corresponding Transit Gateway resources (route tables, associations, propagations, static routes) 45 | * `terraform apply` will create the resources. 46 | 47 | ## Clean-up 48 | 49 | * Step 1: Remove the Transit Gateway routing configuration 50 | * Move to the specific folder - `cd networking-account` 51 | * Comment lines 48 - 54 & 95 - 99 in **main.tf** file. 52 | * Removing these resources (and data sources) will remove the Transit Gateway routing, allowing later to destroy the VPC attachments in the *Spoke Account* 53 | * `terraform apply` will destroy the resources 54 | * Step 2: Clean-up *Spoke Account*. 55 | * Move to the specific folder - `cd spoke-account` 56 | * `terraform destroy` will clean-up all the resources in the Account. 57 | * Step 3: Clean-up *Networking Account* 58 | * Move to the specific folder - `cd networking-account` 59 | * `terraform destroy` will clean-up all the resources in the Account. 60 | * Step 4: Clean-up *Security Account* 61 | * Move to the specific folder - `cd security-account` 62 | * `terraform destroy` will clean-up all the resources in the Account. 63 | 64 | ## AWS Network Firewall Policy 65 | 66 | The AWS Network Firewall Policy is defined in the *policy.tf* file in the network_firewall module directory. By default: 67 | 68 | * All the SSH and RDP traffic is blocked by the Stateless engine. 69 | * The Stateful engine follows Strict Rule Ordering, blocking all the traffic by default. The only rule group allow HTTPS traffic to any **.amazon.com* domain. 70 | 71 | ## Security 72 | 73 | See [CONTRIBUTING](../CONTRIBUTING.md) for more information. 74 | 75 | ## License 76 | 77 | This library is licensed under the MIT-0 License. See the [LICENSE](../LICENSE) file. -------------------------------------------------------------------------------- /single-account/east_west/README.md: -------------------------------------------------------------------------------- 1 | 2 | # AWS Hub and Spoke Architecture with an Inspection VPC - Single AWS Account (East-West) 3 | 4 | ## Prerequisites 5 | * An AWS account with an IAM user with the appropriate permissions 6 | * Terraform installed 7 | 8 | ## Code Principles: 9 | * Writing DRY (Do No Repeat Yourself) code using a modular design pattern 10 | 11 | ## Usage 12 | * Clone the repository 13 | * Edit the variables.tf file in the project root directory. This file contains the information used to configure the Terraform code. 14 | 15 | **Note** EC2 instances will be deployed in all the Availability Zones configured for each VPC, and AWS Network Firewall will be deployed in all the AWS Region's Availability Zones. Keep this in mind when testing this environment from a cost perspective - for production environments, we recommend the use of at least 2 AZs for high-availability. 16 | 17 | ## Target Architecture 18 | 19 | ![Architecture diagram](../../images/single\_account\_eastwest.png) 20 | 21 | ## Deployment 22 | 23 | * `terraform init` to initialize the environment. 24 | * `terraform plan` to check the resources to create 25 | * `terraform apply` to build the architecture. 26 | 27 | ## Clean-up 28 | 29 | * `terraform destroy` will clean-up the resources created. 30 | 31 | ## Requirements 32 | 33 | | Name | Version | 34 | |------|---------| 35 | | [terraform](#requirement\_terraform) | >= 1.3.0 | 36 | | [aws](#requirement\_aws) | > 5.0.0 | 37 | 38 | ## Providers 39 | 40 | | Name | Version | 41 | |------|---------| 42 | | [aws](#provider\_aws) | > 5.0.0 | 43 | 44 | ## Modules 45 | 46 | | Name | Source | Version | 47 | |------|--------|---------| 48 | | [compute](#module\_compute) | ../modules/compute | n/a | 49 | | [iam\_kms](#module\_iam\_kms) | ../modules/iam_kms | n/a | 50 | | [spoke\_vpcs](#module\_spoke\_vpcs) | aws-ia/vpc/aws | = 4.5.0 | 51 | 52 | ## Resources 53 | 54 | | Name | Type | 55 | |------|------| 56 | | [aws_ec2_transit_gateway.tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway) | resource | 57 | | [aws_ec2_transit_gateway_route.tgw_route_default_to_inspection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route) | resource | 58 | | [aws_ec2_transit_gateway_route_table.tgw_route_table_inspection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table) | resource | 59 | | [aws_ec2_transit_gateway_route_table.tgw_route_table_spoke](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table) | resource | 60 | | [aws_ec2_transit_gateway_route_table_association.tgw_inspection_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_association) | resource | 61 | | [aws_ec2_transit_gateway_route_table_association.tgw_spoke_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_association) | resource | 62 | | [aws_ec2_transit_gateway_route_table_propagation.tgw_spoke_propagation_inspection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_propagation) | resource | 63 | | [aws_networkfirewall_firewall.anfw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall) | resource | 64 | | [aws_networkfirewall_firewall_policy.anfw_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall_policy) | resource | 65 | | [aws_networkfirewall_rule_group.allow_icmp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_rule_group) | resource | 66 | | [aws_networkfirewall_rule_group.drop_remote](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_rule_group) | resource | 67 | | [aws_availability_zones.azs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 68 | 69 | ## Inputs 70 | 71 | | Name | Description | Type | Default | Required | 72 | |------|-------------|------|---------|:--------:| 73 | | [aws\_region](#input\_aws\_region) | AWS Region. | `string` | `"eu-west-1"` | no | 74 | | [identifier](#input\_identifier) | Name of the project. | `string` | `"singleaccount-eastwest"` | no | 75 | | [inspection\_vpc](#input\_inspection\_vpc) | Inspection VPC definition. | `any` |
{
"cidr_block": "10.129.0.0/24",
"number_azs": 2,
"private_subnet_netmask": 28,
"public_subnet_netmask": 28,
"tgw_subnet_netmask": 28
}
| no | 76 | | [spoke\_vpcs](#input\_spoke\_vpcs) | Spoke VPCs definition. | `any` |
{
"spoke-vpc-1": {
"cidr_block": "10.0.0.0/24",
"endpoint_subnet_netmask": 28,
"flow_log_config": {
"log_destination_type": "cloud-watch-logs",
"retention_in_days": 7
},
"instance_type": "t2.micro",
"number_azs": 2,
"tgw_subnet_netmask": 28,
"workload_subnet_netmask": 28
},
"spoke-vpc-2": {
"cidr_block": "10.0.1.0/24",
"endpoint_subnet_netmask": 28,
"flow_log_config": {
"log_destination_type": "cloud-watch-logs",
"retention_in_days": 7
},
"instance_type": "t2.micro",
"number_azs": 2,
"tgw_subnet_netmask": 28,
"workload_subnet_netmask": 28
}
}
| no | 77 | 78 | ## Outputs 79 | 80 | No outputs. 81 | -------------------------------------------------------------------------------- /single-account/centralized_egress/README.md: -------------------------------------------------------------------------------- 1 | 2 | # AWS Hub and Spoke Architecture with an Inspection VPC - Single AWS Account (Centralized Egress) 3 | 4 | ## Prerequisites 5 | * An AWS account with an IAM user with the appropriate permissions 6 | * Terraform installed 7 | 8 | ## Code Principles: 9 | * Writing DRY (Do No Repeat Yourself) code using a modular design pattern 10 | 11 | ## Usage 12 | * Clone the repository 13 | * Edit the variables.tf file in the project root directory. This file contains the information used to configure the Terraform code. 14 | 15 | **Note** EC2 instances, and AWS Network Firewall endpoints will be deployed in all the Availability Zones configured for each VPC. Keep this in mind when testing this environment from a cost perspective - for production environments, we recommend the use of at least 2 AZs for high-availability. 16 | 17 | ## Target Architecture 18 | 19 | ![Architecture diagram](../../images/single\_account\_centralizedegress.png) 20 | 21 | ## Deployment 22 | 23 | * `terraform init` to initialize the environment. 24 | * `terraform plan` to check the resources to create 25 | * `terraform apply` to build the architecture. 26 | 27 | ## Clean-up 28 | 29 | * `terraform destroy` will clean-up the resources created. 30 | 31 | ## Requirements 32 | 33 | | Name | Version | 34 | |------|---------| 35 | | [terraform](#requirement\_terraform) | >= 1.3.0 | 36 | | [aws](#requirement\_aws) | > 5.0.0 | 37 | 38 | ## Providers 39 | 40 | | Name | Version | 41 | |------|---------| 42 | | [aws](#provider\_aws) | > 5.0.0 | 43 | 44 | ## Modules 45 | 46 | | Name | Source | Version | 47 | |------|--------|---------| 48 | | [compute](#module\_compute) | ../modules/compute | n/a | 49 | | [iam\_kms](#module\_iam\_kms) | ../modules/iam_kms | n/a | 50 | | [inspection\_vpc](#module\_inspection\_vpc) | aws-ia/vpc/aws | 4.5.0 | 51 | | [network\_firewall](#module\_network\_firewall) | aws-ia/networkfirewall/aws | 1.0.2 | 52 | | [spoke\_vpcs](#module\_spoke\_vpcs) | aws-ia/vpc/aws | = 4.5.0 | 53 | 54 | ## Resources 55 | 56 | | Name | Type | 57 | |------|------| 58 | | [aws_ec2_transit_gateway.tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway) | resource | 59 | | [aws_ec2_transit_gateway_route.tgw_route_default_to_inspection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route) | resource | 60 | | [aws_ec2_transit_gateway_route_table.tgw_route_table_inspection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table) | resource | 61 | | [aws_ec2_transit_gateway_route_table.tgw_route_table_spoke](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table) | resource | 62 | | [aws_ec2_transit_gateway_route_table_association.tgw_inspection_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_association) | resource | 63 | | [aws_ec2_transit_gateway_route_table_association.tgw_spoke_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_association) | resource | 64 | | [aws_ec2_transit_gateway_route_table_propagation.tgw_spoke_propagation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_propagation) | resource | 65 | | [aws_ec2_transit_gateway_route_table_propagation.tgw_spoke_propagation_inspection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_propagation) | resource | 66 | | [aws_networkfirewall_firewall_policy.anfw_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall_policy) | resource | 67 | | [aws_networkfirewall_rule_group.allow_domains](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_rule_group) | resource | 68 | | [aws_networkfirewall_rule_group.drop_remote](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_rule_group) | resource | 69 | 70 | ## Inputs 71 | 72 | | Name | Description | Type | Default | Required | 73 | |------|-------------|------|---------|:--------:| 74 | | [aws\_region](#input\_aws\_region) | AWS Region. | `string` | `"eu-west-1"` | no | 75 | | [identifier](#input\_identifier) | Name of the project. | `string` | `"singleaccount-centralizedegress"` | no | 76 | | [inspection\_vpc](#input\_inspection\_vpc) | Inspection VPC definition. | `any` |
{
"cidr_block": "10.129.0.0/24",
"number_azs": 2,
"private_subnet_netmask": 28,
"public_subnet_netmask": 28,
"tgw_subnet_netmask": 28
}
| no | 77 | | [spoke\_vpcs](#input\_spoke\_vpcs) | Spoke VPCs definition. | `any` |
{
"spoke-vpc-1": {
"cidr_block": "10.0.0.0/24",
"endpoint_subnet_netmask": 28,
"flow_log_config": {
"log_destination_type": "cloud-watch-logs",
"retention_in_days": 7
},
"instance_type": "t2.micro",
"number_azs": 2,
"tgw_subnet_netmask": 28,
"workload_subnet_netmask": 28
},
"spoke-vpc-2": {
"cidr_block": "10.0.1.0/24",
"endpoint_subnet_netmask": 28,
"flow_log_config": {
"log_destination_type": "cloud-watch-logs",
"retention_in_days": 7
},
"instance_type": "t2.micro",
"number_azs": 2,
"tgw_subnet_netmask": 28,
"workload_subnet_netmask": 28
}
}
| no | 78 | 79 | ## Outputs 80 | 81 | | Name | Description | 82 | |------|-------------| 83 | | [instances](#output\_instances) | EC2 instances created. | 84 | | [network\_firewall](#output\_network\_firewall) | AWS Network Firewall ID. | 85 | | [transit\_gateway\_id](#output\_transit\_gateway\_id) | AWS Transit Gateway ID. | 86 | | [transit\_gateway\_route\_tables](#output\_transit\_gateway\_route\_tables) | Transit Gateway Route Table. | 87 | | [vpcs](#output\_vpcs) | VPCs created. | 88 | -------------------------------------------------------------------------------- /single-account/centralized_egress/main.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- single-account/centralized_egress/main.tf --- 5 | 6 | # ---------- HUB AND SPOKE WITH CENTRAL INSPECTION (AWS NETWORK FIREWALL) ---------- 7 | # AWS Transit Gateway 8 | resource "aws_ec2_transit_gateway" "tgw" { 9 | description = "Transit Gateway - ${var.identifier}" 10 | default_route_table_association = "disable" 11 | default_route_table_propagation = "disable" 12 | 13 | tags = { Name = "tgw-${var.identifier}" } 14 | } 15 | 16 | 17 | # Inspection VPC - https://registry.terraform.io/modules/aws-ia/vpc/aws/latest 18 | module "inspection_vpc" { 19 | source = "aws-ia/vpc/aws" 20 | version = "4.5.0" 21 | 22 | name = "inspection-vpc-${var.identifier}" 23 | cidr_block = "100.64.0.0/24" 24 | az_count = 2 25 | 26 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 27 | transit_gateway_routes = { endpoints = "10.0.0.0/16" } 28 | 29 | subnets = { 30 | public = { 31 | netmask = var.inspection_vpc.public_subnet_netmask 32 | nat_gateway_configuration = "all_azs" 33 | } 34 | endpoints = { 35 | netmask = var.inspection_vpc.private_subnet_netmask 36 | connect_to_public_natgw = true 37 | } 38 | transit_gateway = { 39 | netmask = var.inspection_vpc.tgw_subnet_netmask 40 | transit_gateway_default_route_table_association = false 41 | transit_gateway_default_route_table_propagation = false 42 | transit_gateway_appliance_mode_support = "enable" 43 | } 44 | } 45 | } 46 | 47 | # AWS Network Firewall (and related routing) - https://registry.terraform.io/modules/aws-ia/networkfirewall/aws/latest 48 | module "network_firewall" { 49 | source = "aws-ia/networkfirewall/aws" 50 | version = "1.0.2" 51 | 52 | network_firewall_name = "anfw-${var.identifier}" 53 | network_firewall_description = "AWS Network Firewall - ${var.identifier}" 54 | network_firewall_policy = aws_networkfirewall_firewall_policy.anfw_policy.arn 55 | 56 | vpc_id = module.inspection_vpc.vpc_attributes.id 57 | number_azs = var.inspection_vpc.number_azs 58 | vpc_subnets = { for k, v in module.inspection_vpc.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "endpoints" } 59 | 60 | routing_configuration = { 61 | centralized_inspection_with_egress = { 62 | connectivity_subnet_route_tables = { for k, v in module.inspection_vpc.rt_attributes_by_type_by_az.transit_gateway : k => v.id } 63 | public_subnet_route_tables = { for k, v in module.inspection_vpc.rt_attributes_by_type_by_az.public : k => v.id } 64 | network_cidr_blocks = ["10.0.0.0/16"] 65 | } 66 | } 67 | } 68 | 69 | # ---------- SPOKE VPCs ---------- 70 | # Amazon VPC Module - https://registry.terraform.io/modules/aws-ia/vpc/aws/latest 71 | module "spoke_vpcs" { 72 | for_each = var.spoke_vpcs 73 | source = "aws-ia/vpc/aws" 74 | version = "= 4.5.0" 75 | 76 | name = each.key 77 | cidr_block = each.value.cidr_block 78 | az_count = each.value.number_azs 79 | 80 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 81 | transit_gateway_routes = { 82 | workload = "0.0.0.0/0" 83 | } 84 | 85 | subnets = { 86 | workload = { netmask = each.value.workload_subnet_netmask } 87 | endpoints = { netmask = each.value.endpoint_subnet_netmask } 88 | transit_gateway = { 89 | netmask = each.value.tgw_subnet_netmask 90 | transit_gateway_default_route_table_association = false 91 | transit_gateway_default_route_table_propagation = false 92 | } 93 | } 94 | 95 | vpc_flow_logs = { 96 | log_destination_type = each.value.flow_log_config.log_destination_type 97 | retention_in_days = each.value.flow_log_config.retention_in_days 98 | iam_role_arn = module.iam_kms.vpc_flowlogs_role 99 | kms_key_id = module.iam_kms.kms_key 100 | } 101 | } 102 | 103 | # ---------- TRANSIT GATEWAY ROUTING ---------- 104 | # Spoke Route Table 105 | # Spoke VPCs associated and propagating 106 | # Static route: 0.0.0.0/0 --> Inspection VPC attachment 107 | resource "aws_ec2_transit_gateway_route_table" "tgw_route_table_spoke" { 108 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 109 | 110 | tags = { Name = "spoke-rt-${var.identifier}" } 111 | } 112 | 113 | resource "aws_ec2_transit_gateway_route_table_association" "tgw_spoke_association" { 114 | for_each = var.spoke_vpcs 115 | 116 | transit_gateway_attachment_id = module.spoke_vpcs[each.key].transit_gateway_attachment_id 117 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_spoke.id 118 | } 119 | 120 | resource "aws_ec2_transit_gateway_route_table_propagation" "tgw_spoke_propagation" { 121 | for_each = var.spoke_vpcs 122 | 123 | transit_gateway_attachment_id = module.spoke_vpcs[each.key].transit_gateway_attachment_id 124 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_spoke.id 125 | } 126 | 127 | resource "aws_ec2_transit_gateway_route" "tgw_route_default_to_inspection" { 128 | destination_cidr_block = "0.0.0.0/0" 129 | transit_gateway_attachment_id = module.inspection_vpc.transit_gateway_attachment_id 130 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_spoke.id 131 | } 132 | 133 | # Inspection Route Table 134 | # Inspection VPC associated 135 | # Spoke VPCs propagating 136 | resource "aws_ec2_transit_gateway_route_table" "tgw_route_table_inspection" { 137 | transit_gateway_id = aws_ec2_transit_gateway.tgw.id 138 | 139 | tags = { Name = "inspection-rt-${var.identifier}" } 140 | } 141 | 142 | resource "aws_ec2_transit_gateway_route_table_association" "tgw_inspection_association" { 143 | transit_gateway_attachment_id = module.inspection_vpc.transit_gateway_attachment_id 144 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_inspection.id 145 | } 146 | 147 | resource "aws_ec2_transit_gateway_route_table_propagation" "tgw_spoke_propagation_inspection" { 148 | for_each = var.spoke_vpcs 149 | 150 | transit_gateway_attachment_id = module.spoke_vpcs[each.key].transit_gateway_attachment_id 151 | transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw_route_table_inspection.id 152 | } 153 | 154 | # ---------- EC2 INSTANCES & EC2 INSTANCE CONNECT ENDPOINT ---------- 155 | module "compute" { 156 | source = "../modules/compute" 157 | for_each = module.spoke_vpcs 158 | 159 | identifier = var.identifier 160 | vpc_name = each.key 161 | vpc = each.value 162 | vpc_information = var.spoke_vpcs[each.key] 163 | } 164 | 165 | # ---------- IAM ROLE (SSM ACCESS & VPC FLOW LOGS) AND KMS KEY (VPC FLOW LOGS) ---------- 166 | module "iam_kms" { 167 | source = "../modules/iam_kms" 168 | 169 | identifier = var.identifier 170 | aws_region = var.aws_region 171 | } -------------------------------------------------------------------------------- /multi-account/security-account/main.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- security-account/main.tf --- 5 | 6 | # ---------- AWS ORGANIZATIONS AND ACCOUNT INFORMATION ---------- 7 | data "aws_caller_identity" "aws_security_account" {} 8 | data "aws_organizations_organization" "org" {} 9 | 10 | # ---------- NETWORK FIREWALL POLICY ---------- 11 | # Firewall policy 12 | resource "aws_networkfirewall_firewall_policy" "central_inspection_policy" { 13 | name = "central-firewall-policy-${var.identifier}" 14 | 15 | firewall_policy { 16 | # Stateless configuration 17 | stateless_default_actions = ["aws:forward_to_sfe"] 18 | stateless_fragment_default_actions = ["aws:forward_to_sfe"] 19 | 20 | stateless_rule_group_reference { 21 | priority = 10 22 | resource_arn = aws_networkfirewall_rule_group.drop_remote.arn 23 | } 24 | 25 | # Stateful configuration 26 | stateful_engine_options { 27 | rule_order = "STRICT_ORDER" 28 | } 29 | stateful_default_actions = ["aws:drop_strict", "aws:alert_strict"] 30 | stateful_rule_group_reference { 31 | priority = 10 32 | resource_arn = aws_networkfirewall_rule_group.allow_tcp.arn 33 | } 34 | stateful_rule_group_reference { 35 | priority = 20 36 | resource_arn = aws_networkfirewall_rule_group.allow_domains.arn 37 | } 38 | } 39 | } 40 | 41 | # Stateless Rule Group - Dropping SSH traffic 42 | resource "aws_networkfirewall_rule_group" "drop_remote" { 43 | capacity = 2 44 | name = "drop-remote-${var.identifier}" 45 | type = "STATELESS" 46 | rule_group { 47 | rules_source { 48 | stateless_rules_and_custom_actions { 49 | 50 | stateless_rule { 51 | priority = 1 52 | rule_definition { 53 | actions = ["aws:drop"] 54 | match_attributes { 55 | protocols = [6] 56 | source { 57 | address_definition = "0.0.0.0/0" 58 | } 59 | source_port { 60 | from_port = 0 61 | to_port = 65535 62 | } 63 | destination { 64 | address_definition = "0.0.0.0/0" 65 | } 66 | destination_port { 67 | from_port = 22 68 | to_port = 22 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | # Stateful Rule Group - Allowing TCP traffic to port 443 (HTTPS) 79 | resource "aws_networkfirewall_rule_group" "allow_tcp" { 80 | capacity = 1 81 | name = "allow-tcp-${var.identifier}" 82 | type = "STATEFUL" 83 | rule_group { 84 | rule_variables { 85 | ip_sets { 86 | key = "NETWORK" 87 | ip_set { 88 | definition = [var.network_supernet] 89 | } 90 | } 91 | } 92 | rules_source { 93 | rules_string = < $EXTERNAL_NET 443 (msg:"Allowing TCP in port 443"; flow:not_established; sid:892123; rev:1;) 95 | EOF 96 | } 97 | stateful_rule_options { 98 | rule_order = "STRICT_ORDER" 99 | } 100 | } 101 | } 102 | 103 | 104 | # Stateful Rule Group - Allowing access to .amazon.com (HTTPS) 105 | resource "aws_networkfirewall_rule_group" "allow_domains" { 106 | capacity = 100 107 | name = "allow-domains-${var.identifier}" 108 | type = "STATEFUL" 109 | rule_group { 110 | rule_variables { 111 | ip_sets { 112 | key = "HOME_NET" 113 | ip_set { 114 | definition = [var.network_supernet] 115 | } 116 | } 117 | } 118 | rules_source { 119 | rules_source_list { 120 | generated_rules_type = "ALLOWLIST" 121 | target_types = ["TLS_SNI"] 122 | targets = [".amazon.com"] 123 | } 124 | } 125 | stateful_rule_options { 126 | rule_order = "STRICT_ORDER" 127 | } 128 | } 129 | } 130 | 131 | # ---------- AWS RAM SHARE ---------- 132 | # Resource Share 133 | resource "aws_ram_resource_share" "resource_share" { 134 | name = "Security Account Resource Share" 135 | allow_external_principals = false 136 | } 137 | 138 | # Principal Association 139 | resource "aws_ram_principal_association" "principal_association" { 140 | principal = data.aws_organizations_organization.org.arn 141 | resource_share_arn = aws_ram_resource_share.resource_share.arn 142 | } 143 | 144 | # Resource Association - AWS Transit Gateway 145 | resource "aws_ram_resource_association" "firewall_policy_share" { 146 | resource_arn = aws_networkfirewall_firewall_policy.central_inspection_policy.arn 147 | resource_share_arn = aws_ram_resource_share.resource_share.arn 148 | } 149 | 150 | # ---------- AWS SECRETS MANAGER ---------- 151 | # Firewall Policy ARN Secret 152 | resource "aws_secretsmanager_secret" "firewall_policy_arn" { 153 | name = var.secret_name 154 | description = "Firewall Policy ARN (Security Account)." 155 | kms_key_id = aws_kms_key.secrets_key.arn 156 | policy = data.aws_iam_policy_document.secrets_resource_policy.json 157 | recovery_window_in_days = 0 158 | } 159 | 160 | resource "aws_secretsmanager_secret_version" "firewall_policy_arn" { 161 | secret_id = aws_secretsmanager_secret.firewall_policy_arn.id 162 | secret_string = aws_networkfirewall_firewall_policy.central_inspection_policy.arn 163 | } 164 | 165 | # Secrets resource policy - Allowing the AWS Organization to read the secret 166 | data "aws_iam_policy_document" "secrets_resource_policy" { 167 | statement { 168 | actions = [ 169 | "secretsmanager:GetSecretValue", 170 | "secretsmanager:DescribeSecret", 171 | "secretsmanager:GetResourcePolicy" 172 | ] 173 | resources = ["*"] 174 | 175 | principals { 176 | type = "AWS" 177 | identifiers = ["*"] 178 | } 179 | 180 | condition { 181 | test = "StringEquals" 182 | variable = "aws:PrincipalOrgID" 183 | 184 | values = ["${data.aws_organizations_organization.org.id}"] 185 | } 186 | } 187 | } 188 | 189 | # KMS Key to encrypt the secrets 190 | resource "aws_kms_key" "secrets_key" { 191 | description = "KMS Secrets Key - Security Account." 192 | deletion_window_in_days = 7 193 | enable_key_rotation = true 194 | policy = data.aws_iam_policy_document.policy_kms_document.json 195 | 196 | tags = { 197 | Name = "kms-key-${var.identifier}" 198 | } 199 | } 200 | 201 | # KMS Policy 202 | data "aws_iam_policy_document" "policy_kms_document" { 203 | statement { 204 | sid = "Enable AWS Secrets Manager secrets decryption." 205 | effect = "Allow" 206 | actions = [ 207 | "kms:Decrypt", 208 | "kms:Encrypt", 209 | "kms:*" 210 | ] 211 | resources = ["*"] 212 | 213 | principals { 214 | type = "AWS" 215 | identifiers = ["*"] 216 | } 217 | 218 | condition { 219 | test = "StringEquals" 220 | variable = "kms:ViaService" 221 | 222 | values = ["secretsmanager.${var.aws_region}.amazonaws.com"] 223 | } 224 | condition { 225 | test = "StringLike" 226 | variable = "kms:EncryptionContext:SecretARN" 227 | 228 | values = ["arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.aws_security_account.id}:secret:*"] 229 | } 230 | condition { 231 | test = "StringEquals" 232 | variable = "aws:PrincipalOrgID" 233 | 234 | values = ["${data.aws_organizations_organization.org.id}"] 235 | } 236 | } 237 | 238 | statement { 239 | sid = "Enable IAM User Permissions" 240 | actions = ["kms:*"] 241 | resources = ["arn:aws:kms:${var.aws_region}:${data.aws_caller_identity.aws_security_account.id}:*"] 242 | 243 | principals { 244 | type = "AWS" 245 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.aws_security_account.id}:root"] 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /multi-account/networking-account/main.tf: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: MIT-0 */ 3 | 4 | # --- networking-account/main.tf --- 5 | 6 | # ---------- AWS ORGANIZATIONS AND ACCOUNT INFORMATION ---------- 7 | data "aws_caller_identity" "aws_networking_account" {} 8 | data "aws_organizations_organization" "org" {} 9 | 10 | # ---------- AMAZON VPC IPAM ---------- 11 | module "ipam" { 12 | source = "aws-ia/ipam/aws" 13 | version = "2.0.0" 14 | 15 | top_cidr = ["10.0.0.0/8"] 16 | address_family = "ipv4" 17 | create_ipam = true 18 | top_name = "Organization IPAM" 19 | 20 | pool_configurations = { 21 | ireland = { 22 | name = "ireland" 23 | description = "Ireland (eu-west-1) Region" 24 | netmask_length = 16 25 | locale = var.aws_region 26 | 27 | sub_pools = { 28 | spoke = { 29 | name = "spoke-accounts" 30 | netmask_length = 16 31 | ram_share_principals = [data.aws_organizations_organization.org.arn] 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | # ---------- HUB AND SPOKE ARCHITECTURE (WITH INSPECTION) -------- 39 | # Obtaining Firewall Policy ARN from Secrets Manager secret (shared by Security Account) 40 | data "aws_secretsmanager_secret" "firewall_policy_arn" { 41 | arn = "arn:aws:secretsmanager:${var.aws_region}:${var.security_account}:secret:${var.secrets_names.security_account}" 42 | } 43 | 44 | data "aws_secretsmanager_secret_version" "firewall_policy_arn" { 45 | secret_id = data.aws_secretsmanager_secret.firewall_policy_arn.id 46 | } 47 | 48 | # data "aws_secretsmanager_secret_version" "spoke_attachments" { 49 | # secret_id = aws_secretsmanager_secret.spoke_attachments.id 50 | # } 51 | 52 | # locals { 53 | # spoke_vpc_information = jsondecode(data.aws_secretsmanager_secret_version.spoke_attachments.secret_string)["spoke_account"] 54 | # } 55 | 56 | # Hub and Spoke architecture 57 | module "hubspoke" { 58 | source = "aws-ia/network-hubandspoke/aws" 59 | version = "3.2.0" 60 | 61 | identifier = "hubspoke-${var.identifier}" 62 | transit_gateway_attributes = { 63 | name = "tgw-${var.identifier}" 64 | description = "Transit Gateway - ${var.identifier}" 65 | amazon_side_asn = 65050 66 | auto_accept_shared_attachments = "enable" 67 | } 68 | 69 | network_definition = { 70 | type = "CIDR" 71 | value = "10.0.0.0/16" 72 | } 73 | 74 | central_vpcs = { 75 | inspection = { 76 | name = "inspection-vpc-${var.aws_region}" 77 | cidr_block = "100.64.0.0/24" 78 | az_count = 2 79 | inspection_flow = "north-south" 80 | 81 | aws_network_firewall = { 82 | name = "anfw-${var.identifier}" 83 | description = "AWS Network Firewall - ${var.identifier}" 84 | policy_arn = data.aws_secretsmanager_secret_version.firewall_policy_arn.secret_string 85 | } 86 | 87 | subnets = { 88 | public = { netmask = 28 } 89 | endpoints = { netmask = 28 } 90 | transit_gateway = { netmask = 28 } 91 | } 92 | } 93 | } 94 | 95 | # spoke_vpcs = { 96 | # routing_domains = ["prod", "nonprod"] 97 | # number_vpcs = local.spoke_vpc_information.number_spoke_vpcs 98 | # vpc_information = local.spoke_vpc_information.vpc_information 99 | # } 100 | 101 | tags = { 102 | team = "networking" 103 | } 104 | } 105 | 106 | # ---------- AWS RAM (TRANSIT GATEWAY) ---------- 107 | # Resource Share 108 | resource "aws_ram_resource_share" "resource_share" { 109 | name = "Transit Gateway Resource Share" 110 | allow_external_principals = false 111 | } 112 | 113 | # Principal Association 114 | resource "aws_ram_principal_association" "principal_association" { 115 | principal = data.aws_organizations_organization.org.arn 116 | resource_share_arn = aws_ram_resource_share.resource_share.arn 117 | } 118 | 119 | # Resource Association - AWS Transit Gateway 120 | resource "aws_ram_resource_association" "transit_gateway_share" { 121 | resource_arn = module.hubspoke.transit_gateway.arn 122 | resource_share_arn = aws_ram_resource_share.resource_share.arn 123 | } 124 | 125 | # ---------- AWS SECRETS MANAGER ---------- 126 | # Transit Gateway Secret 127 | resource "aws_secretsmanager_secret" "transit_gateway" { 128 | name = var.secrets_names.networking_account_tgw 129 | description = "Transit Gateway (Networking Account)." 130 | kms_key_id = aws_kms_key.secrets_key.arn 131 | policy = data.aws_iam_policy_document.secrets_resource_policy_read.json 132 | recovery_window_in_days = 0 133 | } 134 | 135 | resource "aws_secretsmanager_secret_version" "transit_gateway" { 136 | secret_id = aws_secretsmanager_secret.transit_gateway.id 137 | secret_string = module.hubspoke.transit_gateway.id 138 | } 139 | 140 | # Spoke VPC Attachment information 141 | resource "aws_secretsmanager_secret" "spoke_attachments" { 142 | name = var.secrets_names.networking_account_attachments 143 | description = "VPC Attachments - Spoke Accounts." 144 | kms_key_id = aws_kms_key.secrets_key.arn 145 | policy = data.aws_iam_policy_document.secrets_resource_policy_write.json 146 | recovery_window_in_days = 0 147 | } 148 | 149 | # IPAM Pool Secret 150 | resource "aws_secretsmanager_secret" "ipam_pool" { 151 | name = var.secrets_names.networking_account_ipam 152 | description = "IPAM Pool (Networking Account)." 153 | kms_key_id = aws_kms_key.secrets_key.arn 154 | policy = data.aws_iam_policy_document.secrets_resource_policy_read.json 155 | recovery_window_in_days = 0 156 | } 157 | 158 | resource "aws_secretsmanager_secret_version" "ipam_pool" { 159 | secret_id = aws_secretsmanager_secret.ipam_pool.id 160 | secret_string = module.ipam.pools_level_2["ireland/spoke"].id 161 | } 162 | 163 | # Secrets resource policy - Allowing the AWS Organization to read the secret 164 | data "aws_iam_policy_document" "secrets_resource_policy_read" { 165 | statement { 166 | actions = [ 167 | "secretsmanager:GetSecretValue", 168 | "secretsmanager:DescribeSecret", 169 | "secretsmanager:GetResourcePolicy" 170 | ] 171 | resources = ["*"] 172 | 173 | principals { 174 | type = "AWS" 175 | identifiers = ["*"] 176 | } 177 | 178 | condition { 179 | test = "StringEquals" 180 | variable = "aws:PrincipalOrgID" 181 | 182 | values = ["${data.aws_organizations_organization.org.id}"] 183 | } 184 | } 185 | } 186 | 187 | # Secrets resource policy - Allowing the Spoke AWS Account to write the secret 188 | data "aws_iam_policy_document" "secrets_resource_policy_write" { 189 | statement { 190 | actions = [ 191 | "secretsmanager:GetSecretValue", 192 | "secretsmanager:PutSecretValue", 193 | "secretsmanager:DescribeSecret", 194 | "secretsmanager:GetResourcePolicy" 195 | ] 196 | resources = ["*"] 197 | 198 | principals { 199 | type = "AWS" 200 | identifiers = ["*"] 201 | } 202 | 203 | condition { 204 | test = "StringEquals" 205 | variable = "aws:PrincipalOrgID" 206 | 207 | values = ["${data.aws_organizations_organization.org.id}"] 208 | } 209 | } 210 | } 211 | 212 | # KMS Key to encrypt the secrets 213 | resource "aws_kms_key" "secrets_key" { 214 | description = "KMS Secrets Key - Security Account." 215 | deletion_window_in_days = 7 216 | enable_key_rotation = true 217 | policy = data.aws_iam_policy_document.policy_kms_document.json 218 | 219 | tags = { 220 | Name = "kms-key-${var.identifier}" 221 | } 222 | } 223 | 224 | # KMS Policy 225 | data "aws_iam_policy_document" "policy_kms_document" { 226 | statement { 227 | sid = "Enable AWS Secrets Manager secrets decryption." 228 | effect = "Allow" 229 | actions = [ 230 | "kms:Decrypt", 231 | "kms:Encrypt", 232 | "kms:*" 233 | ] 234 | resources = ["*"] 235 | 236 | principals { 237 | type = "AWS" 238 | identifiers = ["*"] 239 | } 240 | 241 | condition { 242 | test = "StringEquals" 243 | variable = "kms:ViaService" 244 | 245 | values = ["secretsmanager.${var.aws_region}.amazonaws.com"] 246 | } 247 | condition { 248 | test = "StringLike" 249 | variable = "kms:EncryptionContext:SecretARN" 250 | 251 | values = ["arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.aws_networking_account.id}:secret:*"] 252 | } 253 | condition { 254 | test = "StringEquals" 255 | variable = "aws:PrincipalOrgID" 256 | 257 | values = ["${data.aws_organizations_organization.org.id}"] 258 | } 259 | } 260 | 261 | statement { 262 | sid = "Enable IAM User Permissions" 263 | actions = ["kms:*"] 264 | resources = ["arn:aws:kms:${var.aws_region}:${data.aws_caller_identity.aws_networking_account.id}:*"] 265 | 266 | principals { 267 | type = "AWS" 268 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.aws_networking_account.id}:root"] 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /images/diagrams.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | --------------------------------------------------------------------------------