From e3c4ffedb73a7d0fcbcafad7877c5268c147af48 Mon Sep 17 00:00:00 2001 From: Paul McEnery <94845973+pmcenery-bl@users.noreply.github.com> Date: Sat, 17 Jun 2023 20:18:04 +0100 Subject: [PATCH] feat: Support for Eventbridge Scheduler Schedules (#83) Signed-off-by: Paul McEnery Co-authored-by: Anton Babenko --- README.md | 66 ++++++-- examples/api-gateway-event-source/main.tf | 2 +- examples/complete/main.tf | 2 +- examples/default-bus/main.tf | 2 +- examples/with-api-destination/main.tf | 2 +- examples/with-archive/main.tf | 2 +- examples/with-ecs-scheduling/main.tf | 2 +- examples/with-lambda-scheduling/main.tf | 2 +- examples/with-permissions/main.tf | 2 +- examples/with-schedules/README.md | 63 +++++++ examples/with-schedules/main.tf | 96 +++++++++++ examples/with-schedules/outputs.tf | 37 +++++ examples/with-schedules/variables.tf | 0 examples/with-schedules/versions.tf | 18 ++ iam.tf | 2 +- main.tf | 194 +++++++++++++++++++++- outputs.tf | 47 ++++-- variables.tf | 44 ++++- 18 files changed, 547 insertions(+), 36 deletions(-) create mode 100644 examples/with-schedules/README.md create mode 100644 examples/with-schedules/main.tf create mode 100644 examples/with-schedules/outputs.tf create mode 100644 examples/with-schedules/variables.tf create mode 100644 examples/with-schedules/versions.tf diff --git a/README.md b/README.md index 239591a..9262527 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,12 @@ Terraform module to create EventBridge resources. ## Supported Features -- Creates AWS EventBridge Resources (bus, rules, targets, permissions, connections, destinations) +- Creates AWS EventBridge Resources (bus, rules, targets, permissions, connections, destinations, schedules and schedule groups) - Attach resources to an existing EventBridge bus - Support AWS EventBridge Archives and Replays - Conditional creation for many types of resources - Support IAM policy attachments and various ways to create and attach additional policies -## Feature Roadmap - -- Support monitoring usage with Cloudwatch Metrics - ## Usage ### EventBridge Complete @@ -230,6 +226,29 @@ module "eventbridge" { } ``` +### EventBridge Scheduler which triggers Lambda Function + +```hcl +module "eventbridge" { + source = "terraform-aws-modules/eventbridge/aws" + + bus_name = "example" # "default" bus already support schedule_expression in rules + + attach_lambda_policy = true + lambda_target_arns = ["arn:aws:lambda:us-east-1:135367859851:function:resolved-penguin-lambda"] + + schedules = { + lambda-cron = { + description = "Trigger for a Lambda" + schedule_expression = "rate(1 day)" + timezone = "Europe/London" + arn = "arn:aws:lambda:us-east-1:135367859851:function:resolved-penguin-lambda" + input = jsonencode({ "job" : "cron-by-rate" }) + } + } +} +``` + ### EventBridge API Destination ```hcl @@ -324,6 +343,8 @@ module "eventbridge" { create_role = false # to control creation of the IAM role and policies required for EventBridge create_connections = false # to control creation of EventBridge Connection resources create_api_destinations = false # to control creation of EventBridge Destination resources + create_schedule_groups = false # to control creation of EventBridge Schedule Group resources + create_schedules = false # to control creation of EventBridge Schedule resources attach_cloudwatch_policy = false attach_ecs_policy = false @@ -346,8 +367,9 @@ module "eventbridge" { * [Using Default Bus](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/default-bus) - Creates resources in the `default` bus. * [Archive](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/with-archive) - EventBridge Archives resources in various configurations. * [Permissions](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/with-permissions) - Controls permissions to EventBridge. +* [Scheduler](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/with-schedules) - EventBridge Scheduler which works with any bus (recommended way). * [ECS Scheduling Events](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/with-ecs-scheduling) - Use default bus to schedule events on ECS. -* [Lambda Scheduling Events](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/with-lambda-scheduling) - Trigger Lambda functions on schedule. +* [Lambda Scheduling Events](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/with-lambda-scheduling) - Trigger Lambda functions on schedule (works only with default bus). * [API Destination](https://github.com/terraform-aws-modules/terraform-aws-eventbridge/tree/master/examples/with-api-destination) - Control access to EventBridge using API destinations. @@ -409,6 +431,8 @@ No modules. | [aws_iam_role.eventbridge](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.additional_many](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.additional_one](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_scheduler_schedule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule) | resource | +| [aws_scheduler_schedule_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule_group) | resource | | [aws_schemas_discoverer.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/schemas_discoverer) | resource | | [aws_cloudwatch_event_bus.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudwatch_event_bus) | data source | | [aws_iam_policy.tracing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | @@ -432,6 +456,8 @@ No modules. | [append\_connection\_postfix](#input\_append\_connection\_postfix) | Controls whether to append '-connection' to the name of the connection | `bool` | `true` | no | | [append\_destination\_postfix](#input\_append\_destination\_postfix) | Controls whether to append '-destination' to the name of the destination | `bool` | `true` | no | | [append\_rule\_postfix](#input\_append\_rule\_postfix) | Controls whether to append '-rule' to the name of the rule | `bool` | `true` | no | +| [append\_schedule\_group\_postfix](#input\_append\_schedule\_group\_postfix) | Controls whether to append '-group' to the name of the schedule group | `bool` | `true` | no | +| [append\_schedule\_postfix](#input\_append\_schedule\_postfix) | Controls whether to append '-schedule' to the name of the schedule | `bool` | `true` | no | | [archives](#input\_archives) | A map of objects with the EventBridge Archive definitions. | `map(any)` | `{}` | no | | [attach\_api\_destination\_policy](#input\_attach\_api\_destination\_policy) | Controls whether the API Destination policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | | [attach\_cloudwatch\_policy](#input\_attach\_cloudwatch\_policy) | Controls whether the Cloudwatch policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | @@ -459,6 +485,8 @@ No modules. | [create\_permissions](#input\_create\_permissions) | Controls whether EventBridge Permission resources should be created | `bool` | `true` | no | | [create\_role](#input\_create\_role) | Controls whether IAM roles should be created | `bool` | `true` | no | | [create\_rules](#input\_create\_rules) | Controls whether EventBridge Rule resources should be created | `bool` | `true` | no | +| [create\_schedule\_groups](#input\_create\_schedule\_groups) | Controls whether EventBridge Schedule Group resources should be created | `bool` | `true` | no | +| [create\_schedules](#input\_create\_schedules) | Controls whether EventBridge Schedule resources should be created | `bool` | `true` | no | | [create\_schemas\_discoverer](#input\_create\_schemas\_discoverer) | Controls whether default schemas discoverer should be created | `bool` | `false` | no | | [create\_targets](#input\_create\_targets) | Controls whether EventBridge Target resources should be created | `bool` | `true` | no | | [ecs\_target\_arns](#input\_ecs\_target\_arns) | The Amazon Resource Name (ARN) of the AWS ECS Tasks you want to use as EventBridge targets | `list(string)` | `[]` | no | @@ -481,29 +509,37 @@ No modules. | [role\_permissions\_boundary](#input\_role\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the IAM role used by EventBridge | `string` | `null` | no | | [role\_tags](#input\_role\_tags) | A map of tags to assign to IAM role | `map(string)` | `{}` | no | | [rules](#input\_rules) | A map of objects with EventBridge Rule definitions. | `map(any)` | `{}` | no | +| [schedule\_group\_timeouts](#input\_schedule\_group\_timeouts) | A map of objects with EventBridge Schedule Group create and delete timeouts. | `map(string)` | `{}` | no | +| [schedule\_groups](#input\_schedule\_groups) | A map of objects with EventBridge Schedule Group definitions. | `any` | `{}` | no | +| [schedules](#input\_schedules) | A map of objects with EventBridge Schedule definitions. | `map(any)` | `{}` | no | | [schemas\_discoverer\_description](#input\_schemas\_discoverer\_description) | Default schemas discoverer description | `string` | `"Auto schemas discoverer event"` | no | | [sfn\_target\_arns](#input\_sfn\_target\_arns) | The Amazon Resource Name (ARN) of the StepFunctions you want to use as EventBridge targets | `list(string)` | `[]` | no | | [sns\_target\_arns](#input\_sns\_target\_arns) | The Amazon Resource Name (ARN) of the AWS SNS's you want to use as EventBridge targets | `list(string)` | `[]` | no | | [sqs\_target\_arns](#input\_sqs\_target\_arns) | The Amazon Resource Name (ARN) of the AWS SQS Queues you want to use as EventBridge targets | `list(string)` | `[]` | no | | [tags](#input\_tags) | A map of tags to assign to resources. | `map(string)` | `{}` | no | | [targets](#input\_targets) | A map of objects with EventBridge Target definitions. | `any` | `{}` | no | -| [trusted\_entities](#input\_trusted\_entities) | Step Function additional trusted entities for assuming roles (trust relationship) | `list(string)` | `[]` | no | +| [trusted\_entities](#input\_trusted\_entities) | Additional trusted entities for assuming roles (trust relationship) | `list(string)` | `[]` | no | ## Outputs | Name | Description | |------|-------------| -| [eventbridge\_api\_destination\_arns](#output\_eventbridge\_api\_destination\_arns) | The EventBridge API Destination ARNs created | -| [eventbridge\_archive\_arns](#output\_eventbridge\_archive\_arns) | The EventBridge Archive Arns created | -| [eventbridge\_bus\_arn](#output\_eventbridge\_bus\_arn) | The EventBridge Bus Arn | +| [eventbridge\_api\_destination\_arns](#output\_eventbridge\_api\_destination\_arns) | The EventBridge API Destination ARNs | +| [eventbridge\_archive\_arns](#output\_eventbridge\_archive\_arns) | The EventBridge Archive ARNs | +| [eventbridge\_bus\_arn](#output\_eventbridge\_bus\_arn) | The EventBridge Bus ARN | | [eventbridge\_bus\_name](#output\_eventbridge\_bus\_name) | The EventBridge Bus Name | -| [eventbridge\_connection\_arns](#output\_eventbridge\_connection\_arns) | The EventBridge Connection Arns created | -| [eventbridge\_connection\_ids](#output\_eventbridge\_connection\_ids) | The EventBridge Connection IDs created | -| [eventbridge\_permission\_ids](#output\_eventbridge\_permission\_ids) | The EventBridge Permission Arns created | +| [eventbridge\_connection\_arns](#output\_eventbridge\_connection\_arns) | The EventBridge Connection Arns | +| [eventbridge\_connection\_ids](#output\_eventbridge\_connection\_ids) | The EventBridge Connection IDs | +| [eventbridge\_permission\_ids](#output\_eventbridge\_permission\_ids) | The EventBridge Permission IDs | | [eventbridge\_role\_arn](#output\_eventbridge\_role\_arn) | The ARN of the IAM role created for EventBridge | | [eventbridge\_role\_name](#output\_eventbridge\_role\_name) | The name of the IAM role created for EventBridge | -| [eventbridge\_rule\_arns](#output\_eventbridge\_rule\_arns) | The EventBridge Rule ARNs created | -| [eventbridge\_rule\_ids](#output\_eventbridge\_rule\_ids) | The EventBridge Rule IDs created | +| [eventbridge\_rule\_arns](#output\_eventbridge\_rule\_arns) | The EventBridge Rule ARNs | +| [eventbridge\_rule\_ids](#output\_eventbridge\_rule\_ids) | The EventBridge Rule IDs | +| [eventbridge\_schedule\_arns](#output\_eventbridge\_schedule\_arns) | The EventBridge Schedule ARNs created | +| [eventbridge\_schedule\_group\_arns](#output\_eventbridge\_schedule\_group\_arns) | The EventBridge Schedule Group ARNs | +| [eventbridge\_schedule\_group\_ids](#output\_eventbridge\_schedule\_group\_ids) | The EventBridge Schedule Group IDs | +| [eventbridge\_schedule\_group\_states](#output\_eventbridge\_schedule\_group\_states) | The EventBridge Schedule Group states | +| [eventbridge\_schedule\_ids](#output\_eventbridge\_schedule\_ids) | The EventBridge Schedule IDs created | ## Authors diff --git a/examples/api-gateway-event-source/main.tf b/examples/api-gateway-event-source/main.tf index bfa10d1..de7764c 100644 --- a/examples/api-gateway-event-source/main.tf +++ b/examples/api-gateway-event-source/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 998f9e6..7e7c9d1 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/default-bus/main.tf b/examples/default-bus/main.tf index 362c017..a36db86 100644 --- a/examples/default-bus/main.tf +++ b/examples/default-bus/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/with-api-destination/main.tf b/examples/with-api-destination/main.tf index e207290..7f5bb07 100644 --- a/examples/with-api-destination/main.tf +++ b/examples/with-api-destination/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/with-archive/main.tf b/examples/with-archive/main.tf index f5f4623..5c5d009 100644 --- a/examples/with-archive/main.tf +++ b/examples/with-archive/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/with-ecs-scheduling/main.tf b/examples/with-ecs-scheduling/main.tf index a1515ef..1495397 100644 --- a/examples/with-ecs-scheduling/main.tf +++ b/examples/with-ecs-scheduling/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/with-lambda-scheduling/main.tf b/examples/with-lambda-scheduling/main.tf index 6498ba6..e3b9324 100644 --- a/examples/with-lambda-scheduling/main.tf +++ b/examples/with-lambda-scheduling/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/with-permissions/main.tf b/examples/with-permissions/main.tf index 0f0d711..a4e5ebf 100644 --- a/examples/with-permissions/main.tf +++ b/examples/with-permissions/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-southeast-1" + region = "eu-west-1" # Make it faster by skipping something skip_metadata_api_check = true diff --git a/examples/with-schedules/README.md b/examples/with-schedules/README.md new file mode 100644 index 0000000..527af5a --- /dev/null +++ b/examples/with-schedules/README.md @@ -0,0 +1,63 @@ +# EventBridge Scheduler with Schedule Groups Example + +Configuration in this directory creates EventBridge Scheduler resources which triggers Lambda Function. + +## Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.64 | +| [null](#requirement\_null) | >= 2.0 | +| [random](#requirement\_random) | >= 3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [null](#provider\_null) | >= 2.0 | +| [random](#provider\_random) | >= 3.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [eventbridge](#module\_eventbridge) | ../../ | n/a | +| [lambda](#module\_lambda) | terraform-aws-modules/lambda/aws | ~> 5.0 | + +## Resources + +| Name | Type | +|------|------| +| [null_resource.download_package](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [eventbridge\_schedule\_arns](#output\_eventbridge\_schedule\_arns) | The EventBridge Schedule ARNs created | +| [eventbridge\_schedule\_group\_arns](#output\_eventbridge\_schedule\_group\_arns) | The EventBridge Schedule Group ARNs | +| [eventbridge\_schedule\_group\_ids](#output\_eventbridge\_schedule\_group\_ids) | The EventBridge Schedule Group IDs | +| [eventbridge\_schedule\_group\_states](#output\_eventbridge\_schedule\_group\_states) | The EventBridge Schedule Group states | +| [eventbridge\_schedule\_ids](#output\_eventbridge\_schedule\_ids) | The EventBridge Schedule IDs created | +| [lambda\_function\_arn](#output\_lambda\_function\_arn) | The ARN of the Lambda Function | +| [lambda\_function\_name](#output\_lambda\_function\_name) | The name of the Lambda Function | + diff --git a/examples/with-schedules/main.tf b/examples/with-schedules/main.tf new file mode 100644 index 0000000..c710ad9 --- /dev/null +++ b/examples/with-schedules/main.tf @@ -0,0 +1,96 @@ +provider "aws" { + region = "eu-west-1" + + # Make it faster by skipping something + skip_metadata_api_check = true + skip_region_validation = true + skip_credentials_validation = true + skip_requesting_account_id = true +} + +module "eventbridge" { + source = "../../" + + create_bus = true + bus_name = "example" # "default" bus already support schedule_expression in rules + + attach_lambda_policy = true + lambda_target_arns = [module.lambda.lambda_function_arn] + + schedule_groups = { + dev = { + name_prefix = "tmp-dev-" + } + prod = { + name = "prod" + tags = { + Env = "SuperProd" + } + } + } + + schedules = { + lambda-cron = { + group_name = "dev" + description = "Trigger for a Lambda" + schedule_expression = "cron(0 1 * * ? *)" + timezone = "Europe/London" + arn = module.lambda.lambda_function_arn + input = jsonencode({ "job" : "cron-by-rate" }) + } + prod-lambda-cron = { + group_name = "prod" + schedule_expression = "rate(10 hours)" + arn = module.lambda.lambda_function_arn + } + } +} + +################## +# Extra resources +################## + +resource "random_pet" "this" { + length = 2 +} + +############################################# +# Using packaged function from Lambda module +############################################# + +module "lambda" { + source = "terraform-aws-modules/lambda/aws" + version = "~> 5.0" + + function_name = "${random_pet.this.id}-lambda" + handler = "index.lambda_handler" + runtime = "python3.8" + + create_package = false + local_existing_package = local.downloaded + + trusted_entities = ["scheduler.amazonaws.com"] + + create_current_version_allowed_triggers = false + allowed_triggers = { + ScanAmiRule = { + principal = "scheduler.amazonaws.com" + source_arn = module.eventbridge.eventbridge_schedule_arns["lambda-cron"] + } + } +} + +locals { + package_url = "https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-lambda/master/examples/fixtures/python3.8-zip/existing_package.zip" + downloaded = "downloaded_package_${md5(local.package_url)}.zip" +} + +resource "null_resource" "download_package" { + triggers = { + downloaded = local.downloaded + } + + provisioner "local-exec" { + command = "curl -L -o ${local.downloaded} ${local.package_url}" + } +} diff --git a/examples/with-schedules/outputs.tf b/examples/with-schedules/outputs.tf new file mode 100644 index 0000000..b4ceac4 --- /dev/null +++ b/examples/with-schedules/outputs.tf @@ -0,0 +1,37 @@ +# EventBridge Schedule Group +output "eventbridge_schedule_group_ids" { + description = "The EventBridge Schedule Group IDs" + value = module.eventbridge.eventbridge_schedule_arns +} + +output "eventbridge_schedule_group_arns" { + description = "The EventBridge Schedule Group ARNs" + value = module.eventbridge.eventbridge_schedule_group_arns +} + +output "eventbridge_schedule_group_states" { + description = "The EventBridge Schedule Group states" + value = module.eventbridge.eventbridge_schedule_group_states +} + +# EventBridge Schedule +output "eventbridge_schedule_ids" { + description = "The EventBridge Schedule IDs created" + value = module.eventbridge.eventbridge_schedule_ids +} + +output "eventbridge_schedule_arns" { + description = "The EventBridge Schedule ARNs created" + value = module.eventbridge.eventbridge_schedule_arns +} + +# Lambda Function +output "lambda_function_arn" { + description = "The ARN of the Lambda Function" + value = module.lambda.lambda_function_arn +} + +output "lambda_function_name" { + description = "The name of the Lambda Function" + value = module.lambda.lambda_function_name +} diff --git a/examples/with-schedules/variables.tf b/examples/with-schedules/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/with-schedules/versions.tf b/examples/with-schedules/versions.tf new file mode 100644 index 0000000..cdf3d85 --- /dev/null +++ b/examples/with-schedules/versions.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.64" + } + random = { + source = "hashicorp/random" + version = ">= 3.0" + } + null = { + source = "hashicorp/null" + version = ">= 2.0" + } + } +} diff --git a/iam.tf b/iam.tf index 55927f9..1ae6a2f 100644 --- a/iam.tf +++ b/iam.tf @@ -21,7 +21,7 @@ data "aws_iam_policy_document" "assume_role" { principals { type = "Service" - identifiers = distinct(concat(["events.amazonaws.com"], var.trusted_entities)) + identifiers = distinct(concat(["events.amazonaws.com"], var.trusted_entities, length(keys(var.schedules)) > 0 && var.create_schedules ? ["scheduler.amazonaws.com"] : [])) } } } diff --git a/main.tf b/main.tf index 43528a0..6fddaee 100644 --- a/main.tf +++ b/main.tf @@ -29,6 +29,19 @@ locals { "Name" = var.append_destination_postfix ? "${replace(index, "_", "-")}-destination" : index }) ]) + eventbridge_schedule_groups = { + for index, group in var.schedule_groups : + index => merge(group, { + "Name" = var.append_schedule_group_postfix ? "${replace(index, "_", "-")}-group" : index + }) + } + eventbridge_schedules = flatten([ + for index, sched in var.schedules : + merge(sched, { + "name" = index + "Name" = var.append_schedule_postfix ? "${replace(index, "_", "-")}-schedule" : index + }) + ]) } data "aws_cloudwatch_event_bus" "this" { @@ -232,7 +245,7 @@ resource "aws_cloudwatch_event_target" "this" { resource "aws_cloudwatch_event_archive" "this" { for_each = var.create && var.create_archives ? var.archives : {} - name = each.key + name = lookup(each.value, "name", each.key) event_source_arn = try(each.value["event_source_arn"], aws_cloudwatch_event_bus.this[0].arn) description = lookup(each.value, "description", null) @@ -402,3 +415,182 @@ resource "aws_cloudwatch_event_api_destination" "this" { invocation_rate_limit_per_second = lookup(each.value, "invocation_rate_limit_per_second", null) connection_arn = aws_cloudwatch_event_connection.this[each.value.name].arn } + +resource "aws_scheduler_schedule_group" "this" { + for_each = { for k, v in local.eventbridge_schedule_groups : k => v if var.create && var.create_schedule_groups } + + name = lookup(each.value, "name_prefix", null) == null ? try(each.value.name, each.key) : null + name_prefix = lookup(each.value, "name_prefix", null) != null ? each.value.name_prefix : null + + tags = lookup(each.value, "tags", {}) + + timeouts { + create = try(var.schedule_group_timeouts.create, null) + delete = try(var.schedule_group_timeouts.delete, null) + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_scheduler_schedule" "this" { + for_each = { for k, v in local.eventbridge_schedules : v.name => v if var.create && var.create_schedules } + + name = each.value.Name + name_prefix = lookup(each.value, "name_prefix", null) + description = lookup(each.value, "description", null) + group_name = try(aws_scheduler_schedule_group.this[each.value.group_name].id, lookup(each.value, "group_name", null)) + + start_date = lookup(each.value, "start_date", null) + end_date = lookup(each.value, "end_date", null) + + kms_key_arn = lookup(each.value, "kms_key_arn", null) + + schedule_expression = each.value.schedule_expression + schedule_expression_timezone = lookup(each.value, "timezone", null) + + state = lookup(each.value, "state", true) ? "ENABLED" : "DISABLED" + + flexible_time_window { + maximum_window_in_minutes = lookup(each.value, "maximum_window_in_minutes", null) + mode = lookup(each.value, "use_flexible_time_window", false) ? "FLEXIBLE" : "OFF" + } + + target { + arn = each.value.arn + role_arn = can(length(each.value.role_arn) > 0) ? each.value.role_arn : aws_iam_role.eventbridge[0].arn + + input = lookup(each.value, "input", null) + + dynamic "dead_letter_config" { + for_each = lookup(each.value, "dead_letter_arn", null) != null ? [true] : [] + + content { + arn = each.value.dead_letter_arn + } + } + + dynamic "ecs_parameters" { + for_each = lookup(each.value, "ecs_parameters", null) != null ? [ + each.value.ecs_parameters + ] : [] + + content { + task_definition_arn = ecs_parameters.value.task_definition_arn + enable_ecs_managed_tags = lookup(ecs_parameters.value, "enable_ecs_managed_tags", null) + enable_execute_command = lookup(ecs_parameters.value, "enable_execute_command", null) + group = lookup(ecs_parameters.value, "group", null) + launch_type = lookup(ecs_parameters.value, "launch_type", null) + platform_version = lookup(ecs_parameters.value, "platform_version", null) + propagate_tags = lookup(ecs_parameters.value, "propagate_tags", null) + reference_id = lookup(ecs_parameters.value, "reference_id", null) + tags = lookup(ecs_parameters.value, "tags", null) + task_count = lookup(ecs_parameters.value, "task_count", null) + + dynamic "capacity_provider_strategy" { + for_each = lookup(ecs_parameters.value, "capacity_provider_strategy", null) != null ? [ + ecs_parameters.value.capacity_provider_strategy + ] : [] + + content { + capacity_provider = capacity_provider_strategy.value.capacity_provider + base = lookup(capacity_provider_strategy.value, "base", null) + weight = lookup(capacity_provider_strategy.value, "weight", null) + } + } + + dynamic "network_configuration" { + for_each = lookup(ecs_parameters.value, "network_configuration", null) != null ? [ + ecs_parameters.value.network_configuration + ] : [] + + content { + subnets = lookup(network_configuration.value, "subnets", null) + security_groups = lookup(network_configuration.value, "security_groups", null) + assign_public_ip = lookup(network_configuration.value, "assign_public_ip", null) + } + } + + dynamic "placement_constraints" { + for_each = lookup(ecs_parameters.value, "placement_constraints", null) != null ? [ + ecs_parameters.value.placement_constraints + ] : [] + + content { + type = placement_constraints.value.type + expression = lookup(placement_constraints.value, "expression", null) + } + } + + dynamic "placement_strategy" { + for_each = lookup(ecs_parameters.value, "placement_strategy", null) != null ? [ + ecs_parameters.value.placement_strategy + ] : [] + + content { + type = placement_strategy.value.type + field = lookup(placement_strategy.value, "field", null) + } + } + } + } + + dynamic "eventbridge_parameters" { + for_each = lookup(each.value, "eventbridge_parameters", null) != null ? [ + each.value.eventbridge_parameters + ] : [] + + content { + detail_type = eventbridge_parameters.value.detail_type + source = eventbridge_parameters.value.source + } + } + + dynamic "kinesis_parameters" { + for_each = lookup(each.value, "kinesis_parameters", null) != null ? [true] : [] + + content { + partition_key = kinesis_parameters.value.partition_key + } + } + + dynamic "sagemaker_pipeline_parameters" { + for_each = lookup(each.value, "sagemaker_pipeline_parameters", null) != null ? [ + each.value.sagemaker_pipeline_parameters + ] : [] + + content { + dynamic "pipeline_parameter" { + for_each = lookup(sagemaker_pipeline_parameters, "pipeline_parameter", null) != null ? [ + sagemaker_pipeline_parameters.value.pipeline_parameter + ] : [] + + content { + name = pipeline_parameter.value.name + value = pipeline_parameter.value.value + } + } + } + } + + dynamic "sqs_parameters" { + for_each = lookup(each.value, "message_group_id", null) != null ? [true] : [] + + content { + message_group_id = each.value.message_group_id + } + } + + dynamic "retry_policy" { + for_each = lookup(each.value, "retry_policy", null) != null ? [ + each.value.retry_policy + ] : [] + + content { + maximum_event_age_in_seconds = retry_policy.value.maximum_event_age_in_seconds + maximum_retry_attempts = retry_policy.value.maximum_retry_attempts + } + } + } +} diff --git a/outputs.tf b/outputs.tf index 83d02d8..0261ceb 100644 --- a/outputs.tf +++ b/outputs.tf @@ -5,48 +5,75 @@ output "eventbridge_bus_name" { } output "eventbridge_bus_arn" { - description = "The EventBridge Bus Arn" + description = "The EventBridge Bus ARN" value = try(aws_cloudwatch_event_bus.this[0].arn, "") } # EventBridge Archive output "eventbridge_archive_arns" { - description = "The EventBridge Archive Arns created" + description = "The EventBridge Archive ARNs" value = { for v in aws_cloudwatch_event_archive.this : v.name => v.arn } } # EventBridge Permission output "eventbridge_permission_ids" { - description = "The EventBridge Permission Arns created" + description = "The EventBridge Permission IDs" value = { for k, v in aws_cloudwatch_event_permission.this : k => v.id } } # EventBridge Connection output "eventbridge_connection_ids" { - description = "The EventBridge Connection IDs created" + description = "The EventBridge Connection IDs" value = { for k, v in aws_cloudwatch_event_connection.this : k => v.id } } output "eventbridge_connection_arns" { - description = "The EventBridge Connection Arns created" + description = "The EventBridge Connection Arns" value = { for k, v in aws_cloudwatch_event_connection.this : k => v.arn } } # EventBridge Destination output "eventbridge_api_destination_arns" { - description = "The EventBridge API Destination ARNs created" + description = "The EventBridge API Destination ARNs" value = { for k, v in aws_cloudwatch_event_api_destination.this : k => v.arn } } # EventBridge Rule output "eventbridge_rule_ids" { - description = "The EventBridge Rule IDs created" - value = { for k in sort(keys(var.rules)) : k => try(aws_cloudwatch_event_rule.this[k].id, null) if var.create && var.create_rules } + description = "The EventBridge Rule IDs" + value = { for k, v in aws_cloudwatch_event_rule.this : k => v.id } } output "eventbridge_rule_arns" { - description = "The EventBridge Rule ARNs created" - value = { for k in sort(keys(var.rules)) : k => try(aws_cloudwatch_event_rule.this[k].arn, null) if var.create && var.create_rules } + description = "The EventBridge Rule ARNs" + value = { for k, v in aws_cloudwatch_event_rule.this : k => v.arn } +} + +# EventBridge Schedule Groups +output "eventbridge_schedule_group_ids" { + description = "The EventBridge Schedule Group IDs" + value = { for k, v in aws_scheduler_schedule_group.this : k => v.id } +} + +output "eventbridge_schedule_group_arns" { + description = "The EventBridge Schedule Group ARNs" + value = { for k, v in aws_scheduler_schedule_group.this : k => v.arn } +} + +output "eventbridge_schedule_group_states" { + description = "The EventBridge Schedule Group states" + value = { for k, v in aws_scheduler_schedule_group.this : k => v.state } +} + +# EventBridge Schedule +output "eventbridge_schedule_ids" { + description = "The EventBridge Schedule IDs created" + value = { for k, v in aws_scheduler_schedule.this : k => v.id } +} + +output "eventbridge_schedule_arns" { + description = "The EventBridge Schedule ARNs created" + value = { for k, v in aws_scheduler_schedule.this : k => v.arn } } # IAM Role diff --git a/variables.tf b/variables.tf index e598631..c130322 100644 --- a/variables.tf +++ b/variables.tf @@ -28,6 +28,18 @@ variable "append_destination_postfix" { default = true } +variable "append_schedule_group_postfix" { + description = "Controls whether to append '-group' to the name of the schedule group" + type = bool + default = true +} + +variable "append_schedule_postfix" { + description = "Controls whether to append '-schedule' to the name of the schedule" + type = bool + default = true +} + variable "create_bus" { description = "Controls whether EventBridge Bus resource should be created" type = bool @@ -76,6 +88,18 @@ variable "create_schemas_discoverer" { default = false } +variable "create_schedule_groups" { + description = "Controls whether EventBridge Schedule Group resources should be created" + type = bool + default = true +} + +variable "create_schedules" { + description = "Controls whether EventBridge Schedule resources should be created" + type = bool + default = true +} + ####################### variable "bus_name" { @@ -132,12 +156,30 @@ variable "api_destinations" { default = {} } +variable "schedule_groups" { + description = "A map of objects with EventBridge Schedule Group definitions." + type = any + default = {} +} + +variable "schedules" { + description = "A map of objects with EventBridge Schedule definitions." + type = map(any) + default = {} +} + variable "tags" { description = "A map of tags to assign to resources." type = map(string) default = {} } +variable "schedule_group_timeouts" { + description = "A map of objects with EventBridge Schedule Group create and delete timeouts." + type = map(string) + default = {} +} + ###### # IAM ###### @@ -337,7 +379,7 @@ variable "attach_policy_statements" { } variable "trusted_entities" { - description = "Step Function additional trusted entities for assuming roles (trust relationship)" + description = "Additional trusted entities for assuming roles (trust relationship)" type = list(string) default = [] }