Skip to content

Commit

Permalink
feat: package the terraform module
Browse files Browse the repository at this point in the history
  • Loading branch information
Julen Dixneuf committed May 5, 2021
1 parent 0dd66e7 commit 3b8e732
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 57 deletions.
34 changes: 34 additions & 0 deletions examples/asg/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,38 @@ resource "aws_autoscaling_group" "asg_bis" {

module "aws_start_stop_scheduler" {
source = "../.."

name = "test_asg"

schedules = [{
tag = {
key = "start_stop_scheduler_group",
value = "test_asg_2"
},
starts = {
every_20_min = "*/20 * * * ? *",
every_hour = "0 * * * ? *"
},
stops = {
every_20_odd_min = "*/20 * * * ? *",
every_mid_hour = "30 * * * ? *"
},
},
{
tag = {
key = "start_stop_scheduler_group",
value = "test_asg_1"
},
starts = {
every_hour = "0 * * * ? *"
},
stops = {
every_mid_hour = "30 * * * ? *"
},
}
]

tags = {
Green = "IT"
}
}
3 changes: 3 additions & 0 deletions examples/asg/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "scheduler_module_output" {
value = module.aws_start_stop_scheduler
}
119 changes: 82 additions & 37 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
data "aws_region" "current" {}

locals {
name_prefix = "${var.name}_start_stop_scheduler"
}

data "aws_iam_policy_document" "lambda_assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
Expand All @@ -10,8 +16,10 @@ data "aws_iam_policy_document" "lambda_assume_role_policy" {
}

resource "aws_iam_role" "lambda" {
name = "start_stop_scheduler_lambda"
name_prefix = local.name_prefix
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role_policy.json

tags = var.tags
}


Expand Down Expand Up @@ -41,9 +49,9 @@ data "aws_iam_policy_document" "lambda_autoscalinggroup" {
}

resource "aws_iam_role_policy" "lambda_autoscalinggroup" {
name = "start_stop_scheduler_autoscaling_policy"
role = aws_iam_role.lambda.id
policy = data.aws_iam_policy_document.lambda_autoscalinggroup.json
name_prefix = "${local.name_prefix}_autoscaling"
role = aws_iam_role.lambda.id
policy = data.aws_iam_policy_document.lambda_autoscalinggroup.json
}


Expand All @@ -53,74 +61,111 @@ data "archive_file" "lambda_zip" {
output_path = "${path.module}/lambda_function.zip"
}

resource "aws_cloudwatch_log_group" "start_stop_scheduler" {
name = "/aws/lambda/${aws_lambda_function.start_stop_scheduler.function_name}"
retention_in_days = 14
tags = var.tags
}

resource "aws_lambda_function" "start_stop_scheduler" {
filename = data.archive_file.lambda_zip.output_path
function_name = "start_stop_scheduler"
function_name = local.name_prefix
role = aws_iam_role.lambda.arn
handler = "scheduler.main.lambda_handler"
timeout = 30
timeout = var.lambda_timeout

source_code_hash = filebase64sha256(data.archive_file.lambda_zip.output_path)

runtime = "python3.8"

environment {
variables = {
FOO = "bar"
AWS_REGIONS = var.aws_regions == null ? data.aws_region.current.name : join(", ", var.aws_regions)
RDS_SCHEDULE = tostring(var.rds_schedule)
ASG_SCHEDULE = tostring(var.asg_schedule)
}
}

tags = var.tags
}

locals {
flatten_starts = { for index, v in flatten([for sched in var.schedules : [
for key, value in sched.starts : {
tag = sched.tag,
start = { cron = value, description = key },
}
]]) : index => v }

flatten_stops = { for index, v in flatten([for sched in var.schedules : [
for key, value in sched.stops : {
tag = sched.tag,
stop = { cron = value, description = key },
}
]]) : index => v }
}

resource "aws_cloudwatch_event_rule" "start" {
name_prefix = "start_stop_scheduler"
schedule_expression = "cron(*/20 * * * ? *)"
for_each = local.flatten_starts

name_prefix = "${local.name_prefix}_start"
schedule_expression = "cron(${each.value.start.cron})"
description = "Start ressources with tag ${each.value.tag.key}=${each.value.tag.value} with cron '${each.value.start.description}'"
tags = var.tags
}

resource "aws_cloudwatch_event_target" "start" {
rule = aws_cloudwatch_event_rule.start.id
arn = aws_lambda_function.start_stop_scheduler.arn
input = <<EOF
{
"action": "start",
"tag": {
"key": "start_stop_scheduler_group",
"value": "test_asg_2"
}
}
EOF
for_each = local.flatten_starts

rule = aws_cloudwatch_event_rule.start[each.key].id
arn = aws_lambda_function.start_stop_scheduler.arn
input = jsonencode({
"action" : "start",
"tag" : {
"key" : each.value.tag.key,
"value" : each.value.tag.value,
}
})
}

resource "aws_lambda_permission" "allow_cloudwatch_start" {
for_each = local.flatten_starts

action = "lambda:InvokeFunction"
function_name = aws_lambda_function.start_stop_scheduler.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.start.arn
# statement_id_prefix = "value"
source_arn = aws_cloudwatch_event_rule.start[each.key].arn
}


resource "aws_cloudwatch_event_rule" "stop" {
name_prefix = "start_stop_scheduler"
schedule_expression = "cron(10/20 * * * ? *)"
for_each = local.flatten_stops

name_prefix = "${local.name_prefix}_stop"
schedule_expression = "cron(${each.value.stop.cron})"
description = "Stop ressources with tag ${each.value.tag.key}=${each.value.tag.value} with cron '${each.value.stop.description}'"
tags = var.tags
}

resource "aws_cloudwatch_event_target" "stop" {
rule = aws_cloudwatch_event_rule.stop.id
arn = aws_lambda_function.start_stop_scheduler.arn
input = <<EOF
{
"action": "stop",
"tag": {
"key": "start_stop_scheduler_group",
"value": "test_asg_2"
}
}
EOF
for_each = local.flatten_stops

rule = aws_cloudwatch_event_rule.stop[each.key].id
arn = aws_lambda_function.start_stop_scheduler.arn
input = jsonencode({
"action" : "stop",
"tag" : {
"key" : each.value.tag.key,
"value" : each.value.tag.value,
}
})
}

resource "aws_lambda_permission" "allow_cloudwatch_stop" {
for_each = local.flatten_stops

action = "lambda:InvokeFunction"
function_name = aws_lambda_function.start_stop_scheduler.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.stop.arn
# statement_id_prefix = "value"
source_arn = aws_cloudwatch_event_rule.stop[each.key].arn
}
52 changes: 52 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
output "lambda_iam_role_arn" {
description = "The ARN of the IAM role used by Lambda function"
value = aws_iam_role.lambda.arn
}

output "lambda_iam_role_name" {
description = "The name of the IAM role used by Lambda function"
value = aws_iam_role.lambda.name
}

output "lambda_function_arn" {
description = "The ARN of the Lambda function"
value = aws_lambda_function.start_stop_scheduler.arn
}

output "lambda_function_name" {
description = "The name of the Lambda function"
value = aws_lambda_function.start_stop_scheduler.function_name
}

output "lambda_function_invoke_arn" {
description = "The ARN to be used for invoking Lambda function from API Gateway"
value = aws_lambda_function.start_stop_scheduler.function_name
}

output "lambda_function_last_modified" {
description = "The date Lambda function was last modified"
value = aws_lambda_function.start_stop_scheduler.last_modified
}

output "lambda_function_version" {
description = "Latest published version of your Lambda function"
value = aws_lambda_function.start_stop_scheduler.version
}

output "lambda_function_log_group_name" {
description = "The name of the lambda's log group"
value = aws_cloudwatch_log_group.start_stop_scheduler.name
}

output "lambda_function_log_group_arn" {
description = "The ARN of the lambda's log group"
value = aws_cloudwatch_log_group.start_stop_scheduler.arn
}

output "clouwatch_event_rules" {
description = "Cloudwatch event rules generated by the module to trigger the lambda"
value = concat(
[for r in aws_cloudwatch_event_rule.start : { arn = r.arn, description = r.description }],
[for r in aws_cloudwatch_event_rule.stop : { arn = r.arn, description = r.description }]
)
}
70 changes: 50 additions & 20 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
# variable "my_var" {
# type = string
# description = "A variable with a default value and a condition."
# default = "toto!"

# validation {
# condition = length(var.my_var) > 4
# error_message = "This variable should have more than 4 characters."
# }
# }

# variable "another_var" {
# type = string
# description = "A variable with a condition."

# validation {
# condition = length(var.another_var) > 6
# error_message = "This variable should have more than 6 characters."
# }
# }
variable "name" {
description = "A name used to create resources in module"
type = string
validation {
condition = length(var.name) > 1
error_message = "This variable should have more than 1 characters."
}
}

variable "schedules" {
description = "The configuration of your crons. Select your resources with tags, and specify several crons for start and stop."
type = list(map(map(string)))

# TODO validation
}

variable "tags" {
default = {}
description = "Custom Resource tags"
type = map(string)
}

variable "lambda_timeout" {
default = 10
description = "Amount of time your Lambda Function has to run in seconds."
type = number

validation {
condition = var.lambda_timeout < 900
error_message = "AWS Lambda Quota limits lambda execution to 15 min."
}
}

variable "rds_schedule" {
default = true
description = "Run the scheduler on RDS."
type = bool
}

variable "asg_schedule" {
default = true
description = "Run the scheduler on AutoScalingGroup."
type = bool
}

variable "aws_regions" {
default = null
description = "List of AWS region where the scheduler will be applied. By default target the current region."
type = list(string)
}

0 comments on commit 3b8e732

Please sign in to comment.