diff --git a/examples/external-subnets/README.md b/examples/external-subnets/README.md new file mode 100644 index 000000000..ee06dd7d8 --- /dev/null +++ b/examples/external-subnets/README.md @@ -0,0 +1,16 @@ +# Simple VPC with External Subnets and Network ACLs + +Configuration in this directory creates set of VPC resources along with network ACLs for external subnets. + +## Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Note that this example may create resources which can cost money (AWS Elastic IP, for example). Run `terraform destroy` when you don't need these resources. + diff --git a/examples/external-subnets/main.tf b/examples/external-subnets/main.tf new file mode 100644 index 000000000..789aa0e1e --- /dev/null +++ b/examples/external-subnets/main.tf @@ -0,0 +1,102 @@ +provider "aws" { + region = local.region +} + +locals { + name = "ex-${replace(basename(path.cwd), "_", "-")}" + region = "eu-west-1" + + external_subnets = { + example = { + subnets = { + eu-central-1a = "10.0.201.0/24" + eu-central-1b = "10.0.202.0/24" + eu-central-1c = "10.0.203.0/24" + } + acls = { + inbound = [] + outbound = [] + } + } + } + + + tags = { + Example = local.name + GithubRepo = "terraform-aws-vpc" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# VPC Module +################################################################################ + +module "vpc" { + source = "../../" + + name = local.name + cidr = "10.0.0.0/16" + + azs = ["${local.region}a", "${local.region}b", "${local.region}c"] + private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] + + enable_ipv6 = true + + enable_nat_gateway = false + single_nat_gateway = true + + public_subnet_tags = { + Name = "overridden-name-public" + } + + tags = local.tags + + vpc_tags = { + Name = "vpc-name" + } +} + +################################################################################ +# VPC Module External Subnets +################################################################################ + +module "external_subnets" { + source = "../../modules/external-subnets" + + name = local.name + + vpc_id = module.vpc.vpc_id + vgw_id = module.vpc.vgw_id + + external_subnets = local.external_subnets + + tags = local.tags + + depends_on = [ + module.vpc + ] +} + +################################################################################ +# VPC Module Network ACLs +################################################################################ + +module "network_acls" { + source = "../../modules/network-acls" + + vpc_id = module.vpc.vpc_id + subnet_ids = module.external_subnets.external_subnets + + acl_name = "example" + + tags = { + Owner = "user" + Environment = "dev" + } + + depends_on = [ + module.vpc + ] +} diff --git a/examples/external-subnets/outputs.tf b/examples/external-subnets/outputs.tf new file mode 100644 index 000000000..77f244a90 --- /dev/null +++ b/examples/external-subnets/outputs.tf @@ -0,0 +1,535 @@ +output "vpc_id" { + description = "The ID of the VPC" + value = module.vpc.vpc_id +} + +output "vpc_arn" { + description = "The ARN of the VPC" + value = module.vpc.vpc_arn +} + +output "vpc_cidr_block" { + description = "The CIDR block of the VPC" + value = module.vpc.vpc_cidr_block +} + +output "default_security_group_id" { + description = "The ID of the security group created by default on VPC creation" + value = module.vpc.default_security_group_id +} + +output "default_network_acl_id" { + description = "The ID of the default network ACL" + value = module.vpc.default_network_acl_id +} + +output "default_route_table_id" { + description = "The ID of the default route table" + value = module.vpc.default_route_table_id +} + +output "vpc_instance_tenancy" { + description = "Tenancy of instances spin up within VPC" + value = module.vpc.vpc_instance_tenancy +} + +output "vpc_enable_dns_support" { + description = "Whether or not the VPC has DNS support" + value = module.vpc.vpc_enable_dns_support +} + +output "vpc_enable_dns_hostnames" { + description = "Whether or not the VPC has DNS hostname support" + value = module.vpc.vpc_enable_dns_hostnames +} + +output "vpc_main_route_table_id" { + description = "The ID of the main route table associated with this VPC" + value = module.vpc.vpc_main_route_table_id +} + +output "vpc_ipv6_association_id" { + description = "The association ID for the IPv6 CIDR block" + value = module.vpc.vpc_ipv6_association_id +} + +output "vpc_ipv6_cidr_block" { + description = "The IPv6 CIDR block" + value = module.vpc.vpc_ipv6_cidr_block +} + +output "vpc_secondary_cidr_blocks" { + description = "List of secondary CIDR blocks of the VPC" + value = module.vpc.vpc_secondary_cidr_blocks +} + +output "vpc_owner_id" { + description = "The ID of the AWS account that owns the VPC" + value = module.vpc.vpc_owner_id +} + +output "private_subnets" { + description = "List of IDs of private subnets" + value = module.vpc.private_subnets +} + +output "private_subnet_arns" { + description = "List of ARNs of private subnets" + value = module.vpc.private_subnet_arns +} + +output "private_subnets_cidr_blocks" { + description = "List of cidr_blocks of private subnets" + value = module.vpc.private_subnets_cidr_blocks +} + +output "private_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of private subnets in an IPv6 enabled VPC" + value = module.vpc.private_subnets_ipv6_cidr_blocks +} + +output "public_subnets" { + description = "List of IDs of public subnets" + value = module.vpc.public_subnets +} + +output "public_subnet_arns" { + description = "List of ARNs of public subnets" + value = module.vpc.public_subnet_arns +} + +output "public_subnets_cidr_blocks" { + description = "List of cidr_blocks of public subnets" + value = module.vpc.public_subnets_cidr_blocks +} + +output "public_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of public subnets in an IPv6 enabled VPC" + value = module.vpc.public_subnets_ipv6_cidr_blocks +} + +output "outpost_subnets" { + description = "List of IDs of outpost subnets" + value = module.vpc.outpost_subnets +} + +output "outpost_subnet_arns" { + description = "List of ARNs of outpost subnets" + value = module.vpc.outpost_subnet_arns +} + +output "outpost_subnets_cidr_blocks" { + description = "List of cidr_blocks of outpost subnets" + value = module.vpc.outpost_subnets_cidr_blocks +} + +output "outpost_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of outpost subnets in an IPv6 enabled VPC" + value = module.vpc.outpost_subnets_ipv6_cidr_blocks +} + +output "database_subnets" { + description = "List of IDs of database subnets" + value = module.vpc.database_subnets +} + +output "database_subnet_arns" { + description = "List of ARNs of database subnets" + value = module.vpc.database_subnet_arns +} + +output "database_subnets_cidr_blocks" { + description = "List of cidr_blocks of database subnets" + value = module.vpc.database_subnets_cidr_blocks +} + +output "database_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of database subnets in an IPv6 enabled VPC" + value = module.vpc.database_subnets_ipv6_cidr_blocks +} + +output "database_subnet_group" { + description = "ID of database subnet group" + value = module.vpc.database_subnet_group +} + +output "database_subnet_group_name" { + description = "Name of database subnet group" + value = module.vpc.database_subnet_group_name +} + +output "redshift_subnets" { + description = "List of IDs of redshift subnets" + value = module.vpc.redshift_subnets +} + +output "redshift_subnet_arns" { + description = "List of ARNs of redshift subnets" + value = module.vpc.redshift_subnet_arns +} + +output "redshift_subnets_cidr_blocks" { + description = "List of cidr_blocks of redshift subnets" + value = module.vpc.redshift_subnets_cidr_blocks +} + +output "redshift_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of redshift subnets in an IPv6 enabled VPC" + value = module.vpc.redshift_subnets_ipv6_cidr_blocks +} + +output "redshift_subnet_group" { + description = "ID of redshift subnet group" + value = module.vpc.redshift_subnet_group +} + +output "elasticache_subnets" { + description = "List of IDs of elasticache subnets" + value = module.vpc.elasticache_subnets +} + +output "elasticache_subnet_arns" { + description = "List of ARNs of elasticache subnets" + value = module.vpc.elasticache_subnet_arns +} + +output "elasticache_subnets_cidr_blocks" { + description = "List of cidr_blocks of elasticache subnets" + value = module.vpc.elasticache_subnets_cidr_blocks +} + +output "elasticache_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of elasticache subnets in an IPv6 enabled VPC" + value = module.vpc.elasticache_subnets_ipv6_cidr_blocks +} + +output "intra_subnets" { + description = "List of IDs of intra subnets" + value = module.vpc.intra_subnets +} + +output "intra_subnet_arns" { + description = "List of ARNs of intra subnets" + value = module.vpc.intra_subnet_arns +} + +output "intra_subnets_cidr_blocks" { + description = "List of cidr_blocks of intra subnets" + value = module.vpc.intra_subnets_cidr_blocks +} + +output "intra_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of intra subnets in an IPv6 enabled VPC" + value = module.vpc.intra_subnets_ipv6_cidr_blocks +} + +output "elasticache_subnet_group" { + description = "ID of elasticache subnet group" + value = module.vpc.elasticache_subnet_group +} + +output "elasticache_subnet_group_name" { + description = "Name of elasticache subnet group" + value = module.vpc.elasticache_subnet_group_name +} + +output "public_route_table_ids" { + description = "List of IDs of public route tables" + value = module.vpc.public_route_table_ids +} + +output "private_route_table_ids" { + description = "List of IDs of private route tables" + value = module.vpc.private_route_table_ids +} + +output "database_route_table_ids" { + description = "List of IDs of database route tables" + value = module.vpc.database_route_table_ids +} + +output "redshift_route_table_ids" { + description = "List of IDs of redshift route tables" + value = module.vpc.redshift_route_table_ids +} + +output "elasticache_route_table_ids" { + description = "List of IDs of elasticache route tables" + value = module.vpc.elasticache_route_table_ids +} + +output "intra_route_table_ids" { + description = "List of IDs of intra route tables" + value = module.vpc.intra_route_table_ids +} + +output "public_internet_gateway_route_id" { + description = "ID of the internet gateway route" + value = module.vpc.public_internet_gateway_route_id +} + +output "public_internet_gateway_ipv6_route_id" { + description = "ID of the IPv6 internet gateway route" + value = module.vpc.public_internet_gateway_ipv6_route_id +} + +output "database_internet_gateway_route_id" { + description = "ID of the database internet gateway route" + value = module.vpc.database_internet_gateway_route_id +} + +output "database_nat_gateway_route_ids" { + description = "List of IDs of the database nat gateway route" + value = module.vpc.database_nat_gateway_route_ids +} + +output "database_ipv6_egress_route_id" { + description = "ID of the database IPv6 egress route" + value = module.vpc.database_ipv6_egress_route_id +} + +output "private_nat_gateway_route_ids" { + description = "List of IDs of the private nat gateway route" + value = module.vpc.private_nat_gateway_route_ids +} + +output "private_ipv6_egress_route_ids" { + description = "List of IDs of the ipv6 egress route" + value = module.vpc.private_ipv6_egress_route_ids +} + +output "private_route_table_association_ids" { + description = "List of IDs of the private route table association" + value = module.vpc.private_route_table_association_ids +} + +output "database_route_table_association_ids" { + description = "List of IDs of the database route table association" + value = module.vpc.database_route_table_association_ids +} + +output "redshift_route_table_association_ids" { + description = "List of IDs of the redshift route table association" + value = module.vpc.redshift_route_table_association_ids +} + +output "redshift_public_route_table_association_ids" { + description = "List of IDs of the public redshift route table association" + value = module.vpc.redshift_public_route_table_association_ids +} + +output "elasticache_route_table_association_ids" { + description = "List of IDs of the elasticache route table association" + value = module.vpc.elasticache_route_table_association_ids +} + +output "intra_route_table_association_ids" { + description = "List of IDs of the intra route table association" + value = module.vpc.intra_route_table_association_ids +} + +output "public_route_table_association_ids" { + description = "List of IDs of the public route table association" + value = module.vpc.public_route_table_association_ids +} + +output "dhcp_options_id" { + description = "The ID of the DHCP options" + value = module.vpc.dhcp_options_id +} + +output "nat_ids" { + description = "List of allocation ID of Elastic IPs created for AWS NAT Gateway" + value = module.vpc.nat_ids +} + +output "nat_public_ips" { + description = "List of public Elastic IPs created for AWS NAT Gateway" + value = module.vpc.nat_public_ips +} + +output "natgw_ids" { + description = "List of NAT Gateway IDs" + value = module.vpc.natgw_ids +} + +output "igw_id" { + description = "The ID of the Internet Gateway" + value = module.vpc.igw_id +} + +output "igw_arn" { + description = "The ARN of the Internet Gateway" + value = module.vpc.igw_arn +} + +output "egress_only_internet_gateway_id" { + description = "The ID of the egress only Internet Gateway" + value = module.vpc.egress_only_internet_gateway_id +} + +output "cgw_ids" { + description = "List of IDs of Customer Gateway" + value = module.vpc.cgw_ids +} + +output "cgw_arns" { + description = "List of ARNs of Customer Gateway" + value = module.vpc.cgw_arns +} + +output "this_customer_gateway" { + description = "Map of Customer Gateway attributes" + value = module.vpc.this_customer_gateway +} + +output "vgw_id" { + description = "The ID of the VPN Gateway" + value = module.vpc.vgw_id +} + +output "vgw_arn" { + description = "The ARN of the VPN Gateway" + value = module.vpc.vgw_arn +} + +output "default_vpc_id" { + description = "The ID of the Default VPC" + value = module.vpc.default_vpc_id +} + +output "default_vpc_arn" { + description = "The ARN of the Default VPC" + value = module.vpc.default_vpc_arn +} + +output "default_vpc_cidr_block" { + description = "The CIDR block of the Default VPC" + value = module.vpc.default_vpc_cidr_block +} + +output "default_vpc_default_security_group_id" { + description = "The ID of the security group created by default on Default VPC creation" + value = module.vpc.default_vpc_default_security_group_id +} + +output "default_vpc_default_network_acl_id" { + description = "The ID of the default network ACL of the Default VPC" + value = module.vpc.default_vpc_default_network_acl_id +} + +output "default_vpc_default_route_table_id" { + description = "The ID of the default route table of the Default VPC" + value = module.vpc.default_vpc_default_route_table_id +} + +output "default_vpc_instance_tenancy" { + description = "Tenancy of instances spin up within Default VPC" + value = module.vpc.default_vpc_instance_tenancy +} + +output "default_vpc_enable_dns_support" { + description = "Whether or not the Default VPC has DNS support" + value = module.vpc.default_vpc_enable_dns_support +} + +output "default_vpc_enable_dns_hostnames" { + description = "Whether or not the Default VPC has DNS hostname support" + value = module.vpc.default_vpc_enable_dns_hostnames +} + +output "default_vpc_main_route_table_id" { + description = "The ID of the main route table associated with the Default VPC" + value = module.vpc.default_vpc_main_route_table_id +} + +output "public_network_acl_id" { + description = "ID of the public network ACL" + value = module.vpc.public_network_acl_id +} + +output "public_network_acl_arn" { + description = "ARN of the public network ACL" + value = module.vpc.public_network_acl_arn +} + +output "private_network_acl_id" { + description = "ID of the private network ACL" + value = module.vpc.private_network_acl_id +} + +output "private_network_acl_arn" { + description = "ARN of the private network ACL" + value = module.vpc.private_network_acl_arn +} + +output "outpost_network_acl_id" { + description = "ID of the outpost network ACL" + value = module.vpc.outpost_network_acl_id +} + +output "outpost_network_acl_arn" { + description = "ARN of the outpost network ACL" + value = module.vpc.outpost_network_acl_arn +} + +output "intra_network_acl_id" { + description = "ID of the intra network ACL" + value = module.vpc.intra_network_acl_id +} + +output "intra_network_acl_arn" { + description = "ARN of the intra network ACL" + value = module.vpc.intra_network_acl_arn +} + +output "database_network_acl_id" { + description = "ID of the database network ACL" + value = module.vpc.database_network_acl_id +} + +output "database_network_acl_arn" { + description = "ARN of the database network ACL" + value = module.vpc.database_network_acl_arn +} + +output "redshift_network_acl_id" { + description = "ID of the redshift network ACL" + value = module.vpc.redshift_network_acl_id +} + +output "redshift_network_acl_arn" { + description = "ARN of the redshift network ACL" + value = module.vpc.redshift_network_acl_arn +} + +output "elasticache_network_acl_id" { + description = "ID of the elasticache network ACL" + value = module.vpc.elasticache_network_acl_id +} + +output "elasticache_network_acl_arn" { + description = "ARN of the elasticache network ACL" + value = module.vpc.elasticache_network_acl_arn +} + +# VPC flow log +output "vpc_flow_log_id" { + description = "The ID of the Flow Log resource" + value = module.vpc.vpc_flow_log_id +} + +output "vpc_flow_log_destination_arn" { + description = "The ARN of the destination for VPC Flow Logs" + value = module.vpc.vpc_flow_log_destination_arn +} + +output "vpc_flow_log_destination_type" { + description = "The type of the destination for VPC Flow Logs" + value = module.vpc.vpc_flow_log_destination_type +} + +output "vpc_flow_log_cloudwatch_iam_role_arn" { + description = "The ARN of the IAM role used when pushing logs to Cloudwatch log group" + value = module.vpc.vpc_flow_log_cloudwatch_iam_role_arn +} diff --git a/examples/external-subnets/variables.tf b/examples/external-subnets/variables.tf new file mode 100644 index 000000000..e69de29bb diff --git a/examples/external-subnets/versions.tf b/examples/external-subnets/versions.tf new file mode 100644 index 000000000..979baa847 --- /dev/null +++ b/examples/external-subnets/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.73" + } + } +} diff --git a/modules/external-subnets/README.md b/modules/external-subnets/README.md new file mode 100644 index 000000000..4a8ca7299 --- /dev/null +++ b/modules/external-subnets/README.md @@ -0,0 +1,35 @@ +# AWS VPC External Subnets Terraform sub-module + +Terraform sub-module which creates VPC External Subnets resources on AWS. + +## Usage + +See [`examples`](../../examples) directory for working examples to reference: + +```hcl +module "external_subnets" { + source = "terraform-aws-modules/vpc/aws//modules/external-subnets" + + vpc_id = module.vpc.vpc_id + vgw_id = module.vpc.vgw_id + + external_subnets = { + example = { + subnets = { + eu-central-1a = "10.0.1.0/24" + eu-central-1b = "10.0.2.0/24" + eu-central-1c = "10.0.3.0/24" + } + } + } + + tags = { + Owner = "user" + Environment = "dev" + } +} +``` + +## Examples + +- [Complete-VPC](../../examples/external-subnets) with VPC External Subnets. diff --git a/modules/external-subnets/main.tf b/modules/external-subnets/main.tf new file mode 100644 index 000000000..4ec03dcce --- /dev/null +++ b/modules/external-subnets/main.tf @@ -0,0 +1,119 @@ +locals { + + subnets = { + for key, value in var.external_subnets : key => value.subnets + } + + list = flatten([ + for subnet_name, subnet_value in local.subnets : [ + for subnet_az, subnet_cidr in subnet_value : { + subnet_name = subnet_name + subnet_az = subnet_az + subnet_cidr = subnet_cidr + }]]) + + route_list = (var.single_nat_external) ? { + for entry in local.list : entry.subnet_name => entry.subnet_az... if entry.subnet_az == "eu-central-1a" + } : { + for entry in local.list : "${entry.subnet_name}-${entry.subnet_az}" => entry.subnet_az... + } + + subnet_list = { + for entry in local.list : "${entry.subnet_name}-${entry.subnet_az}" => entry + } +} + + +################################################################################ +# Route(s) +################################################################################ +resource "aws_route_table" "external_subnets" { + for_each = var.create ? local.route_list : {} + + vpc_id = var.vpc_id + + tags = merge( + { + "Name" = format("%s-%s", var.name, each.key) + }, + var.tags + ) +} + + +################################################################################ +# Default route(s) +# There are as many routing tables as the number of NAT gateways +################################################################################ +data "aws_nat_gateway" "external_subnets" { + for_each = var.create && length(aws_route_table.external_subnets) > 0 ? { + for k, v in local.route_list : k => v if k != "shared" + } : {} + + tags = merge( + { + Name = format("%s-${each.value[0]}", var.name) + }, + var.tags + ) +} + +resource "aws_route" "external_subnets" { + for_each = var.create && length(aws_route_table.external_subnets) > 0 ? { + for k, v in local.route_list : k => v if k != "shared" + } : {} + + route_table_id = aws_route_table.external_subnets[each.key].id + destination_cidr_block = "0.0.0.0/0" + nat_gateway_id = data.aws_nat_gateway.external_subnets[each.key].id + + timeouts { + create = "5m" + } +} + + +################################################################################ +# Route table association with subnets +################################################################################ +resource "aws_route_table_association" "external_subnets" { + for_each = var.create && length(aws_subnet.external_subnets) > 0 ? local.subnet_list : {} + + subnet_id = aws_subnet.external_subnets[each.key].id + route_table_id = (var.single_nat_external) ? aws_route_table.external_subnets[each.value.subnet_name].id : aws_route_table.external_subnets[each.key].id + + depends_on = [ + aws_subnet.external_subnets, + aws_route_table.external_subnets + ] +} + + +################################################################################ +# Subnets +################################################################################ +resource "aws_subnet" "external_subnets" { + for_each = var.create ? local.subnet_list : {} + + vpc_id = var.vpc_id + cidr_block = each.value.subnet_cidr + availability_zone = length(regexall("^[a-z]{2}-", each.value.subnet_az)) > 0 ? each.value.subnet_az : null + availability_zone_id = length(regexall("^[a-z]{2}-", each.value.subnet_az)) == 0 ? each.value.subnet_az : null + + tags = merge( + { + "Name" = format("%s-%s", var.name, each.key) + }, + var.tags + ) +} + +################################################################################ +# Route table association with VGW +################################################################################ +resource "aws_vpn_gateway_route_propagation" "external_subnets" { + for_each = var.create && var.propagate_external_route_tables_vgw ? local.route_list : {} + + route_table_id = aws_route_table.external_subnets[each.key].id + vpn_gateway_id = var.vgw_id +} diff --git a/modules/external-subnets/outputs.tf b/modules/external-subnets/outputs.tf new file mode 100644 index 000000000..188b47fc4 --- /dev/null +++ b/modules/external-subnets/outputs.tf @@ -0,0 +1,29 @@ +output "external_subnets" { + description = "List of IDs of external subnets" + value = try(aws_subnet.external_subnets[*].id, []) +} + +output "external_subnet_arns" { + description = "List of ARNs of external subnets" + value = try(aws_subnet.external_subnets[*].arn, []) +} + +output "external_subnets_cidr_blocks" { + description = "List of cidr_blocks of external subnets" + value = try(compact(aws_subnet.external_subnets[*].cidr_block), []) +} + +output "external_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of external subnets in an IPv6 enabled VPC" + value = try(compact(aws_subnet.external_subnets[*].ipv6_cidr_block), []) +} + +output "external_route_table_ids" { + description = "List of IDs of external route tables" + value = try(aws_route_table.external_subnets[*].id, []) +} + +output "external_route_table_association_ids" { + description = "List of IDs of the external route table association" + value = try(aws_route_table_association.external_subnets[*].id, []) +} diff --git a/modules/external-subnets/variables.tf b/modules/external-subnets/variables.tf new file mode 100644 index 000000000..fa725388a --- /dev/null +++ b/modules/external-subnets/variables.tf @@ -0,0 +1,47 @@ +variable "create" { + description = "Determines whether resources will be created" + type = bool + default = true +} + +variable "name" { + description = "Name to be used on all the resources as identifier" + type = string + default = "" +} + +variable "vpc_id" { + description = "The ID of the VPC in which the endpoint will be used" + type = string + default = null +} + +variable "vgw_id" { + description = "The ID of the VPN Gateway" + type = string + default = "" +} + +variable "tags" { + description = "A map of tags to use on all resources" + type = map(string) + default = {} +} + +variable "external_subnets" { + description = "A map of list external subnets inside the VPC" + type = map(any) + default = {} +} + +variable "propagate_external_route_tables_vgw" { + description = "Should be true if you want route table propagation at external network created outside of vpc module." + type = bool + default = true +} + +variable "single_nat_external" { + description = "Create single gateway NAT in VPC for external subnets, false provide multiple az gateways" + type = bool + default = true +} diff --git a/modules/external-subnets/versions.tf b/modules/external-subnets/versions.tf new file mode 100644 index 000000000..979baa847 --- /dev/null +++ b/modules/external-subnets/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.73" + } + } +} diff --git a/modules/network-acls/README.md b/modules/network-acls/README.md new file mode 100644 index 000000000..c3b550d8b --- /dev/null +++ b/modules/network-acls/README.md @@ -0,0 +1,27 @@ +# AWS VPC Network ACLs Terraform sub-module + +Terraform sub-module which creates VPC Network ACLs resources on AWS. + +## Usage + +See [`examples`](../../examples) directory for working examples to reference: + +```hcl +module "network_acls" { + source = "terraform-aws-modules/vpc/aws//modules/network-acls" + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.subnet_ids + + acl_name = "example" + + tags = { + Owner = "user" + Environment = "dev" + } +} +``` + +## Examples + +- [Complete-VPC](../../examples/complete-vpc) with VPC Endpoints. diff --git a/modules/network-acls/main.tf b/modules/network-acls/main.tf new file mode 100644 index 000000000..a798b8337 --- /dev/null +++ b/modules/network-acls/main.tf @@ -0,0 +1,50 @@ +################################################################################ +# this Network ACLs +################################################################################ +resource "aws_network_acl" "this" { + count = length(var.acl_name) > 0 ? 1 : 0 + + vpc_id = var.vpc_id + subnet_ids = var.subnet_ids + + tags = merge( + { + "Name" = format("%s", var.acl_name) + }, + var.tags + ) +} + +resource "aws_network_acl_rule" "this_inbound" { + count = length(var.inbound_acl_rules) > 0 ? length(var.inbound_acl_rules) : 0 + + network_acl_id = aws_network_acl.this[0].id + + egress = false + rule_number = var.inbound_acl_rules[count.index]["rule_number"] + rule_action = var.inbound_acl_rules[count.index]["rule_action"] + from_port = lookup(var.inbound_acl_rules[count.index], "from_port", null) + to_port = lookup(var.inbound_acl_rules[count.index], "to_port", null) + icmp_code = lookup(var.inbound_acl_rules[count.index], "icmp_code", null) + icmp_type = lookup(var.inbound_acl_rules[count.index], "icmp_type", null) + protocol = var.inbound_acl_rules[count.index]["protocol"] + cidr_block = lookup(var.inbound_acl_rules[count.index], "cidr_block", null) + ipv6_cidr_block = lookup(var.inbound_acl_rules[count.index], "ipv6_cidr_block", null) +} + +resource "aws_network_acl_rule" "this_outbound" { + count = length(var.outbound_acl_rules) > 0 ? length(var.outbound_acl_rules) : 0 + + network_acl_id = aws_network_acl.this[0].id + + egress = true + rule_number = var.outbound_acl_rules[count.index]["rule_number"] + rule_action = var.outbound_acl_rules[count.index]["rule_action"] + from_port = lookup(var.outbound_acl_rules[count.index], "from_port", null) + to_port = lookup(var.outbound_acl_rules[count.index], "to_port", null) + icmp_code = lookup(var.outbound_acl_rules[count.index], "icmp_code", null) + icmp_type = lookup(var.outbound_acl_rules[count.index], "icmp_type", null) + protocol = var.outbound_acl_rules[count.index]["protocol"] + cidr_block = lookup(var.outbound_acl_rules[count.index], "cidr_block", null) + ipv6_cidr_block = lookup(var.outbound_acl_rules[count.index], "ipv6_cidr_block", null) +} diff --git a/modules/network-acls/outputs.tf b/modules/network-acls/outputs.tf new file mode 100644 index 000000000..44a37f6ec --- /dev/null +++ b/modules/network-acls/outputs.tf @@ -0,0 +1,9 @@ +output "network_acl_id" { + description = "ID of the network ACL" + value = try(aws_network_acl.this[0].id, "") +} + +output "network_acl_arn" { + description = "ARN of the network ACL" + value = try(aws_network_acl.this[0].arn, "") +} diff --git a/modules/network-acls/variables.tf b/modules/network-acls/variables.tf new file mode 100644 index 000000000..54f5c06a1 --- /dev/null +++ b/modules/network-acls/variables.tf @@ -0,0 +1,53 @@ +variable "acl_name" { + description = "Name to be used on the Network ACL." + type = string + default = "" +} + +variable "tags" { + description = "Additional tags for the Default Network ACL." + type = map(string) + default = {} +} + +variable "vpc_id" { + description = "The ID of the VPC where network acl are created." + type = string +} + +variable "subnet_ids" { + description = "A list of subnets where Network ACL are attached." + type = list(string) + default = [] +} +variable "inbound_acl_rules" { + description = "External subnets inbound network ACLs" + type = list(map(string)) + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "outbound_acl_rules" { + description = "External subnets outbound network ACLs" + type = list(map(string)) + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} diff --git a/modules/network-acls/versions.tf b/modules/network-acls/versions.tf new file mode 100644 index 000000000..ab4d354a9 --- /dev/null +++ b/modules/network-acls/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.28" + } + } +}