Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement module for VPC flow logs #1

Merged
merged 17 commits into from
Nov 21, 2017
16 changes: 16 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
addons:
apt:
packages:
- git
- make
- curl

install:
- make init

script:
- make terraform:install
- make terraform:get-plugins
- make terraform:get-modules
- make terraform:lint
- make terraform:validate
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright {yyyy} {name of copyright owner}
Copyright 2017 Cloud Posse, LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SHELL := /bin/bash

-include $(shell curl -sSL -o .build-harness "https://git.io/build-harness"; echo .build-harness)

lint:
$(SELF) terraform:install terraform:get-modules terraform:get-plugins terraform:lint terraform:validate
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# terraform-aws-cloudwatch-flow-logs [![Build Status](https://travis-ci.org/cloudposse/terraform-aws-cloudwatch-flow-logs.svg)](https://travis-ci.org/cloudposse/terraform-aws-cloudwatch-flow-logs)

Terraform module for enabling [`flow logs`](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/flow-logs.html) for `vpc` and `subnets`.

## Usage

```terraform
module "flow_logs" {
source = "git::https://github.com/cloudposse/terraform-aws-cloudwatch-flow-logs.git?ref=master"
vpc_id = "${var.vpc_id}"
namespace = "${var.namespace}"
stage = "${var.stage}"
}
```
## Inputs

| Name | Default | Description | Required |
|:----------------------|:--------------------------------------:|:--------------------------------------------------------------------------------------------------------|:--------:|
| `namespace` | `` | Namespace (e.g. `cp` or `cloudposse`) | Yes |
| `stage` | `` | Stage (e.g. `prod`, `dev`, `staging`) | Yes |
| `name` | `` | Name (e.g. `bastion` or `db`) | No |
| `delimiter` | `-` | Delimiter to be used between `name`, `namespace`, `stage`, etc. | No |
| `attributes` | `[]` | Additional attributes (e.g. `policy` or `role`) | No |
| `tags` | `{}` | Additional tags (e.g. `map("BusinessUnit","XYZ")` | No |
| `vpc_id` | `` | ID of VPC | Yes |
| `subnet_ids` | `[]` | IDs of subnets | Yes |
| `eni_ids` | `[]` | IDs of ENIs | Yes |
| `region` | `` | AWS region e.g. `us-central-1` | No |
| `retention_in_days` | `30` | Number of days you want to retain log events in the log group | No |
| `traffic_type` | `ALL` | Type of traffic to capture. Valid values: ACCEPT,REJECT, ALL | No |
| `shard_count` | `1` | Number of shards that the stream will use | No |
| `retention_period` | `48` | Length of time data records are accessible after they are added to the stream | No |
| `shard_level_metrics` | `[ "IncomingBytes", "OutgoingBytes",]` | List of shard-level CloudWatch metrics which can be enabled for the stream | No |
| `encryption_type` | `NONE` | GUID for the customer-managed KMS key to use for encryption. The only acceptable values are NONE or KMS | No |
| `filter_pattern` | `"[]"` | Valid CloudWatch Logs filter pattern for subscribing to a filtered stream of log events | No |




## Outputs

| Name | Description |
|:---------------------------|:-------------------------------------|
| `log_group_arn` | ARN of the log group |
| `eni_flow_ids` | Flow Log IDs of ENIs |
| `subnet_flow_ids` | Flow Log IDs of subnets |
| `vpc_flow_id` | Flow Log IDs of VPC |
| `kinesis_arn` | ARN of Stream |
| `kinesis_id` | Stream ID |
| `kinesis_name` | Stream name |
| `kinesis_shard_count` | Count of Shards for Stream |
| `subscription_filter_arns` | ARNs of the log subscription filters |

## License

Apache 2 License. See [`LICENSE`](LICENSE) for full details.
73 changes: 73 additions & 0 deletions iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
data "aws_iam_policy_document" "log_assume" {
statement {
actions = ["sts:AssumeRole"]

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

data "aws_iam_policy_document" "log" {
statement {
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
]

resources = [
"*",
]
}
}

resource "aws_iam_role_policy" "log" {
name = "${module.vpc_label.id}"
role = "${aws_iam_role.log.id}"
policy = "${data.aws_iam_policy_document.log.json}"
}

resource "aws_iam_role" "log" {
name = "${module.vpc_label.id}"
assume_role_policy = "${data.aws_iam_policy_document.log_assume.json}"
}

data "aws_iam_policy_document" "kinesis_assume" {
statement {
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["logs.${length(var.region) > 0 ? var.region: data.aws_region.default.name}.amazonaws.com"]
}
}
}

data "aws_iam_policy_document" "kinesis" {
statement {
actions = [
"kinesis:PutRecord*",
"kinesis:DescribeStream",
"kinesis:ListStreams",
]

resources = [
"${aws_kinesis_stream.default.arn}",
]
}
}

resource "aws_iam_role" "kinesis" {
name = "${module.kinesis_label.id}"
assume_role_policy = "${data.aws_iam_policy_document.kinesis_assume.json}"
}

resource "aws_iam_role_policy" "kinesis" {
name = "${module.vpc_label.id}"
role = "${aws_iam_role.kinesis.id}"
policy = "${data.aws_iam_policy_document.kinesis.json}"
}
27 changes: 27 additions & 0 deletions kinesis.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module "kinesis_label" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.3.1"
namespace = "${var.namespace}"
name = "${var.name}"
stage = "${var.stage}"
delimiter = "${var.delimiter}"
attributes = "${compact(concat(var.attributes, list("kinesis")))}"
tags = "${var.tags}"
}

resource "aws_kinesis_stream" "default" {
name = "${module.kinesis_label.id}"
shard_count = "${var.shard_count}"
retention_period = "${var.retention_period}"
shard_level_metrics = "${var.shard_level_metrics}"
encryption_type = "${var.encryption_type}"
kms_key_id = "${var.encryption_type}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a copy and paste error?

tags = "${module.kinesis_label.tags}"
}

resource "aws_cloudwatch_log_subscription_filter" "default" {
name = "${aws_cloudwatch_log_group.default.name}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aws_cloudwatch_log_group name (/aws/lambda/example_lambda_name) may be improper for resource name.
https://www.terraform.io/docs/providers/aws/r/cloudwatch_log_subscription_filter.html

We should use terraform-null-label for resource names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aws_cloudwatch_log_group name (/aws/lambda/example_lambda_name) may be improper for resource name.

it doesn't matter

log_group_name = "${aws_cloudwatch_log_group.default.name}"
filter_pattern = "${var.filter_pattern}"
destination_arn = "${aws_kinesis_stream.default.arn}"
role_arn = "${aws_iam_role.kinesis.arn}"
}
62 changes: 62 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
data "aws_region" "default" {
current = "true"
}

module "log_group_label" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.2.2"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use 0.3.1 label module.

namespace = "${var.namespace}"
name = "${var.name}"
stage = "${var.stage}"
delimiter = "${var.delimiter}"
attributes = "${compact(concat(var.attributes, list("log")))}"
tags = "${var.tags}"
}

module "vpc_label" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.2.2"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use 0.3.1 label module.

namespace = "${var.namespace}"
name = "${var.name}"
stage = "${var.stage}"
delimiter = "${var.delimiter}"
attributes = "${compact(concat(var.attributes, list("vpc")))}"
tags = "${var.tags}"
}

module "subnet_label" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.2.2"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use 0.3.1 label module.

namespace = "${var.namespace}"
name = "${var.name}"
stage = "${var.stage}"
delimiter = "${var.delimiter}"
attributes = "${compact(concat(var.attributes, list("subnets")))}"
tags = "${var.tags}"
}

resource "aws_cloudwatch_log_group" "default" {
name = "${module.log_group_label.id}"
retention_in_days = "${var.retention_in_days}"
tags = "${module.log_group_label.tags}"
}

resource "aws_flow_log" "vpc" {
log_group_name = "${aws_cloudwatch_log_group.default.name}"
iam_role_arn = "${aws_iam_role.log.arn}"
vpc_id = "${var.vpc_id}"
traffic_type = "${var.traffic_type}"
}

resource "aws_flow_log" "subnets" {
count = "${length(compact(var.subnet_ids))}"
log_group_name = "${aws_cloudwatch_log_group.default.name}"
iam_role_arn = "${aws_iam_role.log.arn}"
subnet_id = "${element(var.subnet_ids, count.index)}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use compact here too for consistence.
Otherwise, empty elements will be enumerated.

traffic_type = "${var.traffic_type}"
}

resource "aws_flow_log" "eni" {
count = "${length(compact(var.eni_ids))}"
log_group_name = "${aws_cloudwatch_log_group.default.name}"
iam_role_arn = "${aws_iam_role.log.arn}"
subnet_id = "${element(var.eni_ids, count.index)}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use compact here too for consistence.
Otherwise, empty elements will be enumerated.

traffic_type = "${var.traffic_type}"
}
44 changes: 44 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
output "log_group_arn" {
value = "${aws_cloudwatch_log_group.default.arn}"
description = "ARN of the log group"
}

output "vpc_flow_id" {
value = "${aws_flow_log.vpc.id}"
description = "Flow Log IDs of VPCs"
}

output "subnet_flow_ids" {
value = "${aws_flow_log.subnets.*.id}"
description = "Flow Log IDs of subnets"
}

output "eni_flow_ids" {
value = "${aws_flow_log.eni.*.id}"
description = "Flow Log IDs of ENIs"
}

output "kinesis_id" {
value = "${aws_kinesis_stream.default.id}"
description = "Stream ID"
}

output "kinesis_name" {
value = "${aws_kinesis_stream.default.name}"
description = "Stream name"
}

output "kinesis_shard_count" {
value = "${aws_kinesis_stream.default.shard_count}"
description = "Count of Shards for Stream"
}

output "kinesis_arn" {
value = "${aws_kinesis_stream.default.arn}"
description = "ARN of Stream"
}

output "subscription_filter_arns" {
value = "${aws_cloudwatch_log_subscription_filter.default.arn}"
description = "ARNs of the log subscription filters"
}
92 changes: 92 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
variable "name" {
default = ""
description = "Name (e.g. `bastion` or `db`)"
}

variable "namespace" {
description = "Namespace (e.g. `cp` or `cloudposse`)"
type = "string"
}

variable "stage" {
description = "Stage (e.g. `prod`, `dev`, `staging`)"
type = "string"
}

variable "delimiter" {
type = "string"
default = "-"
description = "Delimiter to be used between `name`, `namespace`, `stage`, etc."
}

variable "attributes" {
type = "list"
default = []
description = "Additional attributes (e.g. `policy` or `role`)"
}

variable "tags" {
type = "map"
default = {}
description = "Additional tags (e.g. map(`BusinessUnit`,`XYZ`)"
}

variable "region" {
description = "AWS region"
default = ""
}

variable "retention_in_days" {
description = "Number of days you want to retain log events in the log group"
default = "30"
}

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

variable "vpc_id" {
description = "ID of VPC"
}

variable "subnet_ids" {
description = "IDs of subnets"
type = "list"
default = []
}

variable "eni_ids" {
description = "IDs of ENIs"
type = "list"
default = []
}

variable "shard_count" {
description = "Number of shards that the stream will use"
default = "1"
}

variable "retention_period" {
description = "Length of time data records are accessible after they are added to the stream"
default = "48"
}

variable "shard_level_metrics" {
description = "List of shard-level CloudWatch metrics which can be enabled for the stream"

default = [
"IncomingBytes",
"OutgoingBytes",
]
}

variable "encryption_type" {
description = "GUID for the customer-managed KMS key to use for encryption. The only acceptable values are NONE or KMS"
default = "NONE"
}

variable "filter_pattern" {
description = "Valid CloudWatch Logs filter pattern for subscribing to a filtered stream of log events"
default = "[version, account, eni, source, destination, srcport, destport, protocol, packets, bytes, windowstart, windowend, action, flowlogstatus]"
}