From 1fe30a1e96987c77f6142fab5ead065668c4ae86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20S=C3=A1nchez=20Carmona?= Date: Thu, 27 Oct 2022 20:39:53 +0200 Subject: [PATCH] adding support for VPC module v3.x and ANFW module --- .header.md | 5 +- README.md | 23 +++--- main.tf | 111 +++++++++++++++----------- modules/network_firewall/main.tf | 42 ---------- modules/network_firewall/outputs.tf | 7 -- modules/network_firewall/variables.tf | 32 -------- outputs.tf | 2 +- policy.tf | 2 +- providers.tf | 3 +- variables.tf | 36 +++------ 10 files changed, 95 insertions(+), 168 deletions(-) delete mode 100644 modules/network_firewall/main.tf delete mode 100644 modules/network_firewall/outputs.tf delete mode 100644 modules/network_firewall/variables.tf diff --git a/.header.md b/.header.md index cc9da55..070826e 100644 --- a/.header.md +++ b/.header.md @@ -1,6 +1,6 @@ # AWS Hub and Spoke Architecture with an Inspection VPC - Terraform Sample -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. +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. ## Prerequisites - An AWS account with an IAM user with the appropriate permissions @@ -15,8 +15,7 @@ This repository contains terraform code to deploy a sample AWS Hub and Spoke arc The variables.tf file contains the variables that are used to configure the Terraform code. -**** Note **** If the ec2_multi_subnet variable is set to true, an EC2 compute instance will be deployed into every Spoke Private subnet, with 2 x Spokes and 3 x Availablity Zones this means 6 EC2 instances. **Please** leave as false to avoid costs unless you are happy to deploy more instances and accept additional costs. -VPC endpoints (for SSM connection) and AWS Network Firewall endpoints will be deployed in all the Availability Zones you indicate in the *vpcs* variable. +**Note** EC2 instances, VPC endpoints, 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. ## Deployment diff --git a/README.md b/README.md index b61cb46..31da927 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # AWS Hub and Spoke Architecture with an Inspection VPC - Terraform Sample -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. +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. ## Prerequisites - An AWS account with an IAM user with the appropriate permissions @@ -16,8 +16,7 @@ This repository contains terraform code to deploy a sample AWS Hub and Spoke arc The variables.tf file contains the variables that are used to configure the Terraform code. -**** Note **** If the ec2\_multi\_subnet variable is set to true, an EC2 compute instance will be deployed into every Spoke Private subnet, with 2 x Spokes and 3 x Availablity Zones this means 6 EC2 instances. **Please** leave as false to avoid costs unless you are happy to deploy more instances and accept additional costs. -VPC endpoints (for SSM connection) and AWS Network Firewall endpoints will be deployed in all the Availability Zones you indicate in the *vpcs* variable. +**Note** EC2 instances, VPC endpoints, 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. ## Deployment @@ -62,7 +61,7 @@ This library is licensed under the MIT-0 License. See the [LICENSE](https://gith | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | ~> 1.1.2 | +| [terraform](#requirement\_terraform) | >= 1.3.0 | | [aws](#requirement\_aws) | >= 3.73.0 | | [awscc](#requirement\_awscc) | >= 0.15.0 | @@ -70,23 +69,25 @@ This library is licensed under the MIT-0 License. See the [LICENSE](https://gith | Name | Version | |------|---------| -| [aws](#provider\_aws) | 4.19.0 | +| [aws](#provider\_aws) | 4.36.1 | ## Modules | Name | Source | Version | |------|--------|---------| -| [aws\_network\_firewall](#module\_aws\_network\_firewall) | ./modules/network_firewall | n/a | +| [aws\_network\_firewall](#module\_aws\_network\_firewall) | aws-ia/networkfirewall/aws | 0.0.2 | | [compute](#module\_compute) | ./modules/compute | n/a | | [iam\_kms](#module\_iam\_kms) | ./modules/iam_kms | n/a | -| [inspection\_vpc](#module\_inspection\_vpc) | aws-ia/vpc/aws | = 1.4.1 | -| [spoke\_vpcs](#module\_spoke\_vpcs) | aws-ia/vpc/aws | = 1.4.1 | +| [inspection\_vpc](#module\_inspection\_vpc) | aws-ia/vpc/aws | = 3.0.1 | +| [spoke\_vpcs](#module\_spoke\_vpcs) | aws-ia/vpc/aws | = 3.0.1 | | [vpc\_endpoints](#module\_vpc\_endpoints) | ./modules/endpoints | n/a | ## Resources | Name | Type | |------|------| +| [aws_ec2_managed_prefix_list.prefix_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_managed_prefix_list) | resource | +| [aws_ec2_managed_prefix_list_entry.pl_entry](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_managed_prefix_list_entry) | resource | | [aws_ec2_transit_gateway.tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway) | resource | | [aws_ec2_transit_gateway_route.default_route_spoke_to_inspection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route) | resource | | [aws_ec2_transit_gateway_route_table.post_inspection_vpc_route_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table) | resource | @@ -103,12 +104,10 @@ This library is licensed under the MIT-0 License. See the [LICENSE](https://gith | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [ec2\_multi\_subnet](#input\_ec2\_multi\_subnet) | Multi subnet Instance Deployment. | `bool` | `true` | no | | [inspection\_vpc](#input\_inspection\_vpc) | Inspection VPC definition. | `any` |
{
"cidr_block": "10.129.0.0/24",
"flow_log_config": {
"log_destination_type": "cloud-watch-logs",
"retention_in_days": 7
},
"number_azs": 2,
"private_subnet_netmask": 28,
"public_subnet_netmask": 28,
"tgw_subnet_netmask": 28
}
| no | | [project\_name](#input\_project\_name) | Name of the project. | `string` | `"aws-hub-and-spoke-demo"` | no | -| [region](#input\_region) | AWS Region. | `string` | `"us-east-1"` | no | -| [spoke\_vpcs](#input\_spoke\_vpcs) | Spoke VPCs definition. | `any` |
{
"spoke-vpc-1": {
"cidr_block": "10.0.0.0/16",
"flow_log_config": {
"log_destination_type": "cloud-watch-logs",
"retention_in_days": 7
},
"instance_type": "t2.micro",
"number_azs": 2,
"private_subnet_netmask": 28,
"tgw_subnet_netmask": 28
},
"spoke-vpc-2": {
"cidr_block": "10.1.0.0/16",
"flow_log_config": {
"log_destination_type": "cloud-watch-logs",
"retention_in_days": 7
},
"instance_type": "t2.micro",
"number_azs": 2,
"private_subnet_netmask": 24,
"tgw_subnet_netmask": 28
}
}
| no | -| [supernet](#input\_supernet) | Hub and Spoke Supernet. | `string` | `"10.0.0.0/8"` | no | +| [region](#input\_region) | AWS Region. | `string` | `"eu-west-1"` | no | +| [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 | ## Outputs diff --git a/main.tf b/main.tf index 027e566..a0952ed 100644 --- a/main.tf +++ b/main.tf @@ -2,34 +2,33 @@ SPDX-License-Identifier: MIT-0 */ /* The VPC module will deploy a VPC for each resoruce defined in the variables.tf file defined as a spoke - Additional resources such as NAT Gateways will be deploeyed according to the value set in the variables file */ + Additional resources such as NAT Gateways will be deployed according to the value set in the variables file */ -# Inspection VPC. Module - https://github.com/aws-ia/terraform-aws-vpc +# Inspection VPC. Module - https://registry.terraform.io/modules/aws-ia/vpc/aws/latest module "inspection_vpc" { source = "aws-ia/vpc/aws" - version = "= 1.4.1" + version = "= 3.0.1" name = "inspection-vpc" cidr_block = var.inspection_vpc.cidr_block az_count = var.inspection_vpc.number_azs + transit_gateway_id = aws_ec2_transit_gateway.tgw.id + transit_gateway_routes = { + inspection = aws_ec2_managed_prefix_list.prefix_list.id + } + subnets = { public = { - name_prefix = "public" netmask = var.inspection_vpc.public_subnet_netmask nat_gateway_configuration = "all_azs" } - - private = { - name_prefix = "inspection" - netmask = var.inspection_vpc.private_subnet_netmask - route_to_nat = true - route_to_transit_gateway = [var.supernet] + inspection = { + netmask = var.inspection_vpc.private_subnet_netmask + connect_to_public_natgw = true } transit_gateway = { - name_prefix = "tgw" netmask = var.inspection_vpc.tgw_subnet_netmask - transit_gateway_id = aws_ec2_transit_gateway.tgw.id transit_gateway_default_route_table_association = false transit_gateway_default_route_table_propagation = false transit_gateway_appliance_mode_support = "enable" @@ -44,27 +43,26 @@ module "inspection_vpc" { } } -# Spoke VPCs. Module - https://github.com/aws-ia/terraform-aws-vpc +# Spoke VPCs. Module - https://registry.terraform.io/modules/aws-ia/vpc/aws/latest module "spoke_vpcs" { for_each = var.spoke_vpcs source = "aws-ia/vpc/aws" - version = "= 1.4.1" + version = "= 3.0.1" name = each.key cidr_block = each.value.cidr_block az_count = each.value.number_azs + transit_gateway_id = aws_ec2_transit_gateway.tgw.id + transit_gateway_routes = { + workload = "0.0.0.0/0" + } + subnets = { - private = { - name_prefix = "private" - netmask = each.value.private_subnet_netmask - route_to_nat = false - route_to_transit_gateway = ["0.0.0.0/0"] - } + workload = { netmask = each.value.workload_subnet_netmask } + endpoints = { netmask = each.value.endpoint_subnet_netmask } transit_gateway = { - name_prefix = "tgw" netmask = each.value.tgw_subnet_netmask - transit_gateway_id = aws_ec2_transit_gateway.tgw.id transit_gateway_default_route_table_association = false transit_gateway_default_route_table_propagation = false } @@ -89,6 +87,22 @@ resource "aws_ec2_transit_gateway" "tgw" { } } +# MANAGED PREFIX LIST (with the Spoke VPCs CIDRs) +# Managed Prefix List resource +resource "aws_ec2_managed_prefix_list" "prefix_list" { + name = "Spoke VPCs" + address_family = "IPv4" + max_entries = length(var.spoke_vpcs) +} + +resource "aws_ec2_managed_prefix_list_entry" "pl_entry" { + for_each = var.spoke_vpcs + + cidr = each.value.cidr_block + description = each.key + prefix_list_id = aws_ec2_managed_prefix_list.prefix_list.id +} + # TRANSIT GATEWAY ROUTING # Spoke Transit Gateway Route Table resource "aws_ec2_transit_gateway_route_table" "spoke_vpc_route_table" { @@ -137,6 +151,27 @@ resource "aws_ec2_transit_gateway_route" "default_route_spoke_to_inspection" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.spoke_vpc_route_table.id } +# AWS Network Firewall module - https://registry.terraform.io/modules/aws-ia/networkfirewall/aws/latest +module "aws_network_firewall" { + source = "aws-ia/networkfirewall/aws" + version = "0.0.2" + + network_firewall_name = "anfw-${var.project_name}" + network_firewall_policy = aws_networkfirewall_firewall_policy.anfw_policy.arn + + number_azs = var.inspection_vpc.number_azs + vpc_id = module.inspection_vpc.vpc_attributes.id + vpc_subnets = { for k, v in module.inspection_vpc.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "inspection" } + + routing_configuration = { + centralized_inspection_with_egress = { + tgw_subnet_route_tables = { for k, v in module.inspection_vpc.rt_attributes_by_type_by_az.transit_gateway : k => v.id } + public_subnet_route_tables = { for k, v in module.inspection_vpc.rt_attributes_by_type_by_az.public : k => v.id } + network_cidr_blocks = values({ for k, v in var.spoke_vpcs : k => v.cidr_block }) + } + } +} + # The VPC Endpoint module deploys the necessary AWS VPC Endpoints to allow SSM (information of the endpoints to create in locals.tf) # VPC Endpoints are only deployed into Spoke VPCs module "vpc_endpoints" { @@ -146,20 +181,11 @@ module "vpc_endpoints" { project_name = var.project_name vpc_name = each.key vpc_id = each.value.vpc_attributes.id - vpc_subnets = values({ for k, v in each.value.private_subnet_attributes_by_az : k => v.id }) + vpc_subnets = values({ for k, v in each.value.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "endpoints" }) endpoints_security_group = local.security_groups.endpoints endpoints_service_names = local.endpoint_service_names } - -# The IAM role creates the nessesary policies for the VPC Flow logs and the EC2 instance -module "iam_kms" { - source = "./modules/iam_kms" - - project_name = var.project_name - aws_region = var.region -} - # The Compute module deployes EC2 instances into Spoke VPCs only, the number of instances are defined in variables.tf module "compute" { for_each = module.spoke_vpcs @@ -168,22 +194,17 @@ module "compute" { project_name = var.project_name vpc_name = each.key vpc_id = each.value.vpc_attributes.id - vpc_subnets = var.ec2_multi_subnet ? values({ for k, v in each.value.private_subnet_attributes_by_az : k => v.id }) : slice(values({ for k, v in each.value.private_subnet_attributes_by_az : k => v.id }), 0, 1) - number_azs = var.ec2_multi_subnet ? var.spoke_vpcs[each.key].number_azs : 1 + vpc_subnets = values({ for k, v in each.value.private_subnet_attributes_by_az : split("/", k)[1] => v.id if split("/", k)[0] == "workload" }) + number_azs = var.spoke_vpcs[each.key].number_azs instance_type = var.spoke_vpcs[each.key].instance_type ec2_iam_instance_profile = module.iam_kms.ec2_iam_instance_profile ec2_security_group = local.security_groups.instance } -/* Module to the AWS Network Firewall - The ANFW policy is defined in the policy.tf file in the aws_network_firewall module directory */ -module "aws_network_firewall" { - source = "./modules/network_firewall" - - project_name = var.project_name - vpc_name = "inspection-vpc" - vpc_info = module.inspection_vpc - policy_document = aws_networkfirewall_firewall_policy.anfw_policy.arn - supernet = var.supernet - number_azs = var.inspection_vpc.number_azs +# The IAM role creates the nessesary policies for the VPC Flow logs and the EC2 instance +module "iam_kms" { + source = "./modules/iam_kms" + + project_name = var.project_name + aws_region = var.region } diff --git a/modules/network_firewall/main.tf b/modules/network_firewall/main.tf deleted file mode 100644 index acd8b4e..0000000 --- a/modules/network_firewall/main.tf +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - SPDX-License-Identifier: MIT-0 */ - -# AWS Network Firewall Resource -resource "aws_networkfirewall_firewall" "anfw" { - name = "ANFW-${var.vpc_name}-${var.project_name}" - firewall_policy_arn = var.policy_document - vpc_id = var.vpc_info.vpc_attributes.id - - dynamic "subnet_mapping" { - for_each = values({ for k, v in var.vpc_info.private_subnet_attributes_by_az : k => v.id }) - - content { - subnet_id = subnet_mapping.value - } - } -} - -# Local variable -locals { - availability_zones = keys({ for k, v in var.vpc_info.private_subnet_attributes_by_az : k => v }) - inspection_endpoints = { for i in aws_networkfirewall_firewall.anfw.firewall_status[0].sync_states : i.availability_zone => i.attachment[0].endpoint_id } -} - -# Route from the TGW Subnet to 0.0.0.0/0 via the firewall endpoint -resource "aws_route" "tgw_to_firewall_endpoint" { - count = var.number_azs - - route_table_id = var.vpc_info.route_table_by_subnet_type.transit_gateway[local.availability_zones[count.index]].id - destination_cidr_block = "0.0.0.0/0" - vpc_endpoint_id = local.inspection_endpoints[local.availability_zones[count.index]] -} - -# Route from the Public Subnet to the Segment CIDR block via the firewall endpoint -resource "aws_route" "public_to_firewall_endpoint" { - count = var.number_azs - - route_table_id = var.vpc_info.route_table_by_subnet_type.public[local.availability_zones[count.index]].id - destination_cidr_block = var.supernet - vpc_endpoint_id = local.inspection_endpoints[local.availability_zones[count.index]] -} - diff --git a/modules/network_firewall/outputs.tf b/modules/network_firewall/outputs.tf deleted file mode 100644 index 7219bfb..0000000 --- a/modules/network_firewall/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - SPDX-License-Identifier: MIT-0 */ - -output "anfw" { - description = "AWS Network Firewall" - value = aws_networkfirewall_firewall.anfw -} diff --git a/modules/network_firewall/variables.tf b/modules/network_firewall/variables.tf deleted file mode 100644 index 36eea5c..0000000 --- a/modules/network_firewall/variables.tf +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - SPDX-License-Identifier: MIT-0 */ - -variable "project_name" { - type = string - description = "Project identifier." -} - -variable "vpc_name" { - type = string - description = "VPC name." -} - -variable "vpc_info" { - type = any - description = "VPC Information." -} - -variable "policy_document" { - type = string - description = "Policy document." -} - -variable "supernet" { - type = string - description = "Network's supernet - used for the routes to the Firewall Endpoint from the public subnet." -} - -variable "number_azs" { - type = number - description = "Number of Availability Zones, indicated in the root variables." -} diff --git a/outputs.tf b/outputs.tf index 37c93ef..c950fd4 100644 --- a/outputs.tf +++ b/outputs.tf @@ -34,5 +34,5 @@ output "instances" { output "network_firewall" { description = "AWS Network Firewall ID." - value = module.aws_network_firewall.anfw.id + value = module.aws_network_firewall.aws_network_firewall.id } diff --git a/policy.tf b/policy.tf index ea63783..c90a75b 100644 --- a/policy.tf +++ b/policy.tf @@ -94,7 +94,7 @@ resource "aws_networkfirewall_rule_group" "allow_icmp" { ip_sets { key = "SUPERNET" ip_set { - definition = [var.supernet] + definition = ["10.0.0.0/8"] } } } diff --git a/providers.tf b/providers.tf index 23be82a..a9355d2 100644 --- a/providers.tf +++ b/providers.tf @@ -2,8 +2,7 @@ SPDX-License-Identifier: MIT-0 */ terraform { - required_version = ">= 0.15.3" - experiments = [module_variable_optional_attrs] + required_version = ">= 1.3.0" required_providers { aws = { source = "hashicorp/aws" diff --git a/variables.tf b/variables.tf index cfe5378..9a0e2bf 100644 --- a/variables.tf +++ b/variables.tf @@ -5,7 +5,7 @@ variable "region" { description = "AWS Region." type = string - default = "us-east-1" + default = "eu-west-1" } variable "project_name" { @@ -14,18 +14,6 @@ variable "project_name" { default = "aws-hub-and-spoke-demo" } -variable "ec2_multi_subnet" { - description = "Multi subnet Instance Deployment." - type = bool - default = true -} - -variable "supernet" { - description = "Hub and Spoke Supernet." - type = string - default = "10.0.0.0/8" -} - # Spoke VPCs variable "spoke_vpcs" { description = "Spoke VPCs definition." @@ -33,11 +21,12 @@ variable "spoke_vpcs" { default = { "spoke-vpc-1" = { - cidr_block = "10.0.0.0/16" - private_subnet_netmask = 28 - tgw_subnet_netmask = 28 - number_azs = 2 - instance_type = "t2.micro" + cidr_block = "10.0.0.0/24" + workload_subnet_netmask = 28 + endpoint_subnet_netmask = 28 + tgw_subnet_netmask = 28 + number_azs = 2 + instance_type = "t2.micro" flow_log_config = { log_destination_type = "cloud-watch-logs" @@ -46,11 +35,12 @@ variable "spoke_vpcs" { } "spoke-vpc-2" = { - cidr_block = "10.1.0.0/16" - private_subnet_netmask = 24 - tgw_subnet_netmask = 28 - number_azs = 2 - instance_type = "t2.micro" + cidr_block = "10.0.1.0/24" + workload_subnet_netmask = 28 + endpoint_subnet_netmask = 28 + tgw_subnet_netmask = 28 + number_azs = 2 + instance_type = "t2.micro" flow_log_config = { log_destination_type = "cloud-watch-logs"