Skip to content

Commit

Permalink
enha: added option to create flow log for the VPC
Browse files Browse the repository at this point in the history
  • Loading branch information
kfc-manager committed Feb 16, 2024
1 parent f26e283 commit b920df8
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This module provides a VPC with variable public and private subnets in it. The t
| public_subnets | A list of CIDR blocks for the public subnets inside the VPC. | `list(string)` | [] | no |
| private_subnets | A list of CIDR blocks for the private subnets inside the VPC. | `list(string)` | [] | no |
| nat_gw | A flag for wether or not creating a NAT Gateway in the first public subnet in order to route the private subnets traffic through it. | `bool` | false | no |
| flow_logs | An object for the definition for a flow log of the VPC. | `object` | null | no |
| tags | A map of tags to add to all resources. Name is always set as tag and the other tags will be appended. | `map(string)` | {} | no |

## Outputs
Expand All @@ -45,6 +46,11 @@ module "network" {
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
private_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
nat_gw = true
flow_log = {
name_prefix = "example-network-dev"
traffic_type = "ALL"
retention_in_days = 7
}
tags = {
Project = "example-project"
Expand Down
78 changes: 78 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,81 @@ resource "aws_main_route_table_association" "main" {
vpc_id = aws_vpc.main.id
route_table_id = aws_route_table.private[0].id
}

################################
# Flow Log #
################################

data "aws_iam_policy_document" "assume_role" {
count = var.flow_log != null ? 1 : 0

statement {
effect = "Allow"

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

actions = ["sts:AssumeRole"]
}
}

resource "aws_cloudwatch_log_group" "main" {
count = var.flow_log != null ? 1 : 0
name = "${try(var.flow_log["name_prefix"], "")}-flow-log"
retention_in_days = try(var.flow_log["retention_in_days"], 1)

tags = merge(
{ "Name" = var.name },
var.tags
)
}

data "aws_iam_policy_document" "log" {
count = var.flow_log != null ? 1 : 0

statement {
effect = "Allow"

actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
]

resources = ["${aws_cloudwatch_log_group.main[0].arn}:*"]
}
}

resource "aws_iam_role" "main" {
count = var.flow_log != null ? 1 : 0
name = "${try(var.flow_log["name_prefix"], "")}-ServiceRoleForFlowLog"
assume_role_policy = data.aws_iam_policy_document.assume_role[0].json

inline_policy {
name = "${try(var.flow_log["name_prefix"], "")}-CloudWatchCreateLog"
policy = data.aws_iam_policy_document.log[0].json
}

tags = merge(
{ "Name" = var.name },
var.tags
)
}

resource "aws_flow_log" "main" {
count = var.flow_log != null ? 1 : 0
vpc_id = aws_vpc.main.id
traffic_type = try(var.flow_log["traffic_type"], "ALL")
iam_role_arn = aws_iam_role.main[0].arn
log_destination = aws_cloudwatch_log_group.main[0].arn
max_aggregation_interval = 60

tags = merge(
{ "Name" = var.name },
var.tags
)
}
100 changes: 100 additions & 0 deletions tests/flow_log.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
provider "aws" {
region = "eu-central-1"
default_tags {
tags = {
Environment = "Test"
}
}
}

run "invalid_name_prefix" {
command = plan

variables {
flow_log = {
name_prefix = "a"
traffic_type = "ALL"
retention_in_days = 1
}
}

expect_failures = [var.flow_log]
}

run "invalid_traffic_type" {
command = plan

variables {
flow_log = {
name_prefix = "abc"
traffic_type = "FOO"
retention_in_days = 1
}
}

expect_failures = [var.flow_log]
}

run "invalid_retention_in_days" {
command = plan

variables {
flow_log = {
name_prefix = "abc"
traffic_type = "ALL"
retention_in_days = 2
}
}

expect_failures = [var.flow_log]
}

run "valid_flow_log" {
command = plan

variables {
flow_log = {
name_prefix = "abc"
traffic_type = "ALL"
retention_in_days = 1
}
}

assert {
condition = length(aws_flow_log.main) == 1
error_message = "Flow log was not created"
}

assert {
condition = length(aws_iam_role.main) == 1
error_message = "IAM role for flow log was not created"
}

assert {
condition = length(aws_cloudwatch_log_group.main) == 1
error_message = "CloudWatch log group for flow log was not created"
}
}

run "no_flow_log" {
command = plan

variables {
flow_log = null
}

assert {
condition = length(aws_flow_log.main) == 0
error_message = "Flow log was created unexpectedly"
}

assert {
condition = length(aws_iam_role.main) == 0
error_message = "IAM role for flow log was created unexpectedly"
}

assert {
condition = length(aws_cloudwatch_log_group.main) == 0
error_message = "CloudWatch log group for flow log was created unexpectedly"
}
}
31 changes: 31 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,37 @@ variable "nat_gw" {
default = false
}

variable "flow_log" {
description = "An object for the definition for a flow log of the VPC"
type = object({
name_prefix = string
traffic_type = string
retention_in_days = number
})
default = null
validation {
condition = length(try(var.flow_log["name_prefix"], "abc")) > 2
error_message = "Name prefix must be at least 3 characters"
}
validation {
condition = try(var.flow_log["traffic_type"], "ALL") == "ALL" || (
try(var.flow_log["traffic_type"], "ACCEPT") == "ACCEPT") || (
try(var.flow_log["traffic_type"], "REJECT") == "REJECT")
error_message = "Traffic type must be 'ALL', 'ACCEPT' or 'REJECT'"
}
validation {
condition = try(var.flow_log["retention_in_days"], 1) == 1 || (
try(var.flow_log["retention_in_days"], 3) == 3) || (
try(var.flow_log["retention_in_days"], 5) == 5) || (
try(var.flow_log["retention_in_days"], 7) == 7) || (
try(var.flow_log["retention_in_days"], 14) == 14) || (
try(var.flow_log["retention_in_days"], 30) == 30) || (
try(var.flow_log["retention_in_days"], 365) == 365) || (
try(var.flow_log["retention_in_days"], 0) == 0)
error_message = "Retention in days must be one of these values: 0, 1, 3, 5, 7, 14, 30, 365"
}
}

variable "tags" {
description = "A map of tags to add to all resources"
type = map(string)
Expand Down

0 comments on commit b920df8

Please sign in to comment.