UPD: back ported changes from upstream. vpc-subnet-manual module now supports s3 flow log

This commit is contained in:
xpk 2024-10-22 08:21:45 +08:00
parent 227918fa3b
commit 49e3228776
Signed by: xpk
GPG Key ID: CD4FF6793F09AB86
4 changed files with 195 additions and 162 deletions

View File

@ -1,91 +1,108 @@
<!-- This readme file is generated with terraform-docs -->
# Overview # Overview
This module performs the following tasks: This module performs the following tasks:
- Create VPC, vpcflow log - Create VPC, vpcflow log
- Create subnets in multiple AZ - Create subnets in every AZ
- Create IGW, NGW - Create IGW, NGW
- Create s3 and ddb endpoints which are free - Create s3 and ddb endpoints which are free
## Requirements ## Subnet addressing
| Name | Version | Subnet cidrs needs to be specified manually
|------|---------|
| terraform | >= 1.3.0 |
| aws | >= 5.0 |
## Providers ## Inputs:
| Name | Version | | Name | Description | Type | Default | Required |
|------|---------| |---------------------------------|---------------------------------------------------|---------------|---------|----------|
| aws | >= 5.0 | | private-subnet-cidrs | private subnets | list | [] | yes |
| random | n/a | | 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 |
## Modules
| Name | Source | Version | ## Outputs:
|------|--------|---------|
| vpc-ep | ../vpc-endpoints | n/a |
## Resources | 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 |
| Name | Type | ## Using s3 bucket for flowlog
|------|------| Make sure the bucket policy allows access from delivery.logs. If the bucket is encrypted with CMK,
| [aws_cloudwatch_log_group.vpcflowlog-loggroup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | make sure the key policy allows the aws service delivery.logs.amazonaws.com.
| [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_flow_log.vpc-flowlog](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_route.private-routes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
| [aws_route.public-routes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
| [aws_route_table.private-route-table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table.public-route-table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.private_route_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | 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 ### 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"
}
]
}
```
| Name | Description | Type | Default | Required | ### Sample CMK policy
|------|-------------|------|---------|:--------:| ```json
| create-free-vpc-endpoints | Set true to deploy s3 and dynamodb endpoints, which are free | `bool` | `true` | no | {
| create-nat-gateway | Set true to deploy NAT gateway | `bool` | `false` | no | "Sid": "Allow AWS Service to use the key",
| enable-flow-log | Set true to deploy vpc flowlog | `bool` | `true` | no | "Effect": "Allow",
| log-group-class | Log group class for flowlog cloudwatch log group, which can be STANDARD or INFREQUENT\_ACCESS | `string` | `"STANDARD"` | no | "Principal": {
| num\_azs | By default, use 2 AZs for redundancy | `number` | `2` | no | "Service": [
| private-subnet-cidrs | List of private subnet cidrs | `list(string)` | n/a | yes | "delivery.logs.amazonaws.com",
| public-subnet-cidrs | List of public subnet cidrs | `list(string)` | n/a | yes | "cloudtrail.amazonaws.com",
| resource-prefix | n/a | `any` | n/a | yes | "s3.amazonaws.com"
| secondary\_cidr\_blocks | Additional cidr blocks | `list(string)` | `[]` | no | ]
| vpc-cidr | CIDR of VPC to be created | `string` | n/a | yes | },
| vpcflowlog-cwl-loggroup-key-arn | CWL log group encryption key arn | `string` | n/a | yes | "Action": [
| vpcflowlog-retain-days | Days to retain flowlog | `number` | `90` | no | "kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
```
## Outputs
| Name | Description | ## Example:
|------|-------------|
| private-route-table-id | n/a | ```hcl
| private-subnet-ids | n/a | module "vpc-subnets" {
| private\_subnets | n/a | source = "../../modules/networking/vpc-subnet-manual"
| public-route-table-id | n/a |
| public-subnet-ids | n/a | resource-prefix = local.resource-prefix
| public\_subnets | n/a | private-subnet-cidrs = ["172.17.0.0/24", "172.17.1.0/24"]
| route\_tables\_for\_gateway\_endpoints | n/a | public-subnet-cidrs = ["172.17.10.0/24", "172.17.11.0/24"]
| secondary\_cidr\_blocks | n/a | vpc-cidr = "172.17.0.0/16"
| vpc-cidr | n/a | enable-flow-log = false
| vpc\_id | n/a | vpcflowlog-cwl-loggroup-key-arn = ""
create-nat-gateway = true
--- create-free-vpc-endpoints = true
## Authorship }
This module was developed by UPDATE_THIS. ```

View File

@ -1,3 +1,9 @@
data "aws_caller_identity" "this" {}
data "aws_availability_zones" "available-az" {
state = "available"
}
data "aws_default_tags" "this" { data "aws_default_tags" "this" {
lifecycle { lifecycle {
postcondition { postcondition {
@ -7,36 +13,29 @@ data "aws_default_tags" "this" {
} }
} }
data "aws_caller_identity" "this" {}
data "aws_availability_zones" "available-az" {
state = "available"
}
locals { locals {
vpc-cidr = var.vpc-cidr no-az = 2 # hard-coding to 2AZ
enabled-az-names = slice(data.aws_availability_zones.available-az.names, 0, var.num_azs) vpc-cidr = var.vpc-cidr
} }
resource "aws_subnet" "private-subnets" { resource "aws_subnet" "private-subnets" {
count = length(var.private-subnet-cidrs) count = length(var.private-subnet-cidrs)
vpc_id = aws_vpc.vpc.id vpc_id = aws_vpc.vpc.id
availability_zone = element(local.enabled-az-names, count.index) availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2)
cidr_block = var.private-subnet-cidrs[count.index] cidr_block = var.private-subnet-cidrs[count.index]
tags = { tags = merge(data.aws_default_tags.this.tags, {
Name = "${var.resource-prefix}-private-${split("-", element(local.enabled-az-names, count.index))[2]}-${count.index + 1}" 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" { resource "aws_subnet" "public-subnets" {
count = length(var.public-subnet-cidrs) count = length(var.public-subnet-cidrs)
vpc_id = aws_vpc.vpc.id vpc_id = aws_vpc.vpc.id
availability_zone = element(local.enabled-az-names, count.index) availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2)
cidr_block = var.public-subnet-cidrs[count.index] cidr_block = var.public-subnet-cidrs[count.index]
tags = { tags = merge(data.aws_default_tags.this.tags, {
Name = "${var.resource-prefix}-public-${split("-", element(local.enabled-az-names, count.index))[2]}-${count.index + 1}" Name = "${var.resource-prefix}-public-${split("-", element(data.aws_availability_zones.available-az.names, count.index))[2]}-${count.index + 1}"
} })
} }
resource "aws_vpc" "vpc" { resource "aws_vpc" "vpc" {
@ -133,21 +132,22 @@ resource "aws_route_table_association" "private_route_association" {
/* /*
harden default security group. the default sg created by aws allows all egress. harden default security group. the default sg created by aws allows all egress.
this resource limits ingress and egress from and to itself this resource limits ingress and egress from and to itself
and allow icmp only
*/ */
resource "aws_default_security_group" "default-sg" { resource "aws_default_security_group" "default-sg" {
vpc_id = aws_vpc.vpc.id vpc_id = aws_vpc.vpc.id
ingress { ingress {
protocol = -1 protocol = "icmp"
self = true self = true
from_port = 0 from_port = -1
to_port = 0 to_port = -1
description = "Allow traffic coming from this SG" description = "Allow traffic coming from this SG"
} }
egress { egress {
from_port = 0 from_port = -1
protocol = -1 protocol = "icmp"
to_port = 0 to_port = -1
self = true self = true
description = "Allow traffic going to this SG" description = "Allow traffic going to this SG"
} }

View File

@ -1,45 +1,59 @@
variable "resource-prefix" {} variable "resource-prefix" {
type = string
description = "Prefix of resource"
}
# VPC variables # VPC variables
variable "vpc-cidr" { variable "vpc-cidr" {
type = string type = string
description = "CIDR of VPC to be created" description = "VPC primary CIDR"
} }
variable "private-subnet-cidrs" { variable "private-subnet-cidrs" {
description = "List of private subnet cidrs" type = list(string)
type = list(string) description = "Private subnet CIDRs"
} }
variable "public-subnet-cidrs" { variable "public-subnet-cidrs" {
description = "List of public subnet cidrs" type = list(string)
type = list(string) description = "Public subnet CIDRs"
default = null
} }
variable "create-nat-gateway" { variable "create-nat-gateway" {
description = "Set true to deploy NAT gateway"
type = bool type = bool
default = false 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" { variable "enable-flow-log" {
description = "Set true to deploy vpc flowlog"
type = bool type = bool
default = true default = true
} }
variable "vpcflowlog-retain-days" { variable "vpcflowlog-retain-days" {
description = "Days to retain flowlog" type = number
type = number default = 90
default = 90 description = "Log retention period for CWlogs"
} }
variable "vpcflowlog-cwl-loggroup-key-arn" { variable "vpcflowlog-cwl-loggroup-key-arn" {
description = "CWL log group encryption key arn" type = string
type = string description = "KMS key arn for cwlog encryption"
} }
variable "create-free-vpc-endpoints" { variable "create-free-vpc-endpoints" {
description = "Set true to deploy s3 and dynamodb endpoints, which are free"
type = bool type = bool
default = true default = true
} }
@ -48,16 +62,4 @@ variable "secondary_cidr_blocks" {
type = list(string) type = list(string)
description = "Additional cidr blocks" description = "Additional cidr blocks"
default = [] default = []
}
variable "num_azs" {
description = "By default, use 2 AZs for redundancy"
type = number
default = 2
}
variable "log-group-class" {
description = "Log group class for flowlog cloudwatch log group, which can be STANDARD or INFREQUENT_ACCESS"
type = string
default = "STANDARD"
} }

View File

@ -1,6 +1,6 @@
resource "aws_flow_log" "vpc-flowlog" { resource "aws_flow_log" "vpc-flowlog" {
count = var.enable-flow-log ? 1 : 0 count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
iam_role_arn = aws_iam_role.vpcflowlog-role.arn iam_role_arn = aws_iam_role.vpcflowlog-role[0].arn
log_destination = aws_cloudwatch_log_group.vpcflowlog-loggroup[0].arn log_destination = aws_cloudwatch_log_group.vpcflowlog-loggroup[0].arn
traffic_type = "ALL" traffic_type = "ALL"
vpc_id = aws_vpc.vpc.id vpc_id = aws_vpc.vpc.id
@ -9,13 +9,22 @@ resource "aws_flow_log" "vpc-flowlog" {
} }
} }
resource "aws_cloudwatch_log_group" "vpcflowlog-loggroup" { resource "aws_flow_log" "vpc-flowlog-s3" {
count = var.enable-flow-log ? 1 : 0 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"
}
}
name_prefix = "/aws/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 kms_key_id = var.vpcflowlog-cwl-loggroup-key-arn
retention_in_days = var.vpcflowlog-retain-days retention_in_days = var.vpcflowlog-retain-days
log_group_class = var.log-group-class
} }
resource "random_id" "rid" { resource "random_id" "rid" {
@ -23,45 +32,50 @@ resource "random_id" "rid" {
} }
resource "aws_iam_role" "vpcflowlog-role" { resource "aws_iam_role" "vpcflowlog-role" {
name = "VpcFlowlogRole-${random_id.rid.dec}" count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
path = "/service/" name = "VpcFlowlogRole-${random_id.rid.dec}"
assume_role_policy = <<EOF path = "/service/"
{ assume_role_policy = jsonencode(
"Version": "2012-10-17",
"Statement": [
{ {
"Sid": "", "Version" : "2012-10-17",
"Effect": "Allow", "Statement" : [
"Principal": { {
"Service": "vpc-flow-logs.amazonaws.com" "Sid" : "",
}, "Effect" : "Allow",
"Action": "sts:AssumeRole" "Principal" : {
"Service" : "vpc-flow-logs.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
} }
] )
}
EOF
} }
resource "aws_iam_role_policy" "vpcflowlog-role-policy" { resource "aws_iam_role_policy" "vpcflowlog-role-policy" {
name = "VpcFlowlogRole-${random_id.rid.dec}" count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0
role = aws_iam_role.vpcflowlog-role.id name = "VpcFlowlogRole-${random_id.rid.dec}"
role = aws_iam_role.vpcflowlog-role[0].id
policy = <<EOF policy = jsonencode(
{
"Version": "2012-10-17",
"Statement": [
{ {
"Action": [ "Version" : "2012-10-17",
"logs:CreateLogGroup", "Statement" : [
"logs:CreateLogStream", {
"logs:PutLogEvents", "Action" : [
"logs:DescribeLogGroups", "logs:CreateLogGroup",
"logs:DescribeLogStreams" "logs:CreateLogStream",
], "logs:PutLogEvents",
"Effect": "Allow", "logs:DescribeLogGroups",
"Resource": "*" "logs:DescribeLogStreams",
"kms:Encrypt",
"kms:ReEncrypt",
"kms:Decrypt"
],
"Effect" : "Allow",
"Resource" : "*"
}
]
} }
] )
}
EOF
} }