This repository has been archived by the owner on Apr 18, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add module to run a Lambda function as a "cron" job (#21)
* Adding a terraform module to create a cron-triggered lambda function * Add environment variables * Fix?? default map value for env_vars * Fix default empty list for secrets * Add test case * Fix IAM copypasta * Reversed my references 🤦 * Make the list variables into lists
- Loading branch information
1 parent
68d3821
commit d062728
Showing
6 changed files
with
289 additions
and
0 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,23 @@ | ||
data "aws_s3_bucket" "releases" { | ||
bucket = "${var.domain_name}-${var.env}-lambda-releases" | ||
} | ||
|
||
data "aws_vpc" "vpc" { | ||
tags { | ||
env = "${var.env}" | ||
} | ||
} | ||
|
||
data "aws_subnet" "application_subnet" { | ||
count = 3 | ||
vpc_id = "${data.aws_vpc.vpc.id}" | ||
|
||
tags { | ||
name = "app-sub-${count.index}" | ||
env = "${var.env}" | ||
} | ||
} | ||
|
||
data "aws_kms_alias" "main" { | ||
name = "alias/${var.env}-main" | ||
} |
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,185 @@ | ||
locals { | ||
handler = "${coalesce(var.handler, var.job_name)}" | ||
} | ||
|
||
resource "aws_lambda_function" "job" { | ||
function_name = "${var.job_name}" | ||
description = "Terraform-managed cron job for ${var.job_name} that invokes ${local.handler}" | ||
|
||
s3_bucket = "${data.aws_s3_bucket.releases.id}" | ||
s3_key = "${var.job_name}.zip" | ||
|
||
handler = "${local.handler}" | ||
runtime = "${var.runtime}" | ||
|
||
// Allow to run up to 5 minutes. Max is 15 minutes | ||
timeout = 600 | ||
memory_size = "${var.memory_size}" | ||
|
||
role = "${aws_iam_role.job.arn}" | ||
|
||
environment { | ||
variables = "${var.env_vars}" | ||
} | ||
|
||
// Encrypts any environment variables | ||
kms_key_arn = "${data.aws_kms_alias.main.target_key_arn}" | ||
|
||
vpc_config { | ||
subnet_ids = ["${data.aws_subnet.application_subnet.*.id}"] | ||
security_group_ids = ["${aws_security_group.job.id}]"] | ||
} | ||
|
||
tags { | ||
terraform = "True" | ||
app = "${var.job_name}" | ||
handler = "${local.handler}" | ||
type = "cron" | ||
} | ||
} | ||
|
||
##### | ||
# Default security group for lambda function | ||
##### | ||
resource "aws_security_group" "job" { | ||
name_prefix = "${var.job_name}-" | ||
vpc_id = "${data.aws_vpc.vpc.id}" | ||
|
||
tags { | ||
env = "${var.env}" | ||
terraform = "True" | ||
app = "${var.job_name}" | ||
name = "cron-${var.job_name}" | ||
} | ||
} | ||
|
||
// Block all inbound | ||
resource "aws_security_group_rule" "ingress" { | ||
type = "ingress" | ||
from_port = 0 | ||
to_port = 0 | ||
protocol = -1 | ||
self = true | ||
|
||
security_group_id = "${aws_security_group.job.id}" | ||
} | ||
|
||
// Allow all outbound by default | ||
resource "aws_security_group_rule" "lb_egress" { | ||
type = "egress" | ||
from_port = 0 | ||
to_port = 0 | ||
protocol = -1 | ||
cidr_blocks = ["0.0.0.0/0"] | ||
|
||
security_group_id = "${aws_security_group.job.id}" | ||
} | ||
|
||
###### | ||
# Cron job setup | ||
###### | ||
resource "aws_cloudwatch_event_rule" "crontab" { | ||
name = "crontab-${var.env}-${var.job_name}" | ||
description = "Terraform-managed crontab for firing ${var.job_name}" | ||
schedule_expression = "cron(${var.cron_expression})" | ||
} | ||
|
||
resource "aws_cloudwatch_event_target" "crontab" { | ||
target_id = "propman_sync_lambda_target" | ||
rule = "${aws_cloudwatch_event_rule.crontab.name}" | ||
arn = "${aws_lambda_function.job.arn}" | ||
} | ||
|
||
resource "aws_lambda_permission" "allow_cloudwatch_to_call_crontab" { | ||
statement_id = "AllowExecutionFromCloudWatch" | ||
action = "lambda:InvokeFunction" | ||
function_name = "${aws_lambda_function.job.function_name}" | ||
principal = "events.amazonaws.com" | ||
source_arn = "${aws_cloudwatch_event_rule.crontab.arn}" | ||
} | ||
|
||
##### | ||
# IAM role which dictates what other AWS services the Lambda function | ||
# may access. | ||
##### | ||
|
||
resource "aws_iam_role" "job" { | ||
name = "cronjob-${var.env}-${var.job_name}" | ||
|
||
assume_role_policy = <<EOF | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Action": "sts:AssumeRole", | ||
"Principal": { | ||
"Service": "lambda.amazonaws.com" | ||
}, | ||
"Effect": "Allow", | ||
"Sid": "" | ||
} | ||
] | ||
} | ||
EOF | ||
} | ||
|
||
# Helper for allowing access to securely stored secrets | ||
# The helper uses the main shared key, provide your own by attaching to the output | ||
# role if you have a more restricted key used in Secrets Manager | ||
resource "aws_iam_role_policy_attachment" "basic_exec_role" { | ||
role = "${aws_iam_role.job.name}" | ||
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" | ||
} | ||
|
||
data "aws_iam_policy_document" "secrets" { | ||
statement { | ||
actions = [ | ||
"secretsmanager:GetSecretValue", | ||
] | ||
|
||
resources = "${var.secrets}" | ||
} | ||
} | ||
|
||
resource "aws_iam_policy" "secrets" { | ||
name = "${var.env}-${var.job_name}-secrets" | ||
path = "/${var.env}/${var.job_name}/" | ||
policy = "${data.aws_iam_policy_document.secrets.json}" | ||
} | ||
|
||
resource "aws_iam_role_policy_attachment" "secrets" { | ||
role = "${aws_iam_role.job.name}" | ||
policy_arn = "${aws_iam_policy.secrets.arn}" | ||
} | ||
|
||
# Use of the shared KMS key for secrets decryption | ||
resource "aws_iam_policy" "shared_key_access" { | ||
name = "${var.env}-${var.job_name}-key-access" | ||
path = "/${var.env}/${var.job_name}/" | ||
description = "Allows function to use main KMS key for environment" | ||
|
||
policy = <<EOF | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Action": "kms:Decrypt", | ||
"Resource": "${data.aws_kms_alias.main.target_key_arn}" | ||
} | ||
] | ||
} | ||
EOF | ||
} | ||
|
||
resource "aws_iam_role_policy_attachment" "shared_key_access" { | ||
role = "${aws_iam_role.job.name}" | ||
policy_arn = "${aws_iam_policy.shared_key_access.arn}" | ||
} | ||
|
||
resource "aws_kms_grant" "primary" { | ||
name = "${var.env}-cron-${var.job_name}" | ||
key_id = "${data.aws_kms_alias.main.target_key_arn}" | ||
grantee_principal = "${aws_iam_role.job.arn}" | ||
operations = ["Decrypt"] | ||
} |
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,4 @@ | ||
output "job_iam_role" { | ||
description = "IAM role name for attaching additional policies to the lambda function with aws_iam_role_policy_attachment" | ||
value = "${aws_iam_role.job.name}" | ||
} |
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,42 @@ | ||
variable "env" { | ||
description = "the name of the environment, e.g. \"testing\". it must be unique in the account." | ||
} | ||
|
||
variable "domain_name" { | ||
description = "the external domain name for reaching the public resources. must have a certificate in ACM associated with it." | ||
} | ||
|
||
variable "job_name" { | ||
description = "name of the job to be run. must be unique in the environment." | ||
} | ||
|
||
variable "cron_expression" { | ||
description = "cron schedule expression. For example, \"0 12 * * ? *\" for daily at noon UTC. https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressions" | ||
} | ||
|
||
variable "runtime" { | ||
description = "OPTIONAL: which lambda runtime to use to invoke the function. Defaults to Go 1.x" | ||
default = "go1.x" | ||
} | ||
|
||
variable "handler" { | ||
description = "OPTIONAL: name of the handler for the lambda. Defaults to job_name" | ||
default = "" | ||
} | ||
|
||
variable "memory_size" { | ||
description = "OPTIONAL: memory to allocate to the lambda function. Defaults to 1024mb" | ||
default = "1024" | ||
} | ||
|
||
variable "env_vars" { | ||
type = "map" | ||
description = "OPTIONAL: a map of environment variables to set for the job" | ||
default = {} | ||
} | ||
|
||
variable "secrets" { | ||
type = "list" | ||
description = "OPTIONAL: a list of ARNs for Secret Manager secrets to allow job to access" | ||
default = [] | ||
} |
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
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