Skip to content

Commit

Permalink
feat(networking): create VPC, Flow Log and NACL modules (#5)
Browse files Browse the repository at this point in the history
* feat(networking): create VPC networking module

* feat(networking): create VPC Flow Logs networking module

* chore: update gitignore

* feat(networking): create VPC Network ACL networking module

* chore(networking): force use of IGW

* chore: create simple example and add NACLs to VPC

* chore: always manage default security group

* feat: create VPC Flow Logs in VPC module

* chore: update flow log cloudwatch name

* fix: create ipv6 egress only internet gateway

* fix: enable dns64

* chore: set module outputs
  • Loading branch information
p5 authored Aug 29, 2024
1 parent f935f30 commit 4af1c6a
Show file tree
Hide file tree
Showing 27 changed files with 1,439 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Terraform/Terragrunt
.terraform
.terraform.lock.hcl
.terragrunt-cache
terraform.tfstate
terraform.tfvars
Expand Down
Empty file removed aws/.gitkeep
Empty file.
55 changes: 55 additions & 0 deletions aws/networking/vpc-flow-logs/cloudwatch.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
resource "aws_cloudwatch_log_group" "logs" {
count = local.enable_cloudwatch_destination ? 1 : 0

name = "/aws/vpc/${var.name}-flow-logs"
retention_in_days = var.cloudwatch_log_retention_in_days

tags = var.tags_all
}

resource "aws_iam_role" "cloudwatch" {
count = local.enable_cloudwatch_destination ? 1 : 0

name_prefix = "${var.name}-flow-logs"
assume_role_policy = data.aws_iam_policy_document.cloudwatch_assume_role[0].json

tags = var.tags_all
}

resource "aws_iam_policy" "cloudwatch_iam_policy" {
count = local.enable_cloudwatch_destination ? 1 : 0

name_prefix = "allow-publish-flow-logs-"
policy = data.aws_iam_policy_document.cloudwatch_iam_policy[0].json

tags = var.tags_all
}

data "aws_iam_policy_document" "cloudwatch_assume_role" {
count = local.enable_cloudwatch_destination ? 1 : 0

statement {
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["vpc-flow-logs.amazonaws.com"]
}
}
}

data "aws_iam_policy_document" "cloudwatch_iam_policy" {
count = local.enable_cloudwatch_destination ? 1 : 0

statement {
sid = "AllowPublishToCloudWatchLogs"
effect = "Allow"
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
]
resources = ["${aws_cloudwatch_log_group.logs[0].arn}:*"]
}
}
39 changes: 39 additions & 0 deletions aws/networking/vpc-flow-logs/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

locals {
enable_s3_destination = var.log_destination_type == "s3"
enable_cloudwatch_destination = var.log_destination_type == "cloud-watch-logs"

create_s3_bucket = local.enable_s3_destination && var.s3_bucket_name != null

log_destination_arn = coalesce(
local.enable_s3_destination && local.create_s3_bucket ? aws_s3_bucket.logs[0].arn : null,
local.enable_s3_destination && !local.create_s3_bucket ? var.existing_s3_bucket_arn : null,
local.enable_cloudwatch_destination ? aws_cloudwatch_log_group.logs[0].arn : null
)
}

resource "aws_flow_log" "this" {
vpc_id = var.vpc_id
traffic_type = var.traffic_type

log_format = var.log_format
log_destination = local.log_destination_arn
log_destination_type = var.log_destination_type
max_aggregation_interval = var.max_aggregation_interval
iam_role_arn = local.enable_cloudwatch_destination ? aws_iam_role.cloudwatch[0].arn : null

dynamic "destination_options" {
for_each = var.destination_options != null ? [var.destination_options] : []
content {
file_format = destination_options.value.file_format
hive_compatible_partitions = destination_options.value.hive_compatible_partitions
per_hour_partition = destination_options.value.per_hour_partition
}
}

tags = merge(var.tags_all, {
Name = "${var.name}-flow-logs"
})
}
147 changes: 147 additions & 0 deletions aws/networking/vpc-flow-logs/s3.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
resource "aws_s3_bucket" "logs" {
count = local.create_s3_bucket ? 1 : 0

bucket = var.s3_bucket_name
tags = var.tags_all
}

resource "aws_s3_bucket_public_access_block" "logs" {
count = local.create_s3_bucket ? 1 : 0

bucket = aws_s3_bucket.logs[0].id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_s3_bucket_versioning" "logs" {
count = local.create_s3_bucket ? 1 : 0

bucket = aws_s3_bucket.logs[0].id

versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_policy" "logs" {
count = local.create_s3_bucket ? 1 : 0

bucket = aws_s3_bucket.logs[0].id
policy = data.aws_iam_policy_document.s3_policy[0].json
}

data "aws_iam_policy_document" "s3_policy" {
count = local.create_s3_bucket ? 1 : 0

statement {
sid = "AWSLogDeliveryWrite"
effect = "Allow"
actions = ["s3:PutObject"]
resources = [
var.destination_options.hive_compatible_partitions ?
"${aws_s3_bucket.logs[0].arn}/AWSLogs/aws-account-id=${data.aws_caller_identity.current.account_id}/*" :
"${aws_s3_bucket.logs[0].arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"
]
principals {
type = "Service"
identifiers = ["delivery.logs.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "s3:x-amz-acl"
values = ["bucket-owner-full-control"]
}
condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [data.aws_caller_identity.current.account_id]
}
condition {
test = "ArnLike"
variable = "aws:SourceArn"
values = ["arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"]
}
}

statement {
sid = "AWSLogDeliveryAclCheck"
effect = "Allow"
principals {
type = "Service"
identifiers = ["delivery.logs.amazonaws.com"]
}
actions = ["s3:GetBucketAcl"]
resources = [
aws_s3_bucket.logs[0].arn
]
condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [data.aws_caller_identity.current.account_id]
}
condition {
test = "ArnLike"
variable = "aws:SourceArn"
values = ["arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"]
}
}

statement {
sid = "EnforceProtocolVersion"
effect = "Deny"
principals {
type = "*"
identifiers = ["*"]
}
actions = ["s3:*"]
resources = [
aws_s3_bucket.logs[0].arn,
"${aws_s3_bucket.logs[0].arn}/*"
]
condition {
test = "NumericLessThan"
variable = "s3:TlsVersion"
values = ["1.2"]
}
}
}

resource "aws_s3_bucket_lifecycle_configuration" "logs" {
count = local.create_s3_bucket ? 1 : 0

bucket = aws_s3_bucket.logs[0].id

rule {
id = "archive"
status = "Enabled"

transition {
days = var.s3_infrequent_access_transition_in_days
storage_class = "STANDARD_IA"
}

transition {
days = var.s3_glacier_transition_in_days
storage_class = "GLACIER"
}

noncurrent_version_expiration {
noncurrent_days = var.s3_non_current_version_expiration_in_days
}

expiration {
days = var.s3_log_retention_in_days
}
}

rule {
id = "abort-incomplete-multipart-uploads"
status = "Enabled"
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
}
}
93 changes: 93 additions & 0 deletions aws/networking/vpc-flow-logs/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
variable "name" {
description = "The name of the flow log."
type = string
}

variable "vpc_id" {
description = "The VPC ID."
type = string
}

variable "traffic_type" {
description = "The type of traffic to capture. Valid values: ACCEPT, REJECT, ALL."
type = string
default = "ALL"
}

variable "log_destination_type" {
description = "The type of destination to which the flow log data is published. Valid values: cloud-watch-logs, s3."
type = string
default = "cloud-watch-logs"
}

variable "log_format" {
description = "The fields to include in the flow log record, in the order in which they should appear. For a list of available fields, see Flow log record format - https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-log-records"
type = string
default = null
}

variable "max_aggregation_interval" {
description = "The maximum interval of time during which a flow of packets is captured and aggregated into a flow log record. Valid Values: 60 seconds (1 minute) or 600 seconds (10 minutes)."
type = number
default = 600
}

variable "destination_options" {
description = "Destination options for flow log delivery. For more information, see Flow log delivery options - https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-log-destinations"
type = object({
file_format = optional(string, null)
hive_compatible_partitions = optional(bool, null)
per_hour_partition = optional(bool, null)
})
default = null
}

variable "s3_bucket_name" {
description = "The name of a new S3 bucket to which the flow log data should be published."
type = string
default = null
}

variable "existing_s3_bucket_arn" {
description = "The ARN of an existing S3 bucket to which the flow log data should be published."
type = string
default = null
}

variable "s3_infrequent_access_transition_in_days" {
description = "The number of days to retain log events in the specified S3 bucket for infrequent access. Valid values: 1-3650."
type = number
default = 30
}

variable "s3_glacier_transition_in_days" {
description = "The number of days to retain log events in the specified S3 bucket for Glacier transition. Valid values: 1-3650."
type = number
default = 180
}

variable "s3_log_retention_in_days" {
description = "The number of days to retain log events in the specified S3 bucket. Valid values: 1-3650."
type = number
default = 365
}

variable "s3_non_current_version_expiration_in_days" {
description = "The number of days to expire non-current S3 object versions."
type = number
default = 30
}

# CloudWatch is designed for short-term retention of log events. For long-term retention, export log events to S3.
variable "cloudwatch_log_retention_in_days" {
description = "The number of days to retain log events in the specified CloudWatch log group. Valid values: 1-3650."
type = number
default = 30
}


variable "tags_all" {
description = "A mapping of tags to assign to the resource."
type = map(string)
default = {}
}
10 changes: 10 additions & 0 deletions aws/networking/vpc-flow-logs/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = ">=1.3"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">=4.0"
}
}
}
6 changes: 6 additions & 0 deletions aws/networking/vpc-network-acls/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
data "aws_vpc" "this" {
filter {
name = "vpc-id"
values = [var.vpc_id]
}
}
Loading

0 comments on commit 4af1c6a

Please sign in to comment.