Skip to main content

NTC VPC

Release Notes Source Code Implementation Blueprint

Description

NTC VPC simplifies the deployment and management of Virtual Private Clouds (VPCs) in AWS. This building block automates the creation of VPCs, subnets, route tables, and network configurations, ensuring a secure, scalable, and well-architected network foundation. With support for both single and multi-account environments, NTC VPC integrates seamlessly with other NTC building blocks, providing a modular approach to network design.

Whether you are setting up isolated environments, connecting workloads, or implementing hybrid architectures, NTC VPC ensures consistent and reliable networking aligned with AWS best practices.

Usage

Latest Release1.6.0
# --------------------------------------------------------------------------------------------------
# ¦ NTC VPC
# --------------------------------------------------------------------------------------------------
module "ntc_vpc" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-vpc?ref=X.X.X"

# a prefix which will be added to all vpc resources
prefix_name = "prod-stage"

# subnets will be generated for each reserved AZ. Resources like NAT Gateway, VPC Endpoints and RAM sharing only for active AZs
# WARNING: changing the reserved count can lead to subnets beeing redeployed
availability_zones = {
reserved = 3
active = 1
# filter_zone_names = []
}

# define customer managed prefix lists e.g. for all on-premises ip ranges
customer_managed_prefix_lists = [
{
name = "onprem-servers-ipv4-ranges"
entries = [
{
cidr = "192.168.10.0/24"
description = "Server Zone A"
},
{
cidr = "192.168.20.0/24"
description = "Server Zone B"
},
{
cidr = "192.168.30.0/24"
description = "Server Zone C"
}
]
# (optional) share subnet with Organizations, OUs or Accounts - requires RAM to be enabled for Organizations
# ram_share_principals = ["o-m29e8d9awz", "ou-6gf5-6ltp3mjf", "945766593056"]
# ram_share_allow_external_principals = false
},
{
name = "cloud-ipv4-ranges"
entries = [
{
cidr = "100.64.0.0/10"
description = "cloudonly"
},
{
cidr = "172.16.0.0/12"
description = "hybrid"
},
{
cidr = "192.168.0.0/16"
description = "onprem"
}
]
# (optional) share subnet with Organizations, OUs or Accounts - requires RAM to be enabled for Organizations
# ram_share_principals = ["o-m29e8d9awz", "ou-6gf5-6ltp3mjf", "945766593056"]
# ram_share_allow_external_principals = false
}
]

# define primary cidr block for the VPC
vpc_ipv4_primary_cidr = "172.16.50.0/24"

# define additional cidr blocks for the VPC
vpc_ipv4_secondary_cidr_blocks = [
{
cidr_identifier = "cloudonly"
cidr_block = "100.64.108.0/22"
}
]

# (optional) use IPAM to get cidr blocks dynamically
vpc_ipam_settings = {
# cidrs will be dynamically requested from IPAM - this overwrites 'vpc_ipv4_primary_cidr'
cidrs_requested_from_ipam = false
# Terraform can allocate (reserve) cidrs (static or dynamic) in IPAM and assign to VPC
cidrs_allocated_by_terraform = false
reservation_description = "this cidr was allocated by terraform"
ipv4_primary_pool_id = module.ntc_ipam.nested_pools_ids["/toplevel/frankfurt"]
ipv4_primary_pool_netmask_length = module.ntc_ipam.nested_pools_allocation_configs["/toplevel/frankfurt"].allocation_default_netmask_length
ipv4_secondary_pools = []
}

vpc_subnets = [
{
# (optional) for VPCs with secondary cidr blocks the 'vpc_cidr_identifier' is required. Primary cidr block is always 'primary'
vpc_cidr_identifier = "cloudonly"
# unique identifier for subnet - renaming will cause subnet to be recreated
subnet_identifier = "cloudonly-public"
# subnets can be of type 'private', 'public', 'firewall' or 'transit'
subnet_type = "public"
# setting netmask_length will dynamically calculate or allocate (with ipam) corresponding cidr ranges
# when calculating cidrs the subnets will be sorted from largest to smallest to optimize cidr space
# WARNING: changing the netmask_length can lead to subnets beeing redeployed
netmask_length = 27
# instead of dynamically calculating subnet cidrs based on netmask length a list of static cidrs can be provided
static_cidrs = []
# specific configuration for subnet type
public_subnet_config = {
default_route_to_internet_gateway = true
map_public_ip_on_launch = true
create_public_nat_gateway = true
}
# network access control list (ACL) allows or denies specific inbound or outbound traffic at the subnet level
# additional layer of security but can lead to unexpected traffic patterns if configured wrong (stateful security group vs. stateless NACL rules)
# https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html#custom-network-acl
network_acl_inbound = [
{
# allow inbound HTTPS traffic from any IPv4 address
rule_number = 100
rule_action = "allow"
protocol = "tcp"
from_port = 443
to_port = 443
ipv4_cidr_block = "0.0.0.0/0"
},
{
# allow inbound return traffic from the internet (that is, for requests that originate in the subnet)
rule_number = 105
rule_action = "allow"
protocol = "tcp"
from_port = 1024
to_port = 65535
ipv4_cidr_block = "0.0.0.0/0"
}
]
network_acl_outbound = [
{
# allow outbound HTTPS traffic to any IPv4 address
rule_number = 100
rule_action = "allow"
protocol = "tcp"
from_port = 443
to_port = 443
ipv4_cidr_block = "0.0.0.0/0"
},
{
# allow outbound responses to clients on the internet with ephemeral ports (for example, serving webpages to people visiting the web servers in the subnet)
rule_number = 105
rule_action = "allow"
protocol = "tcp"
from_port = 1024
to_port = 65535
ipv4_cidr_block = "0.0.0.0/0"
}
]
# (optional) share subnet with Organizations, OUs or Accounts - requires RAM to be enabled for Organizations
ram_share_principals = [
local.ntc_parameters["mgmt-organizations"]["ou_ids"]["/root/workloads/prod"]
]
ram_share_allow_external_principals = false
},
{
# (optional) for VPCs with secondary cidr blocks the 'vpc_cidr_identifier' is required. Primary cidr block is always 'primary'
vpc_cidr_identifier = "cloudonly"
# unique identifier for subnet - renaming will cause subnet to be recreated
subnet_identifier = "cloudonly-private"
# subnets can be of type 'private', 'public', 'firewall' or 'transit'
subnet_type = "private"
# setting netmask_length will dynamically calculate or allocate (with ipam) corresponding cidr ranges
# when calculating cidrs the subnets will be sorted from largest to smallest to optimize cidr space
# WARNING: changing the netmask_length can lead to subnets beeing redeployed
netmask_length = 24
# instead of dynamically calculating subnet cidrs based on netmask length a list of static cidrs can be provided
static_cidrs = []
# specific configuration for subnet type
private_subnet_config = {
default_route_to_public_nat_gateway = false
default_route_to_transit_gateway = true
}
# gateway endpoints can be used for 's3' and 'dynamodb' and can only be accessed from inside the VPC
# to access 's3' endpoint from on-premises an interface endpoint is required. Combine both endpoint types for cost optimization
gateway_endpoints = [
{
common_name = "s3"
policy_json = null
# by default gateway endpoint will be associated to current subnet
associate_with_all_subnets = true
},
# {
# common_name = "dynamodb"
# }
]
# interface endpoints can be centralized and can also be accessed from on-premises
# by default a security group will be created with https/443 ingress rule for the local VPC CIDR
interface_endpoints = [
# {
# common_name = "logs"
# policy_json = null
# # private dns should be disabled for centralized endpoints
# private_dns_enabled = true
# },
# {
# common_name = "ec2"
# policy_json = null
# # private dns should be disabled for centralized endpoints
# private_dns_enabled = true
# }
]
# (optional) share subnet with Organizations, OUs or Accounts - requires RAM to be enabled for Organizations
ram_share_principals = [
local.ntc_parameters["mgmt-organizations"]["ou_ids"]["/root/workloads/prod"]
]
ram_share_allow_external_principals = false
},
{
# (optional) for VPCs with secondary cidr blocks the 'vpc_cidr_identifier' is required. Primary cidr block is always 'primary'
vpc_cidr_identifier = "cloudonly"
# unique identifier for subnet - renaming will cause subnet to be recreated
subnet_identifier = "cloudonly-transit"
# subnets can be of type 'private', 'public', 'firewall' or 'transit'
subnet_type = "transit"
# setting netmask_length will dynamically calculate or allocate (with ipam) corresponding cidr ranges
# when calculating cidrs the subnets will be sorted from largest to smallest to optimize cidr space
# WARNING: changing the netmask_length can lead to subnets beeing redeployed
netmask_length = 28
# instead of dynamically calculating subnet cidrs based on netmask length a list of static cidrs can be provided
static_cidrs = []
# specific configuration for subnet type
transit_subnet_config = {
transit_gateway_create_attachment = true
transit_gateway_appliance_mode_support = false
transit_gateway_ipv6_support = false
transit_gateway_dns_support = true
transit_gateway_security_group_referencing_support = true
transit_gateway_default_route_table_association = false
transit_gateway_default_route_table_propagation = false
transit_gateway_id = module.ntc_core_network_frankfurt.transit_gateway_id
# vpc attachement can only be associated with a single transit gateway route table
transit_gateway_association_with_route_table_id = module.ntc_core_network_frankfurt.transit_gateway_route_table_ids["tgw-core-rtb-spoke-prod"]
# vpc attachement can propagate to multiple transit gateway route table for dynamic routing
transit_gateway_propagation_to_route_table_ids = [
module.ntc_core_network_frankfurt.transit_gateway_route_table_ids["tgw-core-rtb-hub"],
module.ntc_core_network_frankfurt.transit_gateway_route_table_ids["tgw-core-rtb-spoke-prod"],
module.ntc_core_network_frankfurt.transit_gateway_route_table_ids["tgw-core-rtb-onprem"]
]
}
# (optional) share subnet with Organizations, OUs or Accounts - requires RAM to be enabled for Organizations
# ram_share_principals = ["o-m29e8d9awz", "ou-6gf5-6ltp3mjf", "945766593056"]
# ram_share_allow_external_principals = false
},
]

# vpc flow logs can be delivered to s3, cloudwatch and kinesis-data-firehose.
# it is possible to send flow logs from a single vpc to multiple targets in parallel e.g. s3 + cloudwatch
vpc_flow_log_destinations = [
{
destination_type = "s3"
destination_arn = try(local.ntc_parameters["log-archive"]["log_bucket_arns"]["vpc_flow_logs"], "")
# decide wether to capture ALL, only ACCEPT or only REJECT traffic
traffic_type = "ALL"
# interval can be 60 seconds (1min) or 600 seconds (10min)
max_aggregation_interval = 600
# log format fields can be customized
# https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-logs-default
# log_format = "$${account-id} $${action} $${bytes} $${dstaddr} $${dstport} $${end} $${instance-id} $${interface-id} $${log-status} $${packets} $${pkt-dstaddr} $${pkt-srcaddr} $${protocol} $${srcaddr} $${srcport} $${start} $${subnet-id} $${tcp-flags} $${type} $${version} $${vpc-id}"
},
# {
# destination_type = "cloud-watch-logs"
# # cloudwatch log group will be created if 'destination_arn' is omitted
# destination_arn = ""
# cloudwatch_options = {
# use_existing_kms_key = false
# kms_key_arn = ""
# # iam role is required when an existing log group is defined in 'destination_arn'
# iam_role_arn = ""
# }
# },
# {
# destination_type = "kinesis-data-firehose"
# destination_arn = "KINESIS_DATA_FIREHOSE_ARN"
# }
]
}

Requirements

The following requirements are needed by this module:

  • terraform (>= 1.3.0)

  • aws (>= 5.69.0)

Providers

The following providers are used by this module:

  • aws (>= 5.69.0)

Modules

The following Modules are called:

flow_logs

Source: ./modules/flow-logs

Version:

subnet_calculator

Source: ./modules/subnet-calculator

Version:

Resources

The following resources are used by this module:

Required Inputs

No required inputs.

Optional Inputs

The following input variables are optional (have default values):

availability_zones

Description: Count of availability zones used in VPC. Subnets will be generated for each reserved availability zone. Some resources like NAT Gateway, VPC Endpoints or RAM sharing will only apply to active availability zones.

Type:

object({
reserved = optional(number, 3)
active = optional(number, 3)
filter_zone_names = optional(list(string), [])
})

Default: {}

custom_dhcp_options

Description: Define a custom DHCP options set on the VPC.

Type:

object({
enable = optional(bool, false)
domain_name = optional(string, null)
domain_name_servers = optional(list(string), ["AmazonProvidedDNS"])
ntp_servers = optional(list(string), [])
netbios_name_servers = optional(list(string), [])
netbios_node_type = optional(number, 2)
})

Default: {}

customer_managed_prefix_lists

Description: Define customer managed prefix lists to be referenced in route tables.

Type:

list(object({
name = optional(string, null)
address_family = optional(string, "IPv4")
entries = optional(list(object({
cidr = string
description = optional(string, null)
})), [])
max_entries = optional(number, null)
ram_share_principals = optional(list(string), [])
ram_share_allow_external_principals = optional(bool, false)
}))

Default: []

default_security_group_egress_rules

Description: List of maps of egress rules to set on the default security group

Type:

list(object({
self = optional(bool, false)
ipv4_cidr_blocks = optional(list(string), [])
ipv6_cidr_blocks = optional(list(string), [])
prefix_list_names = optional(list(string), [])
security_group_ids = optional(list(string), [])
description = optional(string, null)
from_port = optional(number, 0)
to_port = optional(number, 0)
protocol = optional(string, "all")
}))

Default: []

default_security_group_ingress_rules

Description: List of maps of ingress rules to set on the default security group

Type:

list(object({
self = optional(bool, false)
ipv4_cidr_blocks = optional(list(string), [])
ipv6_cidr_blocks = optional(list(string), [])
prefix_list_names = optional(list(string), [])
security_group_ids = optional(list(string), [])
description = optional(string, null)
from_port = optional(number, 0)
to_port = optional(number, 0)
protocol = optional(string, "all")
}))

Default: []

enable_dns_hostnames

Description: Set to true to enable DNS hostnames on the VPC.

Type: bool

Default: true

enable_dns_support

Description: Set to true to enable DNS support on the VPC.

Type: bool

Default: true

enable_network_address_usage_metrics

Description: Set to true to enable Network Address Usage metrics on the VPC.

Type: bool

Default: false

instance_tenancy

Description: A tenancy option for instances launched into the VPC.

Type: string

Default: "default"

interface_endpoints_security_group_ingress

Description: A dedicated security group for VPC interface endpoints. if 'allowed_cidr_blocks' is empty the local VPC CIDR will be added to the ingress rule.

Type:

object({
create_security_group = optional(bool, true)
allowed_cidr_blocks = optional(list(string), [])
allowed_prefix_list_names = optional(list(string), [])
allowed_security_group_ids = optional(list(string), [])
inbound_ports = optional(list(string), ["443"])
})

Default: {}

manage_default_security_group

Description: Should be true to adopt and manage the default security group

Type: bool

Default: true

prefix_name

Description: Prefix name to be used on all VPC resources as identifier.

Type: string

Default: ""

public_natgw_external_eip_ids

Description: List of EIP IDs to be assigned to the public NAT Gateways (used in combination with reuse_nat_ips)

Type: list(string)

Default: []

public_natgw_use_external_ips

Description: Set to true to indicate that existing EIP should be used for public NAT Gateway. Requires specifiying "public_natgw_external_eip_ids".

Type: bool

Default: false

vpc_flow_log_destinations

Description: List of VPC flow log destinations. VPC flow logs can be delivered to S3, CloudWatch or Kineses Data Firehose and multiple destinations can be configured in parallel.

Type:

list(object({
destination_type = string
destination_arn = optional(string, "")
traffic_type = optional(string, "ALL")
log_format = optional(string, null)
max_aggregation_interval = optional(number, 600)
s3_options = optional(object({
file_format = optional(string, "plain-text")
hive_compatible_partitions = optional(bool, false)
per_hour_partition = optional(bool, false)
}), {})
cloudwatch_options = optional(object({
iam_role_arn = optional(string, "")
iam_role_prefix = optional(string, "vpc-flow-log-role-")
iam_policy_prefix = optional(string, "vpc-flow-log-to-cloudwatch-")
use_existing_kms_key = optional(bool, false)
kms_key_arn = optional(string, "")
permissions_boundary_arn = optional(string, null)
name_prefix = optional(string, "/aws/vpc-flow-log/")
retention_in_days = optional(number, null)
}), {})
}))

Default: []

vpc_ipam_settings

Description: Configuration of VPC IPAM (optional). IPAM can be used to dynamically get a VPC CIDR allocated.

Type:

object({
cidrs_requested_from_ipam = optional(bool, false)
cidrs_allocated_by_terraform = optional(bool, false)
reservation_description = optional(string, null)
ipv4_primary_pool_id = optional(string, "")
ipv4_primary_pool_netmask_length = optional(number, 0)
ipv4_secondary_pools = optional(list(object({
cidr_identifier = string
pool_id = string
netmask_length = optional(number, 0)
})), [])
})

Default: {}

vpc_ipv4_primary_cidr

Description: The CIDR block for the VPC. Will be ignored if IPAM is used.

Type: string

Default: "10.0.0.0/16"

vpc_ipv4_secondary_cidr_blocks

Description: List of secondary IPv4 CIDR blocks to associate with the VPC to extend the IP Address pool. Will be ignored if IPAM is used.

Type:

list(object({
cidr_identifier = string
cidr_block = string
}))

Default: []

vpc_subnets

Description: List of subnets with it's own set of arguments corresponding to subnet type. Subnets are duplicated for each zone in "availability_zones".

Type:

list(object({
vpc_cidr_identifier = optional(string, "primary")
subnet_identifier = string
subnet_type = string
netmask_length = optional(number, 0)
static_cidrs = optional(list(string), [])
network_acl_inbound = optional(list(object({
rule_number = number
rule_action = string
protocol = string
from_port = optional(number, null)
to_port = optional(number, null)
icmp_code = optional(number, 0)
icmp_type = optional(number, 0)
ipv4_cidr_block = optional(string, "")
ipv6_cidr_block = optional(string, "")
})), [])
network_acl_outbound = optional(list(object({
rule_number = number
rule_action = string
protocol = string
from_port = optional(number, null)
to_port = optional(number, null)
icmp_code = optional(number, 0)
icmp_type = optional(number, 0)
ipv4_cidr_block = optional(string, "")
ipv6_cidr_block = optional(string, "")
})), [])
ram_share_principals = optional(list(string), [])
ram_share_allow_external_principals = optional(bool, false)
gateway_endpoints = optional(list(object({
common_name = string
policy_json = optional(string, null)
associate_with_all_subnets = optional(bool, false)
})), [])
interface_endpoints = optional(list(object({
common_name = string
policy_json = optional(string, null)
private_dns_enabled = optional(bool, true)
})), [])
private_subnet_config = optional(object({
default_route_to_public_nat_gateway = optional(bool, false)
default_route_to_transit_gateway = optional(bool, false)
create_private_nat_gateway = optional(bool, false)
secondary_private_ip_address_count = optional(number, 0)
}), {})
public_subnet_config = optional(object({
create_public_nat_gateway = optional(bool, false)
map_public_ip_on_launch = optional(bool, true)
default_route_to_internet_gateway = optional(bool, true)
}), {})
firewall_subnet_config = optional(object({
default_route_to_internet_gateway = optional(bool, false)
default_route_to_public_nat_gateway = optional(bool, false)
default_route_to_transit_gateway = optional(bool, false)
}), {})
transit_subnet_config = optional(object({
transit_gateway_create_attachment = optional(bool, false)
transit_gateway_id = optional(string, "")
transit_gateway_skip_route_table_association = optional(bool, false)
transit_gateway_default_route_table_association = optional(bool, true)
transit_gateway_default_route_table_propagation = optional(bool, true)
transit_gateway_association_with_route_table_id = optional(string, "")
transit_gateway_propagation_to_route_table_ids = optional(list(string), [])
transit_gateway_appliance_mode_support = optional(bool, false)
transit_gateway_ipv6_support = optional(bool, false)
transit_gateway_dns_support = optional(bool, true)
transit_gateway_security_group_referencing_support = optional(bool, false)
}), {})
}))

Default: []

Outputs

The following outputs are exported:

all_prefix_list_ids_by_name

Description: List of all prefix lists

custom_dhcp_options_id

Description: The ID of the DHCP options

customer_managed_prefix_lists

Description: Map of customer managed prefix lists

gateway_endpoints

Description: Map of VPC gateway endpoints

interface_endpoints

Description: Map of VPC interface endpoints

internet_gateway_arn

Description: The ARN of the Internet Gateway

internet_gateway_id

Description: The ID of the Internet Gateway

network_acl_ids

Description: Map of Network ACL IDs

private_nat_gateway_ids_by_subnet

Description: List of private NAT Gateway IDs by private subnet

public_nat_gateway_azs

Description: List of Availability Zones where NAT Gateway is deployed

public_nat_gateway_eip_ids

Description: List of allocation ID of Elastic IPs created for public NAT Gateway

public_nat_gateway_eip_public_ips

Description: List of public Elastic IPs created for public NAT Gateway

public_nat_gateway_ids

Description: List of NAT Gateway IDs

route_table_ids

Description: Map of route table IDs

subnet_arns

Description: Map of subnet names with list of ARNs

subnet_cidr_blocks

Description: Map of subnet names with list of cidr_blocks

subnet_ids

Description: Map of subnet names with list of IDs

transit_gateway_vpc_attachement_id

Description: The ID of the transit gateway VPC attachement

vpc_arn

Description: The ARN of the VPC

vpc_availability_zones

Description: The availability zones of the VPC

vpc_cidr_blocks

Description: The CIDR blocks of the VPC

vpc_enable_dns_hostnames

Description: Whether or not the VPC has DNS hostname support

vpc_enable_dns_support

Description: Whether or not the VPC has DNS support

vpc_id

Description: The ID of the VPC

vpc_instance_tenancy

Description: Tenancy of instances spin up within VPC

vpc_owner_id

Description: The ID of the AWS account that owns the VPC