-
Notifications
You must be signed in to change notification settings - Fork 334
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create re-usable Lambda Terraform module (#596)
- Loading branch information
1 parent
ca9c511
commit fc2ed12
Showing
9 changed files
with
422 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Lambda Module | ||
This Terraform module creates a single AWS Lambda function and its related components: | ||
|
||
* IAM execution role with basic permissions | ||
* Lambda function | ||
* Production alias | ||
* CloudWatch log group | ||
* CloudWatch metric alarms related to Lambda | ||
|
||
All StreamAlert Lambda functions will eventually leverage this module. | ||
|
||
The created IAM role has permission to publish CloudWatch logs and metrics. To add function-specific | ||
permissions, attach/inline them to the created IAM role. | ||
|
||
## Example | ||
```hcl | ||
module "alert_processor" { | ||
function_name = "alert_processor" | ||
handler = "stream_alert.alert_processor.main.handler" | ||
source_bucket = "SOURCE_BUCKET" | ||
source_object_key = "SOURCE_OBJECT_KEY" | ||
environment_variables = { | ||
LOGGER_LEVEL = "info" | ||
} | ||
// Commonly used optional variables | ||
enabled = true | ||
description = "Function Description" | ||
memory_size_mb = 128 | ||
timeout_sec = 60 | ||
vpc_subnet_ids = ["abc"] | ||
vpc_security_group_ids = ["id0"] | ||
aliased_version = 1 | ||
log_retention_days = 14 | ||
alarm_actions = ["SNS_ARN"] | ||
errors_alarm_threshold = 1 | ||
enable_iterator_age_alarm = true | ||
} | ||
// Add additional permissions | ||
resource "aws_iam_role_policy" "policy" { | ||
name = "CustomPolicy" | ||
role = "${module.alert_processor.role_id}" | ||
policy = "${data.aws_iam_policy_document.policy.json}" | ||
} | ||
data "aws_iam_policy_document" "policy" { | ||
statement { | ||
effect = "Allow" | ||
actions = ["s3:PutObject"] | ||
resources = ["arn:aws:s3:::..."] | ||
} | ||
} | ||
``` | ||
|
||
For a complete list of available options and their descriptions, see [`variables.tf`](variables.tf). | ||
|
||
## Outputs | ||
If your Lambda function is in a VPC, `function_vpc_arn` is the ARN of the generated Lambda | ||
function. Otherwise, it will be `function_no_vpc_arn`. (This split is a workaround for a | ||
[Terraform bug](https://github.com/terraform-providers/terraform-provider-aws/issues/443)). | ||
|
||
This module also exports the `role_arn` and `role_id` for the Lambda execution role. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
data "aws_iam_policy_document" "lambda_execution_policy" { | ||
statement { | ||
effect = "Allow" | ||
actions = ["sts:AssumeRole"] | ||
|
||
principals { | ||
type = "Service" | ||
identifiers = ["lambda.amazonaws.com"] | ||
} | ||
} | ||
} | ||
|
||
// Create the execution role for the Lambda function. | ||
resource "aws_iam_role" "role" { | ||
count = "${var.enabled}" | ||
name = "${var.function_name}_role" | ||
assume_role_policy = "${data.aws_iam_policy_document.lambda_execution_policy.json}" | ||
} | ||
|
||
// Base permissions - Allow creating logs and publishing metrics | ||
data "aws_iam_policy_document" "logs_metrics_policy" { | ||
statement { | ||
effect = "Allow" | ||
|
||
actions = [ | ||
"cloudwatch:PutMetricData", | ||
"logs:CreateLogGroup", | ||
"logs:CreateLogStream", | ||
"logs:PutLogEvents", | ||
] | ||
|
||
resources = ["*"] | ||
} | ||
} | ||
|
||
resource "aws_iam_role_policy" "logs_metrics_policy" { | ||
count = "${var.enabled}" | ||
name = "LogsAndMetrics" | ||
role = "${aws_iam_role.role.id}" | ||
policy = "${data.aws_iam_policy_document.logs_metrics_policy.json}" | ||
} | ||
|
||
// Attach VPC policy (if applicable) | ||
resource "aws_iam_role_policy_attachment" "vpc_access" { | ||
count = "${var.enabled && local.vpc_enabled ? 1 : 0}" | ||
role = "${aws_iam_role.role.id}" | ||
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Generic module for any StreamAlert Lambda function. | ||
// TODO - migrate all Lambda functions and Lambda metric alarms to use this module | ||
|
||
locals { | ||
vpc_enabled = "${length(var.vpc_subnet_ids) > 0}" | ||
} | ||
|
||
// Either the function_vpc or the function_no_vpc resource will be used | ||
resource "aws_lambda_function" "function_vpc" { | ||
count = "${var.enabled && local.vpc_enabled ? 1 : 0}" | ||
function_name = "${var.function_name}" | ||
description = "${var.description}" | ||
runtime = "${var.runtime}" | ||
role = "${aws_iam_role.role.arn}" | ||
handler = "${var.handler}" | ||
memory_size = "${var.memory_size_mb}" | ||
publish = "${var.auto_publish_versions}" | ||
timeout = "${var.timeout_sec}" | ||
s3_bucket = "${var.source_bucket}" | ||
s3_key = "${var.source_object_key}" | ||
|
||
environment { | ||
variables = "${var.environment_variables}" | ||
} | ||
|
||
// Empty vpc_config lists are theoretically supported, but it actually breaks subsequent deploys: | ||
// https://github.com/terraform-providers/terraform-provider-aws/issues/443 | ||
vpc_config { | ||
security_group_ids = "${var.vpc_security_group_ids}" | ||
subnet_ids = "${var.vpc_subnet_ids}" | ||
} | ||
|
||
tags { | ||
Name = "${var.name_tag}" | ||
} | ||
|
||
// We need VPC access before the function can be created | ||
depends_on = ["aws_iam_role_policy_attachment.vpc_access"] | ||
} | ||
|
||
resource "aws_lambda_alias" "production_alias_vpc" { | ||
count = "${var.enabled && local.vpc_enabled ? 1 : 0}" | ||
name = "production" | ||
description = "Production alias for ${var.function_name}" | ||
function_name = "${var.function_name}" | ||
function_version = "${var.aliased_version == "" ? aws_lambda_function.function_vpc.version : var.aliased_version}" | ||
depends_on = ["aws_lambda_function.function_vpc"] | ||
} | ||
|
||
resource "aws_lambda_function" "function_no_vpc" { | ||
count = "${var.enabled && !(local.vpc_enabled) ? 1 : 0}" | ||
function_name = "${var.function_name}" | ||
description = "${var.description}" | ||
runtime = "${var.runtime}" | ||
role = "${aws_iam_role.role.arn}" | ||
handler = "${var.handler}" | ||
memory_size = "${var.memory_size_mb}" | ||
publish = "${var.auto_publish_versions}" | ||
timeout = "${var.timeout_sec}" | ||
s3_bucket = "${var.source_bucket}" | ||
s3_key = "${var.source_object_key}" | ||
|
||
environment { | ||
variables = "${var.environment_variables}" | ||
} | ||
|
||
tags { | ||
Name = "${var.name_tag}" | ||
} | ||
} | ||
|
||
resource "aws_lambda_alias" "production_alias_no_vpc" { | ||
count = "${var.enabled && !(local.vpc_enabled) ? 1 : 0}" | ||
name = "production" | ||
description = "Production alias for ${var.function_name}" | ||
function_name = "${var.function_name}" | ||
function_version = "${var.aliased_version == "" ? aws_lambda_function.function_no_vpc.version : var.aliased_version}" | ||
depends_on = ["aws_lambda_function.function_no_vpc"] | ||
} | ||
|
||
resource "aws_cloudwatch_log_group" "lambda_log_group" { | ||
count = "${var.enabled}" | ||
name = "/aws/lambda/${var.function_name}" | ||
retention_in_days = "${var.log_retention_days}" | ||
|
||
tags { | ||
Name = "${var.name_tag}" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
resource "aws_cloudwatch_metric_alarm" "lambda_invocation_errors" { | ||
count = "${var.enabled && var.enable_metric_alarms ? 1 : 0}" | ||
alarm_name = "${var.function_name}_invocation_errors" | ||
namespace = "AWS/Lambda" | ||
metric_name = "Errors" | ||
statistic = "Sum" | ||
comparison_operator = "GreaterThanThreshold" | ||
threshold = "${var.errors_alarm_threshold}" | ||
evaluation_periods = "${var.errors_alarm_evaluation_periods}" | ||
period = "${var.errors_alarm_period_secs}" | ||
alarm_description = "StreamAlert Lambda Invocation Errors: ${var.function_name}" | ||
alarm_actions = "${var.alarm_actions}" | ||
|
||
dimensions { | ||
FunctionName = "${var.function_name}" | ||
Resource = "${var.function_name}:production" | ||
} | ||
} | ||
|
||
resource "aws_cloudwatch_metric_alarm" "lambda_throttles" { | ||
count = "${var.enabled && var.enable_metric_alarms ? 1 : 0}" | ||
alarm_name = "${var.function_name}_throttles" | ||
namespace = "AWS/Lambda" | ||
metric_name = "Throttles" | ||
statistic = "Sum" | ||
comparison_operator = "GreaterThanThreshold" | ||
threshold = "${var.throttles_alarm_threshold}" | ||
evaluation_periods = "${var.throttles_alarm_evaluation_periods}" | ||
period = "${var.throttles_alarm_period_secs}" | ||
alarm_description = "StreamAlert Lambda Throttles: ${var.function_name}" | ||
alarm_actions = "${var.alarm_actions}" | ||
|
||
dimensions { | ||
FunctionName = "${var.function_name}" | ||
Resource = "${var.function_name}:production" | ||
} | ||
} | ||
|
||
resource "aws_cloudwatch_metric_alarm" "streamalert_lambda_iterator_age" { | ||
count = "${var.enabled && var.enable_metric_alarms && var.enable_iterator_age_alarm ? 1 : 0}" | ||
alarm_name = "${var.function_name}_iterator_age" | ||
namespace = "AWS/Lambda" | ||
metric_name = "IteratorAge" | ||
statistic = "Maximum" | ||
comparison_operator = "GreaterThanThreshold" | ||
threshold = "${var.iterator_age_alarm_threshold_ms}" | ||
evaluation_periods = "${var.iterator_age_alarm_evaluation_periods}" | ||
period = "${var.iterator_age_alarm_period_secs}" | ||
alarm_description = "StreamAlert Lambda High Iterator Age: ${var.function_name}" | ||
alarm_actions = "${var.alarm_actions}" | ||
|
||
dimensions { | ||
FunctionName = "${var.function_name}" | ||
Resource = "${var.function_name}:production" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Defined only if the Lambda is in a VPC | ||
output "function_vpc_arn" { | ||
value = "${aws_lambda_function.function_vpc.arn}" | ||
} | ||
|
||
// Defined only if the Lambda is NOT in a VPC | ||
output "function_no_vpc_arn" { | ||
value = "${aws_lambda_function.function_no_vpc.arn}" | ||
} | ||
|
||
output "role_arn" { | ||
value = "${aws_iam_role.role.arn}" | ||
} | ||
|
||
output "role_id" { | ||
value = "${aws_iam_role.role.id}" | ||
} |
Oops, something went wrong.