1
0

initial commit

This commit is contained in:
xpk
2026-02-13 15:44:24 +08:00
parent 66be8224f4
commit 09ce4c881a
570 changed files with 61807 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
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
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| aws | >= 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | >= 5.0 |
| random | n/a |
## Modules
| Name | Source | Version |
|------|--------|---------|
| private-route | ./modules/RouteTables | n/a |
| private-route-multiaz | ./modules/RouteTables | n/a |
| vpc-ep | ../vpc-endpoints | n/a |
## Resources
| Name | Type |
|------|------|
| [aws_cloudwatch_log_group.vpcflowlog-loggroup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_default_security_group.default-sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_security_group) | resource |
| [aws_eip.ngw-eip](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_eip.ngw-eip-multiaz](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_flow_log.vpc-flowlog](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_flow_log.vpc-flowlog-s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_iam_role.vpcflowlog-role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.vpcflowlog-role-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_internet_gateway.igw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
| [aws_nat_gateway.ngw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource |
| [aws_nat_gateway.ngw-multiaz](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource |
| [aws_route.public-routes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
| [aws_route_table.public-route-table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.public_route_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_subnet.private-subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.public-subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_vpc_ipv4_cidr_block_association.additional_cidr](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipv4_cidr_block_association) | resource |
| [random_id.rid](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [aws_availability_zones.available-az](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_default_tags.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| create-free-vpc-endpoints | Whether to deploy free VPC endpoints (s3 and dynamodb) | `bool` | `true` | no |
| create-nat-gateway | Deploy NAT gateway for private subnets | `bool` | `false` | no |
| enable-flow-log | Whether to enable VPC flowlog | `bool` | `true` | no |
| flow-log-bucket-arn | Arn of S3 bucket to be used for flow logging | `string` | `null` | no |
| flow-log-destination | Destination of flowlog. Valid destinations are s3 or cwlog | `string` | `null` | no |
| multiaz-nat-gateway | Whether to deploy 1 NAT gateway for each AZ | `bool` | `false` | no |
| private-subnet-cidrs | Private subnet CIDRs | `list(string)` | `[]` | no |
| public-subnet-cidrs | Public subnet CIDRs | `list(string)` | `[]` | no |
| resource-prefix | Prefix of resource | `string` | n/a | yes |
| secondary\_cidr\_blocks | Additional cidr blocks | `list(string)` | `[]` | no |
| vpc-cidr | VPC primary CIDR | `string` | n/a | yes |
| vpcflowlog-cwl-loggroup-key-arn | KMS key arn for cwlog encryption | `string` | n/a | yes |
| vpcflowlog-retain-days | Log retention period for CWlogs | `number` | `90` | no |
## Outputs
| Name | Description |
|------|-------------|
| private-subnet-details | Details of private subnets |
| private-subnet-ids | List of private subnet id |
| private\_subnets | Private subnet cidrs |
| public-route-table-id | Public route table id |
| public-subnet-details | Details of public subnets |
| public-subnet-ids | List of public subnet id |
| public\_subnets | Public subnet cidrs |
| secondary\_cidr\_blocks | Secondary CIDR block |
| vpc-cidr | VPC primary cidr |
| vpc\_id | VPC id |
---
## Authorship
This module was developed by xpk.
@@ -0,0 +1,12 @@
module "vpc-subnets" {
source = "../../modules/networking/vpc-subnet-manual"
resource-prefix = local.resource-prefix
private-subnet-cidrs = ["172.17.0.0/24", "172.17.1.0/24"]
public-subnet-cidrs = ["172.17.10.0/24", "172.17.11.0/24"]
vpc-cidr = "172.17.0.0/16"
enable-flow-log = false
vpcflowlog-cwl-loggroup-key-arn = ""
create-nat-gateway = true
create-free-vpc-endpoints = true
}
+203
View File
@@ -0,0 +1,203 @@
data "aws_caller_identity" "this" {}
data "aws_availability_zones" "available-az" {
state = "available"
}
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
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
domain = "vpc"
depends_on = [aws_internet_gateway.igw]
}
resource "aws_nat_gateway" "ngw" {
count = var.create-nat-gateway && !var.multiaz-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_eip" "ngw-eip-multiaz" {
count = var.multiaz-nat-gateway ? length(distinct(aws_subnet.private-subnets.*.availability_zone)) : 0
domain = "vpc"
depends_on = [aws_internet_gateway.igw]
}
resource "aws_nat_gateway" "ngw-multiaz" {
count = var.multiaz-nat-gateway ? length(aws_eip.ngw-eip-multiaz) : 0
allocation_id = aws_eip.ngw-eip-multiaz[count.index].id
subnet_id = aws_subnet.public-subnets[count.index].id
tags = {
Name = "${var.resource-prefix}-ngw-${count.index + 1}"
}
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}-public"
}
}
module "private-route" {
source = "./modules/RouteTables"
count = var.create-nat-gateway && !var.multiaz-nat-gateway ? length(aws_subnet.private-subnets) : 0
ngw-id = aws_nat_gateway.ngw[0].id
resource-prefix = var.resource-prefix
subnet-id = aws_subnet.private-subnets[count.index].id
vpc-id = aws_vpc.vpc.id
}
module "private-route-multiaz" {
source = "./modules/RouteTables"
count = var.multiaz-nat-gateway ? length(aws_subnet.private-subnets) : 0
ngw-id = aws_nat_gateway.ngw-multiaz[count.index].id
resource-prefix = var.resource-prefix
subnet-id = aws_subnet.private-subnets[count.index].id
vpc-id = aws_vpc.vpc.id
}
# 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
and allow icmp only
*/
resource "aws_default_security_group" "default-sg" {
vpc_id = aws_vpc.vpc.id
ingress {
protocol = "icmp"
self = true
from_port = -1
to_port = -1
description = "Allow traffic coming from this SG"
}
egress {
from_port = -1
protocol = "icmp"
to_port = -1
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
}
@@ -0,0 +1,39 @@
<!-- This readme file is generated with terraform-docs -->
## Requirements
No requirements.
## Providers
| Name | Version |
|------|---------|
| aws | n/a |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_route.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
| [aws_route_table.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| ngw-id | NAT Gateway ID | `string` | n/a | yes |
| resource-prefix | Resource prefix | `string` | n/a | yes |
| subnet-id | Subnet ID | `string` | n/a | yes |
| vpc-id | VPC ID | `string` | n/a | yes |
## Outputs
No outputs.
---
## Authorship
This module was developed by xpk.
@@ -0,0 +1,17 @@
resource "aws_route_table" "this" {
vpc_id = var.vpc-id
tags = {
Name = "${var.resource-prefix}-private"
}
}
resource "aws_route" "this" {
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = var.ngw-id
route_table_id = aws_route_table.this.id
}
resource "aws_route_table_association" "this" {
route_table_id = aws_route_table.this.id
subnet_id = var.subnet-id
}
@@ -0,0 +1,19 @@
variable vpc-id {
type = string
description = "VPC ID"
}
variable ngw-id {
type = string
description = "NAT Gateway ID"
}
variable subnet-id {
type = string
description = "Subnet ID"
}
variable resource-prefix {
type = string
description = "Resource prefix"
}
+70
View File
@@ -0,0 +1,70 @@
output "vpc_id" {
description = "VPC id"
value = aws_vpc.vpc.id
}
output "vpc-cidr" {
description = "VPC primary cidr"
value = aws_vpc.vpc.cidr_block
}
output "public_subnets" {
description = "Public subnet cidrs"
value = aws_subnet.public-subnets.*.cidr_block
}
output "private_subnets" {
description = "Private subnet cidrs"
value = aws_subnet.private-subnets.*.cidr_block
}
output "public-subnet-ids" {
description = "List of public subnet id"
value = aws_subnet.public-subnets.*.id
}
output "private-subnet-ids" {
description = "List of private subnet id"
value = aws_subnet.private-subnets.*.id
}
# output "private-route-table-id" {
# value = aws_route_table.private-route-table.*.id
# }
output "public-route-table-id" {
description = "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" {
description = "Secondary CIDR block"
value = var.secondary_cidr_blocks
}
output "public-subnet-details" {
description = "Details of public subnets"
value = [
for k, v in aws_subnet.public-subnets : {
cidr = v.cidr_block,
az = v.availability_zone,
name = v.tags["Name"]
}
]
}
output "private-subnet-details" {
description = "Details of private subnets"
value = [
for k, v in aws_subnet.public-subnets : {
cidr = v.cidr_block,
az = v.availability_zone,
name = v.tags["Name"]
}
]
}
+75
View File
@@ -0,0 +1,75 @@
variable "resource-prefix" {
type = string
description = "Prefix of resource"
}
# VPC variables
variable "vpc-cidr" {
type = string
description = "VPC primary CIDR"
}
variable "private-subnet-cidrs" {
type = list(string)
description = "Private subnet CIDRs"
default = []
}
variable "public-subnet-cidrs" {
type = list(string)
description = "Public subnet CIDRs"
default = []
}
variable "create-nat-gateway" {
description = "Deploy NAT gateway for private subnets"
type = bool
default = false
}
variable "multiaz-nat-gateway" {
type = bool
description = "Whether to deploy 1 NAT gateway for each AZ"
default = false
}
variable "flow-log-destination" {
type = string
description = "Destination of flowlog. Valid destinations are s3 or cwlog"
default = null
}
variable "flow-log-bucket-arn" {
type = string
default = null
description = "Arn of S3 bucket to be used for flow logging"
}
variable "enable-flow-log" {
description = "Whether to enable VPC flowlog"
type = bool
default = true
}
variable "vpcflowlog-retain-days" {
type = number
default = 90
description = "Log retention period for CWlogs"
}
variable "vpcflowlog-cwl-loggroup-key-arn" {
type = string
description = "KMS key arn for cwlog encryption"
}
variable "create-free-vpc-endpoints" {
description = "Whether to deploy free VPC endpoints (s3 and dynamodb)"
type = bool
default = true
}
variable "secondary_cidr_blocks" {
type = list(string)
description = "Additional cidr blocks"
default = []
}
+9
View File
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
@@ -0,0 +1,81 @@
resource "aws_flow_log" "vpc-flowlog" {
count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
iam_role_arn = aws_iam_role.vpcflowlog-role[0].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_flow_log" "vpc-flowlog-s3" {
count = var.enable-flow-log && var.flow-log-destination == "s3" ? 1 : 0
log_destination_type = "s3"
log_destination = var.flow-log-bucket-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 && var.flow-log-destination == "cwlog" ? 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 "random_id" "rid" {
byte_length = 2
}
resource "aws_iam_role" "vpcflowlog-role" {
count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
name = "VpcFlowlogRole-${random_id.rid.dec}"
path = "/service/"
assume_role_policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "",
"Effect" : "Allow",
"Principal" : {
"Service" : "vpc-flow-logs.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
}
)
}
resource "aws_iam_role_policy" "vpcflowlog-role-policy" {
count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
name = "VpcFlowlogRole-${random_id.rid.dec}"
role = aws_iam_role.vpcflowlog-role[0].id
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Action" : [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"kms:Encrypt",
"kms:ReEncrypt",
"kms:Decrypt"
],
"Effect" : "Allow",
"Resource" : "*"
}
]
}
)
}
@@ -0,0 +1,13 @@
# delete-default-vpc module
This terraform module calls awscli to delete default vpc in specified region.
## Example of root module
```terraform
data "aws_regions" "current" {}
module delete-default-vpc {
source = "git::https://xpk.headdesk.me/git/xpk/terraform.aws-baseline-infra//modules/networking/delete-default-vpcs"
for_each = data.aws_regions.current.names
region-name = each.value
}
```
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
region=$1
vpc=$(aws ec2 --region ${region} describe-vpcs --filter Name=isDefault,Values=true | jq -r .Vpcs[0].VpcId)
if [ "${vpc}" = "null" ]; then
echo "No default vpc found"
exit 0
fi
aws ec2 --region ${region} describe-internet-gateways --filter Name=attachment.vpc-id,Values=${vpc} | jq -r '.InternetGateways[0].InternetGatewayId' | while read igw; do
echo "Removing internet gateway ${igw}"
aws ec2 --region ${region} detach-internet-gateway --internet-gateway-id ${igw} --vpc-id ${vpc}
aws ec2 --region ${region} delete-internet-gateway --internet-gateway-id ${igw}
done
aws ec2 --region ${region} describe-subnets --filters Name=vpc-id,Values=${vpc} | jq -r '.Subnets[].SubnetId' | while read subnet; do
echo "Removing subnet ${subnet}"
aws ec2 --region ${region} delete-subnet --subnet-id ${subnet}
done
echo "Removing vpc ${vpc}"
aws ec2 --region ${region} delete-vpc --vpc-id ${vpc}
@@ -0,0 +1,8 @@
data aws_regions all-aws-regions {}
resource "null_resource" "shell" {
for_each = data.aws_regions.all-aws-regions.names
provisioner "local-exec" {
command = "/bin/bash -c '${path.module}/exec.sh ${each.value}'"
}
}
+23
View File
@@ -0,0 +1,23 @@
# nacl module
This module takes in list(list(string)) and construct NACL using dynamic block.
Example code in root module
```hcl
module "nacl" {
source = "../../modules/networking/nacl"
egress_rules = [
["210", "-1", "0", "0", "10.29.0.0/16", "allow"],
["220", "tcp", "443", "443", "10.35.32.0/22", "allow"],
["230", "udp", "53", "53", "10.35.67.0/24", "allow"]
]
ingress_rules = [
["310", "-1", "0", "0", "10.29.0.0/16", "allow"],
["320", "tcp", "80", "81", "10.35.32.0/22", "allow"],
["330", "udp", "53", "53", "10.35.67.0/24", "allow"]
]
subnet_ids = ["subnet-0927ba1b06ccfe6c5", "subnet-0551e96ffd016192a"]
vpc_id = "vpc-01a10b033169f89a8"
acl_name = "test-nacl"
}
```
+32
View File
@@ -0,0 +1,32 @@
resource "aws_network_acl" "this" {
vpc_id = var.vpc_id
subnet_ids = var.subnet_ids
tags = {
Name = var.acl_name
}
dynamic "ingress" {
for_each = var.ingress_rules
content {
rule_no = ingress.value[0]
protocol = ingress.value[1]
from_port = ingress.value[2]
to_port = ingress.value[3]
cidr_block = ingress.value[4]
action = ingress.value[5]
}
}
dynamic "egress" {
for_each = var.egress_rules
content {
rule_no = egress.value[0]
protocol = egress.value[1]
from_port = egress.value[2]
to_port = egress.value[3]
cidr_block = egress.value[4]
action = egress.value[5]
}
}
}
+9
View File
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}
+19
View File
@@ -0,0 +1,19 @@
variable vpc_id {
type = string
}
variable subnet_ids {
type = list(string)
}
variable ingress_rules {
type = list(list(string))
}
variable egress_rules {
type = list(list(string))
}
variable acl_name {
type = string
}
@@ -0,0 +1,19 @@
# r53-record-geo sub module
Create route53 geolocation records
## Example
```hcl
module "r53-www" {
source = "../../modules/networking/r53-record-geo"
record_name = "www"
record_type = "A"
record_values = ["192.168.33.10"]
set_identifier = "Global web servers"
record_type_locational = "A"
record_values_locational = ["172.17.16.10"]
set_identifier_locational = "China web servers"
country_code = "CN"
zone_id = aws_route53_zone.zone.zone_id
}
```
+60
View File
@@ -0,0 +1,60 @@
resource "aws_route53_record" "default" {
zone_id = var.zone_id
name = var.record_name
type = var.record_type
ttl = length(var.alias) > 0 ? null : var.record_ttl
records = var.record_values
set_identifier = var.set_identifier
geolocation_routing_policy {
country = "*"
}
dynamic "alias" {
for_each = var.alias
content {
name = alias.value["name"]
zone_id = alias.value["zone_id"]
evaluate_target_health = alias.value["evaluate_target_health"]
}
}
dynamic "weighted_routing_policy" {
for_each = var.weighted_routing_policy
content {
weight = weighted_routing_policy.value["weight"]
}
}
}
resource "aws_route53_record" "locational" {
zone_id = var.zone_id
name = var.record_name
type = var.record_type_locational
ttl = length(var.alias_locational) > 0 ? null : var.record_ttl_locational
records = var.record_values_locational
set_identifier = var.set_identifier_locational
dynamic "alias" {
for_each = var.alias_locational
content {
name = alias.value["name"]
zone_id = alias.value["zone_id"]
evaluate_target_health = alias.value["evaluate_target_health"]
}
}
geolocation_routing_policy {
country = var.country_code
continent = var.continent_code
}
dynamic "alias" {
for_each = var.alias_locational
content {
name = alias.value["name"]
zone_id = alias.value["zone_id"]
evaluate_target_health = alias.value["evaluate_target_health"]
}
}
dynamic "weighted_routing_policy" {
for_each = var.weighted_routing_policy_locational
content {
weight = weighted_routing_policy.value["weight"]
}
}
}
@@ -0,0 +1,62 @@
variable "zone_id" {}
variable "record_name" {}
variable "record_type" {}
variable "record_ttl" {
type = number
default = 900
}
variable "record_values" {
type = list(string)
}
variable "set_identifier" {}
variable "alias" {
type = list(
object({
zone_id = string
name = string
evaluate_target_health = bool
})
)
}
variable "weighted_routing_policy" {
type = list(
object({
weight = number
})
)
default = []
}
variable "record_type_locational" {}
variable "record_ttl_locational" {
type = number
default = 900
}
variable "record_values_locational" {
type = list(string)
}
variable "set_identifier_locational" {}
variable "country_code" {
type = string
default = null
}
variable "continent_code" {
type = string
default = null
}
variable "alias_locational" {
type = list(
object({
zone_id = string
name = string
evaluate_target_health = bool
})
)
}
variable "weighted_routing_policy_locational" {
type = list(
object({
weight = number
})
)
default = []
}
+30
View File
@@ -0,0 +1,30 @@
# r53-record sub module
Create route53 record
## Example
```hcl
module "r53-ftp" {
source = "../../modules/networking/r53-record"
record_name = "ftp"
record_type = "A"
record_values = ["192.168.0.100"]
zone_id = aws_route53_zone.zone.zone_id
}
module "r53-www" {
source = "../../modules/networking/r53-record"
record_name = "www"
record_type = "A"
record_values = null
alias = [
{
zone_id = "Z2LIHJ7PKBEMWN"
name = "vpce-07b8a9af30673995f-2n2ird8h.ssm.ap-east-1.vpce.amazonaws.com"
evaluate_target_health = true
}
]
zone_id = aws_route53_zone.zone.zone_id
}
```
+22
View File
@@ -0,0 +1,22 @@
resource "aws_route53_record" "this" {
zone_id = var.zone_id
name = var.record_name
type = var.record_type
ttl = length(var.alias) > 0 ? null : var.record_ttl
records = var.record_values
set_identifier = var.set_identifier
dynamic "alias" {
for_each = var.alias
content {
name = alias.value["name"]
zone_id = alias.value["zone_id"]
evaluate_target_health = alias.value["evaluate_target_health"]
}
}
dynamic "weighted_routing_policy" {
for_each = var.weighted_routing_policy
content {
weight = weighted_routing_policy.value["weight"]
}
}
}
@@ -0,0 +1,32 @@
variable "zone_id" {}
variable "record_name" {}
variable "record_type" {}
variable "record_ttl" {
type = number
default = 900
}
variable "record_values" {
type = list(string)
}
variable "set_identifier" {
type = string
default = ""
}
variable "alias" {
type = list(
object({
zone_id = string
name = string
evaluate_target_health = bool
})
)
default = []
}
variable "weighted_routing_policy" {
type = list(
object({
weight = number
})
)
default = []
}
+275
View File
@@ -0,0 +1,275 @@
# vpc-endpoints module
This module deploys VPC endpoints.
Automatically, this module performs the following additional tasks
- Create and attach security group which allows access from the same VPC
- Associate endpoints with 1 subnet in each availability zone
# Inputs
| Variable | Type | Required | Description |
|-----------------------|--------------|----------|-------------------------------------------------|
| voc-id | string | yes | ID of VPC to deploy endpoints to |
| interface-ep-services | list(string) | yes | Interface endpoint names |
| gateway-ep-services | list(string) | no | Gateway endpoint names |
| resource-prefix | string | yes | Prefix that will be added to resource name tags |
# Types of endpoints
## Gateway endpoints
At time of writing, AWS provides 2 gateway endpoints at no charge.
* s3
* dynamodb
For gateway endpoints, all route tables in the VPC will be updated with routes to the private links.
Full documentation: https://docs.aws.amazon.com/vpc/latest/privatelink/gateway-endpoints.html
## Interface endpoints
Interface endpoints are placed in one subnet for every AZ. Security group is created automatically
and allow access from the VPC's cidr, plus all additional CIDRs if applicable.
At time of writing, AWS provides 200+ interface endpoints:
* access-analyzer
* account
* execute-api
* appmesh
* appmesh-envoy-management
* apprunner
* apprunner.requests
* application-autoscaling
* mgn
* appstream.api
* appstream.streaming
* appsync-api
* athena
* auditmanager
* rds
* autoscaling-plans
* backup
* backup-gateway
* batch
* billingconductor
* braket
* cleanrooms
* cloudcontrolapi
* cloudcontrolapi-fips
* clouddirectory
* cloudformation
* cloudhsmv2
* cloudtrail
* evidently
* evidently-dataplane
* monitoring
* rum
* rum-dataplane
* synthetics
* events
* logs
* codeartifact.api
* codeartifact.repositories
* codebuild
* codebuild-fips
* codecommit
* codecommit-fips
* git-codecommit
* git-codecommit-fips
* codedeploy
* codedeploy-commands-secure
* codeguru-profiler
* codeguru-reviewer
* codepipeline
* codestar-connections.api
* comprehend
* comprehendmedical
* config
* app-integrations
* cases
* connect-campaigns
* profile
* voiceid
* wisdom
* dataexchange
* dms
* dms-fips
* datasync
* devops-guru
* ds
* ebs
* ec2
* autoscaling
* imagebuilder
* ecr.api
* ecr.dkr
* ecs
* ecs-agent
* ecs-telemetry
* eks
* elasticbeanstalk
* elasticbeanstalk-health
* drs
* elasticfilesystem
* elasticfilesystem-fips
* elastic-inference.runtime
* elasticloadbalancing
* elasticache
* elasticache-fips
* elasticmapreduce
* emr-containers
* emr-serverless
* events
* fis
* finspace
* finspace-api
* forecast
* forecastquery
* forecast-fips
* forecastquery-fips
* frauddetector
* fsx
* fsx-fips
* glue
* databrew
* grafana
* grafana-workspace
* groundstation
* guardduty-data
* guardduty-data-fips
* healthlake
* identitystore
* rolesanywhere
* inspector2
* iot.data
* iot.fleethub.api
* deviceadvisor.iot
* iotwireless.api
* lorawan.cups
* lorawan.lns
* iotfleetwise
* greengrass
* iotroborunner
* iotsitewise.api
* iotsitewise.data
* iottwinmaker.api
* iottwinmaker.data
* kendra
* kendra-ranking
* kms
* kms-fips
* cassandra
* cassandra-fips
* kinesis-firehose
* kinesis-streams
* lakeformation
* lambda
* models-v2-lex
* runtime-v2-lex
* license-manager
* license-manager-fips
* lookoutequipment
* lookoutmetrics
* lookoutvision
* macie2
* m2
* aps
* aps-workspaces
* airflow.api
* airflow.env
* airflow.ops
* console
* signin
* memory-db
* memorydb-fips
* migrationhub-orchestrator
* refactor-spaces
* migrationhub-strategy
* nimble
* analytics-omics
* control-storage-omics
* storage-omics
* tags-omics
* workflows-omics
* service-managed
* panorama
* payment-cryptography.controlplane
* payment-cryptography.dataplane
* personalize
* personalize-events
* personalize-runtime
* pinpoint
* pinpoint-sms-voice-v2
* polly
* private-networks
* acm-pca
* proton
* qldb.session
* rds
* rds-data
* redshift
* redshift-fips
* redshift-data
* rekognition
* rekognition-fips
* streaming-rekognition
* streaming-rekognition-fips
* robomaker
* s3
* com.amazonaws.s3-global.accesspoint
* s3-outposts
* aws.sagemaker.region.notebook
* aws.sagemaker.region.studio
* sagemaker.api
* sagemaker.featurestore-runtime
* sagemaker.metrics
* sagemaker.runtime
* sagemaker.runtime-fips
* secretsmanager
* securityhub
* sts
* servicecatalog
* servicecatalog-appregistry
* email-smtp
* simspaceweaver
* snow-device-management
* sns
* sqs
* swf
* swf-fips
* states
* sync-states
* storagegateway
* ec2messages
* ssm
* ssm-contacts
* ssm-incidents
* ssmmessages
* tnb
* textract
* textract-fips
* transcribe
* transcribestreaming
* transcribe
* transcribestreaming
* transfer
* transfer.server
* translate
* verifiedpermissions
* vpc-lattice
* workspaces
* xray
Full documentation: https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html
## Example
```hcl
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
}
```
+102
View File
@@ -0,0 +1,102 @@
data "aws_region" "this" {}
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
resource "aws_vpc_endpoint" "vpc-interface-ep" {
for_each = toset(var.interface-ep-services)
vpc_id = data.aws_vpc.this-vpc.id
service_name = "com.amazonaws.${data.aws_region.this.id}.${each.value}"
vpc_endpoint_type = "Interface"
security_group_ids = [
aws_security_group.vpc-ep-sg.id,
]
# deploy to all subnets
subnet_ids = local.one_subnet_in_each_az
private_dns_enabled = true
tags = { "Name" : "${var.resource-prefix}-vpcep-${each.value}" }
lifecycle {
precondition {
condition = data.aws_vpc.this-vpc.enable_dns_support
error_message = "enableDnsSupport needs to be turned on."
}
}
}
resource "aws_vpc_endpoint" "vpc-gateway-ep" {
for_each = toset(var.gateway-ep-services)
vpc_id = data.aws_vpc.this-vpc.id
service_name = "com.amazonaws.${data.aws_region.this.id}.${each.value}"
vpc_endpoint_type = "Gateway"
route_table_ids = data.aws_route_tables.this.ids
tags = { "Name" : "${var.resource-prefix}-vpcep-${each.value}" }
}
resource "random_id" "rid" {
byte_length = 2
}
resource "aws_security_group" "vpc-ep-sg" {
name = "HttpsAccessToVpcEndpoints-${random_id.rid.dec}"
description = "HttpsAccessToVpcEndpoints-${random_id.rid.dec}"
vpc_id = data.aws_vpc.this-vpc.id
ingress {
description = "TLS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
# cidr_blocks = [data.aws_vpc.this-vpc.cidr_block]
cidr_blocks = data.aws_vpc.this-vpc.cidr_block_associations.*.cidr_block
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = { "Name" : "VpcEpAccess" }
}
data "aws_vpc" "this-vpc" {
id = var.vpc-id
}
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_route_tables" "this" {
vpc_id = var.vpc-id
}
locals {
# 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), "")])
}
@@ -0,0 +1,11 @@
# requires 1.3.0 for postcondition validation
# https://learn.hashicorp.com/tutorials/terraform/custom-conditions
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.75.2"
}
}
}
@@ -0,0 +1,18 @@
variable vpc-id {}
variable interface-ep-services {
type = list(string)
description = "List of interface endpoint. E.g. dkr,lambda,kms,elasticloadbalancing,execute-api,ec2,ssm,secretsmanager,monitoring,guardduty-data"
}
variable gateway-ep-services {
type = list(string)
default = []
description = "s3 and dynamodb gateway endpoints are free."
}
variable resource-prefix {}
/*
variable secondary_cidrs {
type = list(string)
description = "Additional cidr blocks"
default = []
}
*/
@@ -0,0 +1,52 @@
# 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 |
## Example:
```hcl
module "vpc-subnets" {
source = "../../modules/networking/vpc-subnet-manual"
resource-prefix = local.resource-prefix
private-subnet-cidrs = ["172.17.0.0/24", "172.17.1.0/24"]
public-subnet-cidrs = ["172.17.10.0/24", "172.17.11.0/24"]
vpc-cidr = "172.17.0.0/16"
enable-flow-log = false
vpcflowlog-cwl-loggroup-key-arn = ""
create-nat-gateway = true
create-free-vpc-endpoints = true
}
```
@@ -0,0 +1,204 @@
data "aws_caller_identity" "this" {
provider = aws.NetworkDeployer
}
data "aws_availability_zones" "available-az" {
provider = aws.NetworkDeployer
state = "available"
}
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
locals {
no-az = 2 # hard-coding to 2AZ
vpc-cidr = var.vpc-cidr
}
resource "aws_subnet" "private-subnets" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
for_each = toset(var.secondary_cidr_blocks)
vpc_id = aws_vpc.vpc.id
cidr_block = each.value
}
resource "aws_internet_gateway" "igw" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
count = var.create-nat-gateway ? 1 : 0
# deprecated # vpc = true
domain = "vpc"
depends_on = [aws_internet_gateway.igw]
}
resource "aws_nat_gateway" "ngw" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
provider = aws.NetworkDeployer
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" {
providers = {
aws = aws.NetworkDeployer
}
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
}
@@ -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
}
@@ -0,0 +1,15 @@
# requires 1.3.0 for postcondition validation
# https://learn.hashicorp.com/tutorials/terraform/custom-conditions
# provider 5.0.0 or above is required by the domain attribute in aws_eip
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0.0"
configuration_aliases = [ aws.NetworkDeployer, aws.SecurityDeployer, aws.CommonDeployer ]
}
}
}
@@ -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 = []
}
@@ -0,0 +1,71 @@
resource "aws_flow_log" "vpc-flowlog" {
provider = aws.NetworkDeployer
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" {
provider = aws.CommonDeployer
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 "random_id" "rid" {
byte_length = 2
}
resource "aws_iam_role" "vpcflowlog-role" {
provider = aws.SecurityDeployer
name = "VpcFlowlogRole-${random_id.rid.dec}"
path = "/service/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "vpc-flow-logs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "vpcflowlog-role-policy" {
provider = aws.SecurityDeployer
name = "VpcFlowlogRole-${random_id.rid.dec}"
role = aws_iam_role.vpcflowlog-role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}
@@ -0,0 +1,109 @@
# 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
- Additional CIDR, if any, will introduce a 10s wait before subnet creation
## 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 |
## Using s3 bucket for flowlog
Make sure the bucket policy allows access from delivery.logs. If the bucket is encrypted with CMK,
make sure the key policy allows the aws service delivery.logs.amazonaws.com.
### Sample s3 bucket policy
```json
{
"Id" : "policy01",
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "AWSLogDeliveryWrite",
"Effect" : "Allow",
"Principal" : {
"Service" : "delivery.logs.amazonaws.com"
},
"Action" : "s3:PutObject",
"Resource" : "arn:aws:s3:::BUCKET_NAME/*"
},
{
"Sid" : "AWSLogDeliveryCheck",
"Effect" : "Allow",
"Principal" : {
"Service" : "delivery.logs.amazonaws.com"
},
"Action" : "s3:GetBucketAcl",
"Resource" : "arn:aws:s3:::BUCKET_NAME"
}
]
}
```
### Sample CMK policy
```json
{
"Sid": "Allow AWS Service to use the key",
"Effect": "Allow",
"Principal": {
"Service": [
"delivery.logs.amazonaws.com",
"cloudtrail.amazonaws.com",
"s3.amazonaws.com"
]
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
```
## Example:
```hcl
module "vpc-subnets" {
source = "../../modules/networking/vpc-subnet-manual"
resource-prefix = local.resource-prefix
private-subnet-cidrs = ["172.17.0.0/24", "172.17.1.0/24"]
public-subnet-cidrs = ["172.17.10.0/24", "172.17.11.0/24"]
vpc-cidr = "172.17.0.0/16"
enable-flow-log = false
vpcflowlog-cwl-loggroup-key-arn = ""
create-nat-gateway = true
create-free-vpc-endpoints = true
}
```
@@ -0,0 +1,179 @@
data "aws_caller_identity" "this" {}
data "aws_availability_zones" "available-az" {
state = "available"
}
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
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}"
TfInternal = try(time_sleep.wait-10s[0].triggers.slept, "na")
})
}
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}"
TfInternal = try(time_sleep.wait-10s[0].triggers.slept, "na")
})
}
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
}
# Optionally wait for additional cidr association
resource "time_sleep" "wait-10s" {
depends_on = [aws_vpc_ipv4_cidr_block_association.additional_cidr]
count = length(var.secondary_cidr_blocks)
create_duration = "10s"
triggers = {
slept = length(aws_vpc_ipv4_cidr_block_association.additional_cidr)
}
}
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
}
@@ -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
}
@@ -0,0 +1,13 @@
# requires 1.3.0 for postcondition validation
# https://learn.hashicorp.com/tutorials/terraform/custom-conditions
# provider 5.0.0 or above is required by the domain attribute in aws_eip
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0.0"
}
}
}
@@ -0,0 +1,65 @@
variable "resource-prefix" {
type = string
description = "Prefix of resource"
}
# VPC variables
variable "vpc-cidr" {
type = string
description = "VPC primary CIDR"
}
variable "private-subnet-cidrs" {
type = list(string)
description = "Private subnet CIDRs"
}
variable "public-subnet-cidrs" {
type = list(string)
description = "Public subnet CIDRs"
default = null
}
variable "create-nat-gateway" {
type = bool
default = false
}
variable "flow-log-destination" {
type = string
description = "Destination of flowlog. Valid destinations are s3 or cwlog"
default = null
}
variable "flow-log-bucket-arn" {
type = string
default = null
description = "Arn of S3 bucket to be used for flow logging"
}
variable "enable-flow-log" {
type = bool
default = true
}
variable "vpcflowlog-retain-days" {
type = number
default = 90
description = "Log retention period for CWlogs"
}
variable "vpcflowlog-cwl-loggroup-key-arn" {
type = string
description = "KMS key arn for cwlog encryption"
}
variable "create-free-vpc-endpoints" {
type = bool
default = true
}
variable "secondary_cidr_blocks" {
type = list(string)
description = "Additional cidr blocks"
default = []
}
@@ -0,0 +1,81 @@
resource "aws_flow_log" "vpc-flowlog" {
count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
iam_role_arn = aws_iam_role.vpcflowlog-role[0].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_flow_log" "vpc-flowlog-s3" {
count = var.enable-flow-log && var.flow-log-destination == "s3" ? 1 : 0
log_destination_type = "s3"
log_destination = var.flow-log-bucket-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 && var.flow-log-destination == "cwlog" ? 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 "random_id" "rid" {
byte_length = 2
}
resource "aws_iam_role" "vpcflowlog-role" {
count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
name = "VpcFlowlogRole-${random_id.rid.dec}"
path = "/service/"
assume_role_policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "",
"Effect" : "Allow",
"Principal" : {
"Service" : "vpc-flow-logs.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
}
)
}
resource "aws_iam_role_policy" "vpcflowlog-role-policy" {
count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
name = "VpcFlowlogRole-${random_id.rid.dec}"
role = aws_iam_role.vpcflowlog-role[0].id
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Action" : [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"kms:Encrypt",
"kms:ReEncrypt",
"kms:Decrypt"
],
"Effect" : "Allow",
"Resource" : "*"
}
]
}
)
}
+51
View File
@@ -0,0 +1,51 @@
# Overview
This module performs the following tasks:
- Create VPC, vpcflow log
- Create subnets in every AZ
- Create IGW, NGW
## Subnet addressing
Subnet cidrs are calculated automatically. Due to the design of terraform's cidrsubnets, this module has limitations:
* supports 2, 4, 6, 8, or 12 subnets in total.
* hard-coded to work with 2 AZs, regardless of number of AZs available in the region.
Based on the input variables, it will create subnet cidrs using the following function
| Private Subnets per az | Public Subnets per az | Function | Example if a /24 is used on VPC |
|------------------------|-----------------------|------------------------------------------------------|---------------------------------|
| 1 | 0 | cidrsubnets(local.vpc-cidr, 1,1) | 2 * /25 |
| 1 | 1 | cidrsubnets(local.vpc-cidr, 2,2,2,2) | 4 * /26 |
| 2 | 1 | cidrsubnets(local.vpc-cidr, 3,3,3,3,3,3) | 6 * /27 |
| 2 | 2 | cidrsubnets(local.vpc-cidr, 3,3,3,3,3,3,3,3) | 8 * /27 |
| 6 | 6 | cidrsubnets(local.vpc-cidr, 4,4,4,4,4,4,4,4,4,4,4,4) | 12 * /28 |
## Inputs:
| Name | Description | Type | Default | Required |
| -------------------------------- | ------------------------------------------------- | ------ | ------- |:--------:|
| application | name of application | string | none | yes |
| environment | capacity of environment (prd/dev/lab) | string | none | yes |
| customer-name | owner of aws resources | string | none | yes |
| project | name of project | string | none | yes |
| default-tags | tags to be added to resources | list | none | yes |
| number-of-private-subnets-per-az | number of private subnets per az | number | 0 | yes |
| number-of-public-subnets-per-az | number of public subnets per az | number | 0 | 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 |
| aws-region-short | short name of aws region (e.g. apne1) | string | none | yes |
| aws-region | aws region (e.g. ap-northeast-1) | string | none | yes |
| vpcflowlog-cwl-loggroup-key-arn | kms key alias arn for log group encryption | string | none | yes |
## Outputs:
| Name | Description | Type |
| --------------- | ------------------- | ------ |
| vpc_id | vpc id | string |
| public_subnets | list of cidr blocks | list |
| private_subnets | list of cidr blocks | list |
+1
View File
@@ -0,0 +1 @@
data aws_caller_identity this {}
+31
View File
@@ -0,0 +1,31 @@
output vpc_id {
value = aws_vpc.vpc.id
}
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 vpc-cidr {
value = aws_vpc.vpc.cidr_block
}
output private-rtb-id {
value = try(aws_route_table.private-route-table[0].id, null)
}
output public-rtb-id {
value = try(aws_route_table.public-route-table[0].id, null)
}
@@ -0,0 +1,42 @@
variable "customer-name" {}
variable "environment" {}
variable "project" {}
variable "application" {}
# variable "default-tags" {}
variable "aws-region" {}
locals {
resource-prefix = "${var.environment}-${substr(var.aws-region, 0, 2)}-${var.customer-name}-${var.project}"
}
# VPC variables
variable "vpc-cidr" {}
variable number-of-public-subnets-per-az {
type = number
default = 0
}
variable number-of-private-subnets-per-az {
type = number
default = 0
}
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
}
@@ -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 = "${local.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 = "${local.resource-prefix}-vpcflowlog"
path = "/service/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "vpc-flow-logs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "vpcflowlog-role-policy" {
name = "${local.resource-prefix}-vpcflowlog"
role = aws_iam_role.vpcflowlog-role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}
+166
View File
@@ -0,0 +1,166 @@
data "aws_availability_zones" "available-az" {
state = "available"
}
locals {
// subnet_start = cidrsubnets(var.vpc-cidr, 1, 1) # divide vpc into 2
# no-az = length(data.aws_availability_zones.available-az.id)
no-az = 2 # hard-coding to 2AZ
vpc-cidr = var.vpc-cidr
total-no-subnets = local.no-az * (var.number-of-private-subnets-per-az + var.number-of-public-subnets-per-az)
# simple-divide = local.total-no-subnets >=8 ? cidrsubnets(local.vpc-cidr, 4,4,4,4,4,4,4,4) : local.total-no-subnets >=6 ? cidrsubnets(local.vpc-cidr, 3,3,3,3,3,3) : local.total-no-subnets >=4 ? cidrsubnets(local.vpc-cidr, 2,2,2,2) : local.total-no-subnets >=2 ? cidrsubnets(local.vpc-cidr, 1,1) : null
simple-divide = local.total-no-subnets >= 12 ? cidrsubnets(local.vpc-cidr, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4) : local.total-no-subnets >= 8 ? cidrsubnets(local.vpc-cidr, 3, 3, 3, 3, 3, 3, 3, 3) : local.total-no-subnets >= 6 ? cidrsubnets(local.vpc-cidr, 3, 3, 3, 3, 3, 3) : local.total-no-subnets >= 4 ? cidrsubnets(local.vpc-cidr, 2, 2, 2, 2) : local.total-no-subnets >= 2 ? cidrsubnets(local.vpc-cidr, 1, 1) : null
public-subnets = slice(local.simple-divide, 0, var.number-of-public-subnets-per-az * local.no-az)
private-subnets = slice(local.simple-divide, var.number-of-public-subnets-per-az * local.no-az, local.total-no-subnets)
}
resource "aws_subnet" "private-subnets" {
count = length(local.private-subnets)
# count = length(var.private-subnet-cidrs)
# count = var.number-of-private-subnets-per-az * length(data.aws_availability_zones.available-az.names)
vpc_id = aws_vpc.vpc.id
availability_zone = element(data.aws_availability_zones.available-az.names, count.index)
# cidr_block = cidrsubnet(local.subnet_start[0], 2, count.index)
# cidr_block = var.private-subnet-cidrs[count.index]
cidr_block = local.private-subnets[count.index]
tags = {
Name = "${local.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(local.public-subnets)
# count = length(var.public-subnet-cidrs)
# count = var.number-of-public-subnets-per-az * length(data.aws_availability_zones.available-az.names)
vpc_id = aws_vpc.vpc.id
availability_zone = element(data.aws_availability_zones.available-az.names, count.index)
# cidr_block = cidrsubnet(local.subnet_start[1], 2, count.index)
# cidr_block = var.public-subnet-cidrs[count.index]
cidr_block = local.public-subnets[count.index]
tags = {
Name = "${local.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 = "${local.resource-prefix}-vpc"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_internet_gateway" "igw" {
count = var.number-of-public-subnets-per-az > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${local.resource-prefix}-igw"
}
}
resource "aws_eip" "ngw-eip" {
count = var.create-nat-gateway ? 1 : 0
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 = "${local.resource-prefix}-ngw"
}
depends_on = [aws_internet_gateway.igw]
}
resource "aws_route_table" "public-route-table" {
count = var.number-of-public-subnets-per-az > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${local.resource-prefix}-public"
}
}
resource "aws_route_table" "private-route-table" {
count = var.number-of-private-subnets-per-az > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${local.resource-prefix}-private"
}
}
resource "aws_route" "public-routes" {
count = var.number-of-public-subnets-per-az > 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 = var.number-of-private-subnets-per-az > 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 = "${local.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 = local.resource-prefix
vpc-id = aws_vpc.vpc.id
}