diff --git a/modules/networking/vpc-endpoints/main.tf b/modules/networking/vpc-endpoints/main.tf index da98681..7d6dc78 100644 --- a/modules/networking/vpc-endpoints/main.tf +++ b/modules/networking/vpc-endpoints/main.tf @@ -1,4 +1,5 @@ data "aws_region" "this" {} +data aws_default_tags this {} resource "aws_vpc_endpoint" "vpc-interface-ep" { for_each = toset(var.interface-ep-services) @@ -14,7 +15,7 @@ resource "aws_vpc_endpoint" "vpc-interface-ep" { subnet_ids = local.one_subnet_in_each_az private_dns_enabled = true - tags = merge({ "Name" : "${var.resource-prefix}-vpcep-${each.value}" }, var.default-tags) + tags = { "Name" : "${var.resource-prefix}-vpcep-${each.value}" } lifecycle { precondition { @@ -29,11 +30,11 @@ resource "aws_vpc_endpoint" "vpc-gateway-ep" { vpc_id = data.aws_vpc.this-vpc.id service_name = "com.amazonaws.${data.aws_region.this.name}.${each.value}" vpc_endpoint_type = "Gateway" - - tags = merge({ "Name" : "${var.resource-prefix}-vpcep-${each.value}" }, var.default-tags) + route_table_ids = data.aws_route_tables.this.ids + tags = { "Name" : "${var.resource-prefix}-vpcep-${each.value}" } } -resource random_id rid { +resource "random_id" "rid" { byte_length = 2 } @@ -54,39 +55,40 @@ resource "aws_security_group" "vpc-ep-sg" { from_port = 0 to_port = 0 protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + cidr_blocks = compact(concat(["0.0.0.0/0"], var.secondary_cidrs)) } - tags = merge({ "Name" : "VpcEpAccess" }, var.default-tags) + tags = { "Name" : "VpcEpAccess" } } - - data "aws_vpc" "this-vpc" { id = var.vpc-id } -data "aws_subnets" "this" { +data "aws_availability_zones" "this" { + state = "available" +} + +# find all subnets for this vpc in all availability zones +data "aws_subnets" "subnets_and_az" { + for_each = toset(data.aws_availability_zones.this.zone_ids) + filter { name = "vpc-id" values = [var.vpc-id] } + + filter { + name = "availability-zone-id" + values = [each.value] + } } -data "aws_subnet" "this" { - for_each = toset(data.aws_subnets.this.ids) - id = each.key +data "aws_route_tables" "this" { + vpc_id = var.vpc-id } locals { - subnets_azs = { - for s in data.aws_subnet.this : s.availability_zone => s.id - } - one_subnet_in_each_az = compact([ - for az in data.aws_availability_zones.this.names : lookup(local.subnets_azs, az, "") - ]) + # pick first subnet in each AZ + one_subnet_in_each_az = compact([for k, v in data.aws_subnets.subnets_and_az : try(element(v.ids, length(v.ids) - 1), "")]) } - -data "aws_availability_zones" "this" { - state = "available" -} \ No newline at end of file diff --git a/modules/networking/vpc-endpoints/variables.tf b/modules/networking/vpc-endpoints/variables.tf index a917bcf..3fa355f 100644 --- a/modules/networking/vpc-endpoints/variables.tf +++ b/modules/networking/vpc-endpoints/variables.tf @@ -8,5 +8,9 @@ variable gateway-ep-services { default = ["s3","dynamodb"] description = "Gateway endpoints are free, so deploy for all supported services by default." } -variable default-tags {} -variable resource-prefix {} \ No newline at end of file +variable resource-prefix {} +variable secondary_cidrs { + type = list(string) + description = "Additional cidr blocks" + default = [] +} \ No newline at end of file diff --git a/modules/networking/vpc-subnet-manual/README.md b/modules/networking/vpc-subnet-manual/README.md new file mode 100644 index 0000000..005c94a --- /dev/null +++ b/modules/networking/vpc-subnet-manual/README.md @@ -0,0 +1,36 @@ +# Overview + +This module performs the following tasks: + +- Create VPC, vpcflow log +- Create subnets in every AZ +- Create IGW, NGW +- Create s3 and ddb endpoints which are free + +## Subnet addressing + +Subnet cidrs needs to be specified manually + +## Inputs: + +| Name | Description | Type | Default | Required | +|---------------------------------|---------------------------------------------------|---------------|---------|----------| +| private-subnet-cidrs | private subnets | list | [] | yes | +| public-subnet-cidrs | public subnets | list | [] | yes | +| create-nat-gateway | whether to deploy NAT gateway for private subnets | bool | true | yes | +| vpc-cidr | VPC cidr | string | none | yes | +| enable-flowlog | whether to enable vpc flowlog | bool | true | yes | +| vpcflowlog-retain-days | number of days to retain vpc cloudwatch log | number | 90 | yes | +| vpcflowlog-cwl-loggroup-key-arn | kms key alias arn for log group encryption | string | none | yes | +| secondary_cidr_blocks | Additional CIDR blocks to be associated with VPC | list(string) | none | no | +| resource-prefix | Prefix of resource name | string | "" | yes | + + +## Outputs: + +| Name | Description | Type | +|-----------------------|-------------------------|---------| +| vpc_id | vpc id | string | +| public_subnets | list of cidr blocks | list | +| private_subnets | list of cidr blocks | list | +| secondary_cidr_blocks | list of secondary cidrs | list | diff --git a/modules/networking/vpc-subnet-manual/main.tf b/modules/networking/vpc-subnet-manual/main.tf new file mode 100644 index 0000000..bac0731 --- /dev/null +++ b/modules/networking/vpc-subnet-manual/main.tf @@ -0,0 +1,161 @@ +data aws_caller_identity this {} + +data "aws_availability_zones" "available-az" { + state = "available" +} + +data "aws_default_tags" "this" {} + +locals { + no-az = 2 # hard-coding to 2AZ + vpc-cidr = var.vpc-cidr +} + +resource "aws_subnet" "private-subnets" { + count = length(var.private-subnet-cidrs) + vpc_id = aws_vpc.vpc.id + availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2) + cidr_block = var.private-subnet-cidrs[count.index] + tags = merge(data.aws_default_tags.this.tags, { + Name = "${var.resource-prefix}-private-${split("-", element(data.aws_availability_zones.available-az.names, count.index))[2]}-${count.index + 1}" + }) +} + +resource "aws_subnet" "public-subnets" { + count = length(var.public-subnet-cidrs) + vpc_id = aws_vpc.vpc.id + availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2) + cidr_block = var.public-subnet-cidrs[count.index] + tags = merge(data.aws_default_tags.this.tags, { + Name = "${var.resource-prefix}-public-${split("-", element(data.aws_availability_zones.available-az.names, count.index))[2]}-${count.index + 1}" + }) +} + +resource "aws_vpc" "vpc" { + cidr_block = var.vpc-cidr + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.resource-prefix}-vpc" + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_vpc_ipv4_cidr_block_association" "additional_cidr" { + for_each = toset(var.secondary_cidr_blocks) + vpc_id = aws_vpc.vpc.id + cidr_block = each.value +} + +resource "aws_internet_gateway" "igw" { + count = length(var.public-subnet-cidrs) > 0 ? 1 : 0 + vpc_id = aws_vpc.vpc.id + + tags = { + Name = "${var.resource-prefix}-igw" + } +} + +resource "aws_eip" "ngw-eip" { + count = var.create-nat-gateway ? 1 : 0 + # deprecated # vpc = true + domain = "vpc" + depends_on = [aws_internet_gateway.igw] +} + +resource "aws_nat_gateway" "ngw" { + count = var.create-nat-gateway ? 1 : 0 + allocation_id = aws_eip.ngw-eip[0].id + subnet_id = aws_subnet.public-subnets[0].id + + tags = { + Name = "${var.resource-prefix}-ngw" + } + depends_on = [aws_internet_gateway.igw] +} + +resource "aws_route_table" "public-route-table" { + count = length(var.public-subnet-cidrs) > 0 ? 1 : 0 + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.resource-prefix}-publicroutetable" + } +} + +resource "aws_route_table" "private-route-table" { + count = length(var.private-subnet-cidrs) > 0 ? 1 : 0 + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.resource-prefix}-privateroutetable" + } +} + +resource "aws_route" "public-routes" { + count = length(var.public-subnet-cidrs) > 0 ? 1 : 0 + + destination_cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw[0].id + route_table_id = aws_route_table.public-route-table[0].id +} + +resource "aws_route" "private-routes" { + count = length(var.private-subnet-cidrs) > 0 && var.create-nat-gateway ? 1 : 0 + + destination_cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.ngw[0].id + route_table_id = aws_route_table.private-route-table[0].id +} + +resource "aws_route_table_association" "public_route_association" { + count = length(aws_subnet.public-subnets) + route_table_id = aws_route_table.public-route-table[0].id + subnet_id = aws_subnet.public-subnets[count.index].id +} + +resource "aws_route_table_association" "private_route_association" { + count = length(aws_subnet.private-subnets) + route_table_id = aws_route_table.private-route-table[0].id + subnet_id = aws_subnet.private-subnets[count.index].id +} + +/* +harden default security group. the default sg created by aws allows all egress. +this resource limits ingress and egress from and to itself +*/ + +resource "aws_default_security_group" "default-sg" { + vpc_id = aws_vpc.vpc.id + ingress { + protocol = -1 + self = true + from_port = 0 + to_port = 0 + description = "Allow traffic coming from this SG" + } + egress { + from_port = 0 + protocol = -1 + to_port = 0 + self = true + description = "Allow traffic going to this SG" + } + tags = { + Name = "${var.resource-prefix}-defaultsg" + } +} + +# Enable gateway endpoints which are free +module "vpc-ep" { + count = var.create-free-vpc-endpoints ? 1 : 0 + source = "../vpc-endpoints" + + gateway-ep-services = ["s3", "dynamodb"] + interface-ep-services = [] + resource-prefix = var.resource-prefix + vpc-id = aws_vpc.vpc.id + secondary_cidrs = var.secondary_cidr_blocks +} \ No newline at end of file diff --git a/modules/networking/vpc-subnet-manual/outputs.tf b/modules/networking/vpc-subnet-manual/outputs.tf new file mode 100644 index 0000000..89b527a --- /dev/null +++ b/modules/networking/vpc-subnet-manual/outputs.tf @@ -0,0 +1,39 @@ +output "vpc_id" { + value = aws_vpc.vpc.id +} + +output "vpc-cidr" { + value = aws_vpc.vpc.cidr_block +} + +output "public_subnets" { + value = aws_subnet.public-subnets.*.cidr_block +} + +output "private_subnets" { + value = aws_subnet.private-subnets.*.cidr_block +} + +output "public-subnet-ids" { + value = aws_subnet.public-subnets.*.id +} + +output "private-subnet-ids" { + value = aws_subnet.private-subnets.*.id +} + +output "private-route-table-id" { + value = aws_route_table.private-route-table.*.id +} + +output "public-route-table-id" { + value = aws_route_table.public-route-table.*.id +} + +output "route_tables_for_gateway_endpoints" { + value = concat(aws_route_table.public-route-table.*.id, aws_route_table.private-route-table.*.id) +} + +output "secondary_cidr_blocks" { + value = var.secondary_cidr_blocks +} \ No newline at end of file diff --git a/modules/networking/vpc-subnet-manual/variables.tf b/modules/networking/vpc-subnet-manual/variables.tf new file mode 100644 index 0000000..c2f6b74 --- /dev/null +++ b/modules/networking/vpc-subnet-manual/variables.tf @@ -0,0 +1,37 @@ +variable resource-prefix {} + +# VPC variables +variable "vpc-cidr" {} + +variable "private-subnet-cidrs" { + type = list(any) +} + +variable "public-subnet-cidrs" { + type = list(any) +} + +variable "create-nat-gateway" { + type = bool + default = false +} +variable "enable-flow-log" { + type = bool + default = true +} +variable "vpcflowlog-retain-days" { + type = number + default = 90 +} +variable "vpcflowlog-cwl-loggroup-key-arn" {} +# variable "private-subnet-cidrs" {} +# variable "public-subnet-cidrs" {} +variable "create-free-vpc-endpoints" { + type = bool + default = true +} +variable "secondary_cidr_blocks" { + type = list(string) + description = "Additional cidr blocks" + default = [] +} \ No newline at end of file diff --git a/modules/networking/vpc-subnet-manual/vpc-flowlog.tf b/modules/networking/vpc-subnet-manual/vpc-flowlog.tf new file mode 100644 index 0000000..630c0e9 --- /dev/null +++ b/modules/networking/vpc-subnet-manual/vpc-flowlog.tf @@ -0,0 +1,63 @@ +resource "aws_flow_log" "vpc-flowlog" { + count = var.enable-flow-log ? 1 : 0 + iam_role_arn = aws_iam_role.vpcflowlog-role.arn + log_destination = aws_cloudwatch_log_group.vpcflowlog-loggroup[0].arn + traffic_type = "ALL" + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.resource-prefix}-vpcflowlog" + } +} + +resource "aws_cloudwatch_log_group" "vpcflowlog-loggroup" { + count = var.enable-flow-log ? 1 : 0 + + name_prefix = "vpcflowlog/${aws_vpc.vpc.id}/" + kms_key_id = var.vpcflowlog-cwl-loggroup-key-arn + + retention_in_days = var.vpcflowlog-retain-days +} + +resource "aws_iam_role" "vpcflowlog-role" { + name = "${var.resource-prefix}-vpcflowlog" + path = "/service/" + assume_role_policy = <