diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index a49e7c15..adfa54b8 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -53,6 +53,7 @@ updates: - "infrastructure/modules/ecs-service" - "infrastructure/modules/efs" - "infrastructure/modules/elasticache" + - "infrastructure/modules/eventbridge" - "infrastructure/modules/github-config" - "infrastructure/modules/guardduty" - "infrastructure/modules/iam" diff --git a/README.md b/README.md index 22d1adc1..bad05d02 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,7 @@ Rules: | `ecs-service` | terraform-aws-modules/ecs/aws//modules/service | ECS service and task definition | | `efs` | terraform-aws-modules/efs/aws | EFS file system with access points and mount targets | | `elasticache` | — | ElastiCache cluster (Redis/Memcached) | +| `eventbridge` | terraform-aws-modules/eventbridge/aws | AWS EventBridge Resources | | `github-config` | — | GitHub OIDC provider and runner configuration | | `guardduty` | — | GuardDuty threat detection | | `iam` | terraform-aws-modules/iam/aws | IAM policies and roles | diff --git a/infrastructure/modules/eventbridge/.terraform.lock.hcl b/infrastructure/modules/eventbridge/.terraform.lock.hcl new file mode 100644 index 00000000..2f63013b --- /dev/null +++ b/infrastructure/modules/eventbridge/.terraform.lock.hcl @@ -0,0 +1,30 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.64.0" + constraints = ">= 6.42.0" + hashes = [ + "h1:/wtl8WUDUuXhCa2iiZA4Qm2uwo3sAi0zegrHiC7YiJ0=", + "h1:2fTLxzUDmp/KVIHbIeLTB4bIzWHx8E6Dw+1ALLUi+Yw=", + "h1:4siTahLyzGh4BoMQcL7VXeL/mn8iR/zKv93NyhQob+0=", + "h1:EEWCXlg69fty/Qi+kehrREnVEaWdKnLlVon542b6nxQ=", + "h1:wXARLY+IeQ7ufYxCLTPCwToWGMRvOpiOTfJS97iwUzI=", + "zh:07172315d67bc9781240272759cdfc7bd32b7e72384a56862c2c1da3cca99a81", + "zh:154ce7d2659de9a59ddfe96d7cab41a9ddc2cb267a7d4bcdf4e737ff2ffdec06", + "zh:17324d4335a7a7ac01cc23eded530775606680ff53b47cb74a3cb95d1121f836", + "zh:307ab92324ec5a61b124881ab8cac1d9e316f4527dfd0e1b59794c229407eb4e", + "zh:31e25f1903661332e36a95283042dd3ec50b47c186db00663fbd976a11e6a6b2", + "zh:3311d9f3bd12a24886027dbe73859dcd1e67bd0e3046227a338cf2c7ca04d18e", + "zh:37916156a3aac3b29be3acebd15d53145ea4ab5d4aaa825eaebe75481fa00500", + "zh:4158cb8c38b3ac6aa98eb15935ec6bd7c30838d85d2b00acc9812df8382ae908", + "zh:5bfb9499c66d9db5b34dc5c60f426a1ab1baa5457ce2aefebca826a9c3f92fb0", + "zh:6eb29ead5a4aca3b1f35812e7e8c75419180e1928e479b458f206861277736db", + "zh:7a82b6dd0c0cdef8045a4adfbddd36acb86b6b23fcbed8e189c2d71f7dc4a502", + "zh:9556bd792032c3f7e73ea4dd08cec88dc1327f5a4a57d79c30ba844ae2b9a3c0", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:c5234180464cb800c83a41f57462742b802c150ad7d4417626fcd9cb511c01d2", + "zh:cd776b83b1f7b36635957350afe7ce28ba4e4ea3a5e2deb00d13dbd3b35d9d40", + "zh:fb583a7b791c6f915b86573d04f05ddbf7f1a5e4120c5d8a7450a3086c1225c4", + ] +} diff --git a/infrastructure/modules/eventbridge/README.md b/infrastructure/modules/eventbridge/README.md new file mode 100644 index 00000000..f2a37b5b --- /dev/null +++ b/infrastructure/modules/eventbridge/README.md @@ -0,0 +1,303 @@ +# EventBridge + +NHS Screening wrapper around the community +[`terraform-aws-modules/terraform-aws-eventbridge`](https://registry.terraform.io/modules/terraform-aws-modules/eventbridge/aws/4.3.2) +module that consumes the shared `context.tf` for naming and tagging. +Note that this provides access to AWS EventBridge Scheduler in addition +to AWS EventBridge. + +Main differences from the wrapped module: + +1. KMS keys must be provided. + + Many of the underlying resources take an optional KMS key, falling back to + using an AWS-owned key. In this module, we make it compulsory to provide + these keys. + + As this is incompatible with schema discovery, we disable schema discovery. + +2. Names are modified in an attempt to avoid clashes when deploying into +multiple namespaces. + + The underlying module names many resources based on the keys of maps passed + in as variables. This causes conflicts when a stack is deployed into + multiple workspaces in the same AWS account. + + This module prefixes many of these keys with the bus name. The bus name by + default includes the workspace id. + + Not every upstream resource name is prefixed; check planned names for + collisions when deploying several workspaces into one account and region. + +3. Connections are disallowed, as the underlying module does not redact +credentials contained therein from the terraform state. + +## What this module enforces + +| Control | How it is enforced | +| --- | --- | +| Encryption at rest | A customer-managed KMS key is required for the bus, and for each configured archive, schedule, and pipe. | +| SNS KMS access | SNS target policies require specific KMS key ARNs; wildcard keys are rejected. | +| Naming | The bus defaults to the context ID; connection, destination, pipe, schedule-group, and log-delivery names are scoped to the context. | +| Tagging | Bus resources and IAM roles receive context tags. | +| Creation gate | `module.this.enabled` controls creation of the upstream module. | +| Secrets protection | Connections are disallowed, as the wrapped module doesn't handle them safely | + +## Usage + +### Minimal encrypted bus + + module "events" { + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/eventbridge?ref=" + + service = "bcss" + environment = "test" + name = "events" + kms_key_identifier = module.eventbridge_kms.key_arn + } + +### Production SNS target + + module "events" { + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/eventbridge?ref=" + + service = "bcss" + environment = "production" + name = "notifications" + kms_key_identifier = module.eventbridge_kms.key_arn + + rules = { + screening_completed = { + description = "Route completed screening events" + event_pattern = jsonencode({ source = ["bcss.screening"] }) + } + } + targets = { + screening_completed = [{ name = "notifications", arn = aws_sns_topic.notifications.arn }] + } + + attach_sns_policy = true + sns_target_arns = [aws_sns_topic.notifications.arn] + sns_kms_arns = [module.sns_kms.key_arn] + } + +### Advanced scheduler group + + module "scheduled_events" { + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/eventbridge?ref=" + + service = "bcss" + environment = "production" + name = "jobs" + kms_key_identifier = module.eventbridge_kms.key_arn + + schedule_groups = { nightly = {} } + schedules = { + nightly_job = { + arn = aws_lambda_function.job.arn + schedule_expression = "cron(0 2 * * ? *)" + group_name = "nightly" + kms_key_arn = module.scheduler_kms.key_arn + } + } + } + +## Conventions + +- Pass a pinned release ref and supply the required customer-managed KMS keys. +- Keys of `schedule_groups` are logical identifiers; schedule `group_name` references a key, not the prefixed AWS name. +- `log_delivery` entries default to context-prefixed names when `name` is omitted or null. +- Additional `role_tags` are combined with context tags; context values take precedence. +- Review the plan for resource names, policies, and target permissions before applying. + +## Validation + +When `attach_sns_policy` is true, provide at least one specific KMS key ARN in `sns_kms_arns`. +Wildcards and aliases are not accepted for this policy. + +## What this module does NOT do + +- It does not create KMS keys, SNS topics, target resources, or their resource policies. +- It does not guarantee unique names for every upstream resource or configure all target permissions. +- It does not support EventBridge connections or API destinations + + + + +## Requirements + +| Name | Version | +| ---- | ------- | +| [terraform](#requirement\_terraform) | >= 1.13 | +| [aws](#requirement\_aws) | >= 6.42 | + +## Providers + +| Name | Version | +| ---- | ------- | +| [terraform](#provider\_terraform) | n/a | + +## Modules + +| Name | Source | Version | +| ---- | ------ | ------- | +| [eventbridge](#module\_eventbridge) | git::https://github.com/terraform-aws-modules/terraform-aws-eventbridge.git | f9934726324c988f823682884b4fa003586a7b6f | +| [this](#module\_this) | ../tags | n/a | + +## Resources + +| Name | Type | +| ---- | ---- | +| [terraform_data.validations](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +| ---- | ----------- | ---- | ------- | :------: | +| [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no | +| [append\_destination\_postfix](#input\_append\_destination\_postfix) | Controls whether to append '-destination' to the name of the destination | `bool` | `true` | no | +| [append\_pipe\_postfix](#input\_append\_pipe\_postfix) | Controls whether to append '-pipe' to the name of the pipe | `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 | +| [application\_role](#input\_application\_role) | The role the application is performing | `string` | `"General"` | no | +| [archives](#input\_archives) | A map of objects with the EventBridge Archive definitions.

The type should really be

map(object({
name = optional(string)
event\_source\_arn = optional(string)
description = optional(string)
event\_pattern = optional(string)
retention\_days = optional(number)
kms\_key\_identifier = string
}))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `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 | +| [attach\_ecs\_policy](#input\_attach\_ecs\_policy) | Controls whether the ECS policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | +| [attach\_kinesis\_firehose\_policy](#input\_attach\_kinesis\_firehose\_policy) | Controls whether the Kinesis Firehose policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | +| [attach\_kinesis\_policy](#input\_attach\_kinesis\_policy) | Controls whether the Kinesis policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | +| [attach\_lambda\_policy](#input\_attach\_lambda\_policy) | Controls whether the Lambda Function policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | +| [attach\_policies](#input\_attach\_policies) | Controls whether list of policies should be added to IAM role | `bool` | `false` | no | +| [attach\_policy](#input\_attach\_policy) | Controls whether policy should be added to IAM role | `bool` | `false` | no | +| [attach\_policy\_json](#input\_attach\_policy\_json) | Controls whether policy\_json should be added to IAM role | `bool` | `false` | no | +| [attach\_policy\_jsons](#input\_attach\_policy\_jsons) | Controls whether policy\_jsons should be added to IAM role | `bool` | `false` | no | +| [attach\_policy\_statements](#input\_attach\_policy\_statements) | Controls whether policy\_statements should be added to IAM role | `bool` | `false` | no | +| [attach\_sfn\_policy](#input\_attach\_sfn\_policy) | Controls whether the StepFunction policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | +| [attach\_sns\_policy](#input\_attach\_sns\_policy) | Controls whether the SNS policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | +| [attach\_sqs\_policy](#input\_attach\_sqs\_policy) | Controls whether the SQS policy should be added to IAM role for EventBridge Target | `bool` | `false` | no | +| [attach\_tracing\_policy](#input\_attach\_tracing\_policy) | Controls whether X-Ray tracing policy should be added to IAM role for EventBridge | `bool` | `false` | no | +| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | +| [aws\_region](#input\_aws\_region) | The AWS region | `string` | `"eu-west-2"` | no | +| [bus\_description](#input\_bus\_description) | Event bus description | `string` | `null` | no | +| [bus\_name](#input\_bus\_name) | A unique name for your EventBridge Bus. Must be unique per AWS account and region. Defaults to whatever the tags module produces | `string` | `null` | no | +| [cloudwatch\_target\_arns](#input\_cloudwatch\_target\_arns) | The Amazon Resource Name (ARN) of the Cloudwatch Log Streams you want to use as EventBridge targets | `list(string)` | `[]` | no | +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional\_tag\_map, which are merged. | `any` |
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"project": null,
"regex_replace_chars": null,
"region": null,
"service": null,
"stack": null,
"tags": {},
"terraform_source": null,
"workspace": null
}
| no | +| [create\_archives](#input\_create\_archives) | Controls whether EventBridge Archive resources should be created | `bool` | `false` | no | +| [create\_bus](#input\_create\_bus) | Controls whether EventBridge Bus resource should be created | `bool` | `true` | no | +| [create\_log\_delivery](#input\_create\_log\_delivery) | Controls whether EventBridge log delivery resources should be created | `bool` | `true` | no | +| [create\_log\_delivery\_source](#input\_create\_log\_delivery\_source) | Controls whether EventBridge log delivery source resource should be created | `bool` | `true` | no | +| [create\_permissions](#input\_create\_permissions) | Controls whether EventBridge Permission resources should be created | `bool` | `true` | no | +| [create\_pipe\_role\_only](#input\_create\_pipe\_role\_only) | Controls whether an IAM role should be created for the pipes only | `bool` | `false` | no | +| [create\_pipes](#input\_create\_pipes) | Controls whether EventBridge Pipes 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\_targets](#input\_create\_targets) | Controls whether EventBridge Target resources should be created | `bool` | `true` | no | +| [data\_classification](#input\_data\_classification) | Used to identify the data classification of the resource, e.g 1-5 | `string` | `"n/a"` | no | +| [data\_type](#input\_data\_type) | The tag data\_type | `string` | `"None"` | no | +| [dead\_letter\_config](#input\_dead\_letter\_config) | Configuration details of the Amazon SQS queue for EventBridge to use as a
dead-letter queue (DLQ).

The type should really be

object({
arn = optional(string)
})

but it causes problems in the community module when Terraform sets
omitted fields to null. | `any` | `{}` | no | +| [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | +| [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no | +| [ecs\_pass\_role\_resources](#input\_ecs\_pass\_role\_resources) | List of approved roles to be passed | `list(string)` | `[]` | 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 | +| [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | +| [environment](#input\_environment) | ID element. Usually used to indicate role, e.g. 'prd', 'dev', 'test', 'preprod', 'prod', 'uat' | `string` | `null` | no | +| [event\_source\_name](#input\_event\_source\_name) | The partner event source that the new event bus will be matched with. Must match name. | `string` | `null` | no | +| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | +| [kinesis\_firehose\_target\_arns](#input\_kinesis\_firehose\_target\_arns) | The Amazon Resource Name (ARN) of the Kinesis Firehose Delivery Streams you want to use as EventBridge targets | `list(string)` | `[]` | no | +| [kinesis\_target\_arns](#input\_kinesis\_target\_arns) | The Amazon Resource Name (ARN) of the Kinesis Streams you want to use as EventBridge targets | `list(string)` | `[]` | no | +| [kms\_key\_identifier](#input\_kms\_key\_identifier) | The identifier of the AWS KMS customer managed key for EventBridge to use, to encrypt events on this event bus. The identifier can be the key Amazon Resource Name (ARN), KeyId, key alias, or key alias ARN. | `string` | n/a | yes | +| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | +| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no | +| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | +| [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` |
[
"default"
]
| no | +| [lambda\_target\_arns](#input\_lambda\_target\_arns) | The Amazon Resource Name (ARN) of the Lambda Functions you want to use as EventBridge targets | `list(string)` | `[]` | no | +| [log\_config](#input\_log\_config) | The configuration block for the EventBridge bus log config settings |
object({
include_detail = string
level = string
})
| `null` | no | +| [log\_delivery](#input\_log\_delivery) | Map of the configuration block for the EventBridge bus log delivery settings (key is the type of log delivery: cloudwatch\_logs, s3, firehose) |
map(object({
enabled = optional(bool, true)
destination_arn = string
source_name = optional(string)
name = optional(string)
output_format = optional(string)
field_delimiter = optional(string)
record_fields = optional(list(string))
s3_delivery_configuration = optional(object({
enable_hive_compatible_path = optional(bool)
suffix_path = optional(string)
}))
}))
| `{}` | no | +| [log\_delivery\_source\_name](#input\_log\_delivery\_source\_name) | Name of log delivery source; defaults to the name we use for the bus | `string` | `null` | no | +| [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | +| [number\_of\_policies](#input\_number\_of\_policies) | Number of policies to attach to IAM role | `number` | `0` | no | +| [number\_of\_policy\_jsons](#input\_number\_of\_policy\_jsons) | Number of policies JSON to attach to IAM role | `number` | `0` | no | +| [on\_off\_pattern](#input\_on\_off\_pattern) | Used to turn resources on and off based on a time pattern | `string` | `"n/a"` | no | +| [owner](#input\_owner) | The name and or NHS.net email address of the service owner | `string` | `"None"` | no | +| [permissions](#input\_permissions) | A map of objects with EventBridge Permission definitions.

The type should really be

map(object({
action = optional(string)
event\_bus\_name = optional(string)
condition\_org = optional(string)
}))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `map(any)` | `{}` | no | +| [pipes](#input\_pipes) | A map of EventBridge Pipe definitions.

The type should really be

map(object({
role\_arn = optional(string)
source = string
target = string
kms\_key\_identifier = string
description = optional(string)
desired\_state = optional(string)
source\_parameters = optional(any)
target\_parameters = optional(any)
enrichment = optional(string)
enrichment\_parameters = optional(any)
log\_configuration = optional(any)
tags = optional(map(string), {})
}))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `any` | `{}` | no | +| [policies](#input\_policies) | List of policy statements ARN to attach to IAM role | `list(string)` | `[]` | no | +| [policy](#input\_policy) | An additional policy document ARN to attach to IAM role | `string` | `null` | no | +| [policy\_json](#input\_policy\_json) | An additional policy document as JSON to attach to IAM role | `string` | `null` | no | +| [policy\_jsons](#input\_policy\_jsons) | List of additional policy documents as JSON to attach to IAM role | `list(string)` | `[]` | no | +| [policy\_path](#input\_policy\_path) | Path of IAM policy to use for EventBridge | `string` | `null` | no | +| [policy\_statements](#input\_policy\_statements) | Map of dynamic policy statements to attach to IAM role

The type should really be

map(object({
sid = optional(string)
effect = optional(string)
actions = optional(list(string))
not\_actions = optional(list(string))
resources = optional(list(string))
not\_resources = optional(list(string))
principals = optional(any)
not\_principals = optional(any)
condition = optional(any)
}))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `any` | `{}` | no | +| [project](#input\_project) | ID element. A project identifier, indicating the name or role of the project the resource is for, such as `website` or `api` | `string` | `null` | no | +| [public\_facing](#input\_public\_facing) | Whether this resource is public facing | `bool` | `false` | no | +| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | +| [region](#input\_region) | ID element \_(Rarely used, not included by default)\_. Usually an abbreviation of the selected AWS region e.g. 'uw2', 'ew2' or 'gbl' for resources like IAM roles that have no region | `string` | `null` | no | +| [role\_description](#input\_role\_description) | Description of IAM role to use for EventBridge | `string` | `null` | no | +| [role\_force\_detach\_policies](#input\_role\_force\_detach\_policies) | Specifies to force detaching any policies the IAM role has before destroying it. | `bool` | `true` | no | +| [role\_name](#input\_role\_name) | Name of IAM role to use for EventBridge | `string` | `null` | no | +| [role\_path](#input\_role\_path) | Path of IAM role to use for EventBridge | `string` | `null` | no | +| [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.

The type should really be

map(object({
name\_prefix = optional(string)
description = optional(string)
event\_pattern = optional(string)
schedule\_expression = optional(string)
role\_arn = optional(bool) # the underlying module uses the role created by the wrapped module if true, or null if false
enabled = optional(bool)
state = optional(string)
force\_destroy = optional(bool)
}))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `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.

Names are derived from the object keys and cannot be overridden.

The type should really be

map(object({
tags = optional(map(string), {})
}))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `any` | `{}` | no | +| [schedules](#input\_schedules) | A map of objects with EventBridge Schedule definitions.

The type should really be

map(object({
arn = string
schedule\_expression = string
name\_prefix = optional(string)
description = optional(string)
group\_name = optional(string)
start\_date = optional(string)
end\_date = optional(string)
kms\_key\_arn = string
timezone = optional(string)
state = optional(bool, true) # the underlying module turns this into "ENABLED" if true or "DISABLED" if false
maximum\_window\_in\_minutes = optional(number)
use\_flexible\_time\_window = optional(bool, false)
role\_arn = optional(string)
input = optional(string)
dead\_letter\_arn = optional(string)
ecs\_parameters = optional(any)
eventbridge\_parameters = optional(any)
partition\_key = optional(string)
sagemaker\_pipeline\_parameters = optional(any)
message\_group\_id = optional(string)
retry\_policy = optional(any)
}))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `map(any)` | `{}` | no | +| [schemas\_discoverer\_description](#input\_schemas\_discoverer\_description) | Default schemas discoverer description | `string` | `"Auto schemas discoverer event"` | no | +| [service](#input\_service) | ID element. Usually an abbreviation of your service directorate name, e.g. 'bcss' or 'csms', to help ensure generated IDs are globally unique | `string` | `null` | no | +| [service\_category](#input\_service\_category) | The tag service\_category | `string` | `"n/a"` | 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\_kms\_arns](#input\_sns\_kms\_arns) | Specific customer-managed KMS key ARNs used by SNS targets; required when attach\_sns\_policy is enabled | `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 | +| [stack](#input\_stack) | ID element. The name of the stack/component, e.g. `database`, `web`, `waf`, `eks` | `string` | `null` | no | +| [tag\_version](#input\_tag\_version) | Used to identify the tagging version in use | `string` | `"1.0"` | no | +| [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no | +| [targets](#input\_targets) | A map of objects with EventBridge Target definitions.

The type should really be

map(list(object({
name = string
arn = optional(string)
destination = optional(string)
target\_id = optional(string)
input = optional(string)
input\_path = optional(string)
force\_destroy = optional(bool)
attach\_role\_arn = optional(bool)
run\_command\_targets = optional(any)
ecs\_target = optional(any)
batch\_target = optional(any)
partition\_key\_path = optional(string)
message\_group\_id = optional(string)
http\_target = optional(any)
appsync\_target = optional(any)
input\_transformer = optional(any)
dead\_letter\_arn = optional(string)
retry\_policy = optional(any)
})))

but it causes problems in the community module when Terraform sets
omitted fields to null. | `any` | `{}` | no | +| [terraform\_source](#input\_terraform\_source) | Source location to record in the Terraform\_source tag. Defaults to the caller module path when not set. | `string` | `null` | no | +| [tool](#input\_tool) | The tool used to deploy the resource | `string` | `"Terraform"` | no | +| [trusted\_entities](#input\_trusted\_entities) | Additional trusted entities for assuming roles (trust relationship) | `list(string)` | `[]` | no | +| [workspace](#input\_workspace) | ID element. The Terraform workspace, to help ensure generated IDs are unique across workspaces | `string` | `null` | no | + +## Outputs + +| Name | Description | +| ---- | ----------- | +| [eventbridge\_api\_destination\_arns](#output\_eventbridge\_api\_destination\_arns) | The EventBridge API Destination ARNs | +| [eventbridge\_api\_destinations](#output\_eventbridge\_api\_destinations) | The EventBridge API Destinations created and their attributes | +| [eventbridge\_archive\_arns](#output\_eventbridge\_archive\_arns) | The EventBridge Archive ARNs | +| [eventbridge\_archives](#output\_eventbridge\_archives) | The EventBridge Archives created and their attributes | +| [eventbridge\_bus](#output\_eventbridge\_bus) | The EventBridge Bus created and their attributes | +| [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 | +| [eventbridge\_connection\_ids](#output\_eventbridge\_connection\_ids) | The EventBridge Connection IDs | +| [eventbridge\_connections](#output\_eventbridge\_connections) | The EventBridge Connections created and their attributes | +| [eventbridge\_iam\_roles](#output\_eventbridge\_iam\_roles) | The EventBridge IAM roles created and their attributes | +| [eventbridge\_log\_delivery\_source\_arn](#output\_eventbridge\_log\_delivery\_source\_arn) | The EventBridge Bus CloudWatch Log Delivery Source ARN | +| [eventbridge\_log\_delivery\_source\_name](#output\_eventbridge\_log\_delivery\_source\_name) | The EventBridge Bus CloudWatch Log Delivery Source Name | +| [eventbridge\_permission\_ids](#output\_eventbridge\_permission\_ids) | The EventBridge Permission IDs | +| [eventbridge\_permissions](#output\_eventbridge\_permissions) | The EventBridge Permissions created and their attributes | +| [eventbridge\_pipe\_arns](#output\_eventbridge\_pipe\_arns) | The EventBridge Pipes ARNs | +| [eventbridge\_pipe\_ids](#output\_eventbridge\_pipe\_ids) | The EventBridge Pipes IDs | +| [eventbridge\_pipe\_role\_arns](#output\_eventbridge\_pipe\_role\_arns) | The ARNs of the IAM role created for EventBridge Pipes | +| [eventbridge\_pipe\_role\_names](#output\_eventbridge\_pipe\_role\_names) | The names of the IAM role created for EventBridge Pipes | +| [eventbridge\_pipes](#output\_eventbridge\_pipes) | The EventBridge Pipes created and their attributes | +| [eventbridge\_pipes\_iam\_roles](#output\_eventbridge\_pipes\_iam\_roles) | The EventBridge Pipes IAM roles created and their attributes | +| [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 | +| [eventbridge\_rule\_ids](#output\_eventbridge\_rule\_ids) | The EventBridge Rule IDs | +| [eventbridge\_rules](#output\_eventbridge\_rules) | The EventBridge Rules created and their attributes | +| [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\_groups](#output\_eventbridge\_schedule\_groups) | The EventBridge Schedule Groups created and their attributes | +| [eventbridge\_schedule\_ids](#output\_eventbridge\_schedule\_ids) | The EventBridge Schedule IDs created | +| [eventbridge\_schedules](#output\_eventbridge\_schedules) | The EventBridge Schedules created and their attributes | +| [eventbridge\_targets](#output\_eventbridge\_targets) | The EventBridge Targets created and their attributes | + + + diff --git a/infrastructure/modules/eventbridge/context.tf b/infrastructure/modules/eventbridge/context.tf new file mode 100644 index 00000000..e934a84f --- /dev/null +++ b/infrastructure/modules/eventbridge/context.tf @@ -0,0 +1,376 @@ +# tflint-ignore-file: terraform_standard_module_structure, terraform_unused_declarations +# +# ONLY EDIT THIS FILE IN github.com/NHSDigital/screening-terraform-modules-aws/infrastructure/modules/tags +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/NHSDigital/screening-terraform-modules-aws/blob/master/infrastructure/modules/tags/exports/context.tf +# and then place it in your Terraform module to automatically get +# tag module standard configuration inputs suitable for passing +# to other modules. +# +# curl -sL https://raw.githubusercontent.com/NHSDigital/screening-terraform-modules-aws/master/infrastructure/modules/tags/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "../tags" + + enabled = var.enabled + service = var.service + project = var.project + region = var.region + environment = var.environment + stack = var.stack + workspace = var.workspace + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + terraform_source = coalesce(var.terraform_source, path.module) + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of screening-terraform-modules-aws/tags/variables.tf here +# tflint-ignore: terraform_unused_declarations +variable "aws_region" { + type = string + description = "The AWS region" + default = "eu-west-2" + validation { + condition = contains(["eu-west-1", "eu-west-2", "us-east-1"], var.aws_region) + error_message = "AWS Region must be one of eu-west-1, eu-west-2, us-east-1" + } +} + +variable "context" { + type = any + default = { + enabled = true + service = null + project = null + region = null + environment = null + stack = null + workspace = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + terraform_source = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "terraform_source" { + type = string + default = null + description = "Source location to record in the Terraform_source tag. Defaults to the caller module path when not set." +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "service" { + type = string + default = null + description = "ID element. Usually an abbreviation of your service directorate name, e.g. 'bcss' or 'csms', to help ensure generated IDs are globally unique" +} + +variable "region" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. Usually an abbreviation of the selected AWS region e.g. 'uw2', 'ew2' or 'gbl' for resources like IAM roles that have no region" +} + +variable "project" { + type = string + default = null + description = "ID element. A project identifier, indicating the name or role of the project the resource is for, such as `website` or `api`" +} +variable "stack" { + type = string + default = null + description = "ID element. The name of the stack/component, e.g. `database`, `web`, `waf`, `eks`" +} +variable "workspace" { + type = string + default = null + description = "ID element. The Terraform workspace, to help ensure generated IDs are unique across workspaces" +} +variable "environment" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prd', 'dev', 'test', 'preprod', 'prod', 'uat'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +variable "owner" { + type = string + description = "The name and or NHS.net email address of the service owner" + default = "None" +} + +variable "tag_version" { + type = string + description = "Used to identify the tagging version in use" + default = "1.0" +} + +variable "data_classification" { + type = string + description = "Used to identify the data classification of the resource, e.g 1-5" + default = "n/a" + validation { + condition = contains(["n/a", "1", "2", "3", "4", "5"], var.data_classification) + error_message = "Data Classification must be \"n/a\" or between 1-5" + } +} + +variable "data_type" { + type = string + description = "The tag data_type" + default = "None" + validation { + condition = contains(["None", "PCD", "PID", "Anonymised", "UserAccount", "Audit"], var.data_type) + error_message = "Data Type must be one of None, PCD, PID, Anonymised, UserAccount, Audit" + } +} + + +variable "public_facing" { + type = bool + description = "Whether this resource is public facing" + default = false +} + +variable "service_category" { + type = string + description = "The tag service_category" + default = "n/a" + validation { + condition = contains(["n/a", "Bronze", "Silver", "Gold", "Platinum"], var.service_category) + error_message = "The Service Category must be one of n/a, Bronze, Silver, Gold, Platinum" + } +} +variable "on_off_pattern" { + type = string + description = "Used to turn resources on and off based on a time pattern" + default = "n/a" +} + +variable "application_role" { + type = string + description = "The role the application is performing" + default = "General" +} + +variable "tool" { + type = string + description = "The tool used to deploy the resource" + default = "Terraform" +} + +#### End of copy of screening-terraform-modules-aws/tags/variables.tf diff --git a/infrastructure/modules/eventbridge/locals.tf b/infrastructure/modules/eventbridge/locals.tf new file mode 100644 index 00000000..afd037fd --- /dev/null +++ b/infrastructure/modules/eventbridge/locals.tf @@ -0,0 +1,56 @@ +locals { + # allow bus name to be overridden without touching tags + bus_name = ( + # `module.this.id` is `""` (an error) when `module.this.enabled` is false + # `coalesce()` errors if all arguments are null + module.this.enabled + ? coalesce( + var.bus_name, + module.this.enabled ? module.this.id : null, + ) + : null + ) + + # log delivery names must be unique per AWS account + # provide a default name based on the module ID + log_delivery = { + for k, v in var.log_delivery : k => merge( + { + name = "${module.this.id}-${k}" + }, + { for attribute, value in v : attribute => value if value != null } + ) + } + + # schedule group names must be unique per AWS account and region + # prefix provided names with the module ID to ensure uniqueness + # validation prevents explicitly setting `name` or `name_prefix` + schedule_groups = { + for k, v in var.schedule_groups : k => ( + merge(v, { name = "${module.this.id}-${k}" }) + ) + } + + # if a schedule gives a group_name, fix it to match the corresponding + # name in schedule_groups + schedules = { + for k, v in var.schedules : k => ( + try(local.schedule_groups[v.group_name], null) != null # either property could be absent + ? merge( + v, + { + group_name = local.schedule_groups[v.group_name].name + } + ) + : v + ) + } + + # pipe names must be unique per AWS account and region + # prefix keys with the module ID to ensure uniqueness + pipes = { + for k, v in var.pipes : "${module.this.id}-${k}" => { + for attribute, value in v : attribute => value if value != null + } + } +} diff --git a/infrastructure/modules/eventbridge/main.tf b/infrastructure/modules/eventbridge/main.tf new file mode 100644 index 00000000..8324e018 --- /dev/null +++ b/infrastructure/modules/eventbridge/main.tf @@ -0,0 +1,96 @@ +################################################################ +# EventBridge +# +# Thin NHS wrapper around the community EventBridge module: +# * Customer-managed KMS keys are required for encrypted resources. +# * SNS KMS permissions are restricted to explicitly approved keys. +# * Resource names and tags are derived from context.tf. +# * Creation is gated by module.this.enabled. +# * Cross-variable input constraints are enforced in validations.tf. +# * Connections have been removed from this wrapper, because the +# underlying community module stores them in full in the terraform +# state, including credentials. +################################################################ + +module "eventbridge" { + source = "git::https://github.com/terraform-aws-modules/terraform-aws-eventbridge.git?ref=f9934726324c988f823682884b4fa003586a7b6f" # v4.3.2 + + create = module.this.enabled + tags = module.this.tags + + create_role = var.create_role + create_pipe_role_only = var.create_pipe_role_only + append_rule_postfix = var.append_rule_postfix + append_destination_postfix = var.append_destination_postfix + append_schedule_group_postfix = var.append_schedule_group_postfix + append_schedule_postfix = var.append_schedule_postfix + append_pipe_postfix = var.append_pipe_postfix + create_bus = var.create_bus + create_rules = var.create_rules + create_targets = var.create_targets + create_permissions = var.create_permissions + create_archives = var.create_archives + create_schedule_groups = var.create_schedule_groups + create_schedules = var.create_schedules + create_pipes = var.create_pipes + create_log_delivery_source = var.create_log_delivery_source + create_log_delivery = var.create_log_delivery + region = var.aws_region + bus_name = local.bus_name + bus_description = var.bus_description + log_config = var.log_config + log_delivery = local.log_delivery + log_delivery_source_name = var.log_delivery_source_name + event_source_name = var.event_source_name + kms_key_identifier = var.kms_key_identifier + dead_letter_config = var.dead_letter_config + schemas_discoverer_description = var.schemas_discoverer_description + rules = var.rules + targets = var.targets + archives = var.archives + permissions = var.permissions + schedule_groups = local.schedule_groups + schedules = local.schedules + pipes = local.pipes + schedule_group_timeouts = var.schedule_group_timeouts + role_name = var.role_name + role_description = var.role_description + role_path = var.role_path + policy_path = var.policy_path + role_force_detach_policies = var.role_force_detach_policies + role_permissions_boundary = var.role_permissions_boundary + role_tags = merge(var.role_tags, module.this.tags) + ecs_pass_role_resources = var.ecs_pass_role_resources + attach_kinesis_policy = var.attach_kinesis_policy + attach_kinesis_firehose_policy = var.attach_kinesis_firehose_policy + attach_sqs_policy = var.attach_sqs_policy + attach_sns_policy = var.attach_sns_policy + attach_ecs_policy = var.attach_ecs_policy + attach_lambda_policy = var.attach_lambda_policy + attach_sfn_policy = var.attach_sfn_policy + attach_cloudwatch_policy = var.attach_cloudwatch_policy + attach_api_destination_policy = var.attach_api_destination_policy + attach_tracing_policy = var.attach_tracing_policy + kinesis_target_arns = var.kinesis_target_arns + kinesis_firehose_target_arns = var.kinesis_firehose_target_arns + sqs_target_arns = var.sqs_target_arns + sns_target_arns = var.sns_target_arns + sns_kms_arns = var.sns_kms_arns + ecs_target_arns = var.ecs_target_arns + lambda_target_arns = var.lambda_target_arns + sfn_target_arns = var.sfn_target_arns + cloudwatch_target_arns = var.cloudwatch_target_arns + attach_policy_json = var.attach_policy_json + attach_policy_jsons = var.attach_policy_jsons + attach_policy = var.attach_policy + attach_policies = var.attach_policies + number_of_policy_jsons = var.number_of_policy_jsons + number_of_policies = var.number_of_policies + attach_policy_statements = var.attach_policy_statements + trusted_entities = var.trusted_entities + policy_json = var.policy_json + policy_jsons = var.policy_jsons + policy = var.policy + policies = var.policies + policy_statements = var.policy_statements +} diff --git a/infrastructure/modules/eventbridge/outputs.tf b/infrastructure/modules/eventbridge/outputs.tf new file mode 100644 index 00000000..33dfc38f --- /dev/null +++ b/infrastructure/modules/eventbridge/outputs.tf @@ -0,0 +1,170 @@ +output "eventbridge_bus_name" { + description = "The EventBridge Bus Name" + value = module.eventbridge.eventbridge_bus_name +} + +output "eventbridge_bus_arn" { + description = "The EventBridge Bus ARN" + value = module.eventbridge.eventbridge_bus_arn +} + +output "eventbridge_archive_arns" { + description = "The EventBridge Archive ARNs" + value = module.eventbridge.eventbridge_archive_arns +} + +output "eventbridge_permission_ids" { + description = "The EventBridge Permission IDs" + value = module.eventbridge.eventbridge_permission_ids +} + +output "eventbridge_connection_ids" { + description = "The EventBridge Connection IDs" + value = module.eventbridge.eventbridge_connection_ids +} + +output "eventbridge_connection_arns" { + description = "The EventBridge Connection Arns" + value = module.eventbridge.eventbridge_connection_arns +} + +output "eventbridge_api_destination_arns" { + description = "The EventBridge API Destination ARNs" + value = module.eventbridge.eventbridge_api_destination_arns +} + +output "eventbridge_rule_ids" { + description = "The EventBridge Rule IDs" + value = module.eventbridge.eventbridge_rule_ids +} + +output "eventbridge_rule_arns" { + description = "The EventBridge Rule ARNs" + value = module.eventbridge.eventbridge_rule_arns +} + +output "eventbridge_schedule_group_ids" { + description = "The EventBridge Schedule Group IDs" + value = module.eventbridge.eventbridge_schedule_group_ids +} + +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 +} + +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 +} + +output "eventbridge_role_arn" { + description = "The ARN of the IAM role created for EventBridge" + value = module.eventbridge.eventbridge_role_arn +} + +output "eventbridge_role_name" { + description = "The name of the IAM role created for EventBridge" + value = module.eventbridge.eventbridge_role_name +} + +output "eventbridge_pipe_ids" { + description = "The EventBridge Pipes IDs" + value = module.eventbridge.eventbridge_pipe_ids +} + +output "eventbridge_pipe_arns" { + description = "The EventBridge Pipes ARNs" + value = module.eventbridge.eventbridge_pipe_arns +} + +output "eventbridge_pipe_role_arns" { + description = "The ARNs of the IAM role created for EventBridge Pipes" + value = module.eventbridge.eventbridge_pipe_role_arns +} + +output "eventbridge_pipe_role_names" { + description = "The names of the IAM role created for EventBridge Pipes" + value = module.eventbridge.eventbridge_pipe_role_names +} + +output "eventbridge_bus" { + description = "The EventBridge Bus created and their attributes" + value = module.eventbridge.eventbridge_bus +} + +output "eventbridge_archives" { + description = "The EventBridge Archives created and their attributes" + value = module.eventbridge.eventbridge_archives +} + +output "eventbridge_permissions" { + description = "The EventBridge Permissions created and their attributes" + value = module.eventbridge.eventbridge_permissions +} + +output "eventbridge_connections" { + description = "The EventBridge Connections created and their attributes" + value = module.eventbridge.eventbridge_connections + sensitive = true +} + +output "eventbridge_api_destinations" { + description = "The EventBridge API Destinations created and their attributes" + value = module.eventbridge.eventbridge_api_destinations +} + +output "eventbridge_targets" { + description = "The EventBridge Targets created and their attributes" + value = module.eventbridge.eventbridge_targets +} + +output "eventbridge_rules" { + description = "The EventBridge Rules created and their attributes" + value = module.eventbridge.eventbridge_rules +} + +output "eventbridge_schedule_groups" { + description = "The EventBridge Schedule Groups created and their attributes" + value = module.eventbridge.eventbridge_schedule_groups +} + +output "eventbridge_schedules" { + description = "The EventBridge Schedules created and their attributes" + value = module.eventbridge.eventbridge_schedules +} + +output "eventbridge_pipes" { + description = "The EventBridge Pipes created and their attributes" + value = module.eventbridge.eventbridge_pipes +} + +output "eventbridge_log_delivery_source_arn" { + description = "The EventBridge Bus CloudWatch Log Delivery Source ARN" + value = module.eventbridge.eventbridge_log_delivery_source_arn +} + +output "eventbridge_log_delivery_source_name" { + description = "The EventBridge Bus CloudWatch Log Delivery Source Name" + value = module.eventbridge.eventbridge_log_delivery_source_name +} + +output "eventbridge_pipes_iam_roles" { + description = "The EventBridge Pipes IAM roles created and their attributes" + value = module.eventbridge.eventbridge_pipes_iam_roles +} + +output "eventbridge_iam_roles" { + description = "The EventBridge IAM roles created and their attributes" + value = module.eventbridge.eventbridge_iam_roles +} diff --git a/infrastructure/modules/eventbridge/validations.tf b/infrastructure/modules/eventbridge/validations.tf new file mode 100644 index 00000000..f2b13fbd --- /dev/null +++ b/infrastructure/modules/eventbridge/validations.tf @@ -0,0 +1,24 @@ +resource "terraform_data" "validations" { + count = module.this.enabled ? 1 : 0 + + lifecycle { + precondition { + condition = anytrue([ + !var.create_role, + !var.attach_sns_policy, + length(var.sns_kms_arns) > 0, + ]) + + error_message = "attach_sns_policy requires at least one specific sns_kms_arns entry." + } + + precondition { + condition = anytrue([ + !var.create_bus, + try(trimspace(var.kms_key_identifier) != "", false), + ]) + + error_message = "kms_key_identifier must not be empty when creating the bus" + } + } +} diff --git a/infrastructure/modules/eventbridge/variables.tf b/infrastructure/modules/eventbridge/variables.tf new file mode 100644 index 00000000..29606d17 --- /dev/null +++ b/infrastructure/modules/eventbridge/variables.tf @@ -0,0 +1,712 @@ +################################################################ +# Creation controls +################################################################ + +variable "create_role" { + description = "Controls whether IAM roles should be created" + type = bool + default = true +} + +variable "create_pipe_role_only" { + description = "Controls whether an IAM role should be created for the pipes only" + type = bool + default = false +} + +variable "create_bus" { + description = "Controls whether EventBridge Bus resource should be created" + type = bool + default = true +} + +variable "create_rules" { + description = "Controls whether EventBridge Rule resources should be created" + type = bool + default = true +} + +variable "create_targets" { + description = "Controls whether EventBridge Target resources should be created" + type = bool + default = true +} + +variable "create_permissions" { + description = "Controls whether EventBridge Permission resources should be created" + type = bool + default = true +} + +variable "create_archives" { + description = "Controls whether EventBridge Archive resources should be created" + type = bool + 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 "create_pipes" { + description = "Controls whether EventBridge Pipes resources should be created" + type = bool + default = true +} + +variable "create_log_delivery_source" { + description = "Controls whether EventBridge log delivery source resource should be created" + type = bool + default = true +} + +variable "create_log_delivery" { + description = "Controls whether EventBridge log delivery resources should be created" + type = bool + default = true +} + +################################################################ +# Naming +################################################################ + +variable "append_rule_postfix" { + description = "Controls whether to append '-rule' to the name of the rule" + type = bool + default = true +} + +variable "append_destination_postfix" { + description = "Controls whether to append '-destination' to the name of the destination" + type = bool + 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 "append_pipe_postfix" { + description = "Controls whether to append '-pipe' to the name of the pipe" + type = bool + default = true +} + +################################################################ +# Bus and logging +################################################################ + +variable "bus_name" { + description = "A unique name for your EventBridge Bus. Must be unique per AWS account and region. Defaults to whatever the tags module produces" + type = string + nullable = true + default = null + + validation { + condition = try(trimspace(var.bus_name), "null") != "" + error_message = "bus_name must not be empty" + } +} + +variable "bus_description" { + description = "Event bus description" + type = string + default = null +} + +variable "log_config" { + description = "The configuration block for the EventBridge bus log config settings" + type = object({ + include_detail = string + level = string + }) + default = null +} + +variable "log_delivery" { + description = "Map of the configuration block for the EventBridge bus log delivery settings (key is the type of log delivery: cloudwatch_logs, s3, firehose)" + type = map(object({ + enabled = optional(bool, true) + destination_arn = string + source_name = optional(string) + name = optional(string) + output_format = optional(string) + field_delimiter = optional(string) + record_fields = optional(list(string)) + s3_delivery_configuration = optional(object({ + enable_hive_compatible_path = optional(bool) + suffix_path = optional(string) + })) + })) + default = {} +} + +variable "log_delivery_source_name" { + description = "Name of log delivery source; defaults to the name we use for the bus" + type = string + default = null +} + +variable "event_source_name" { + description = "The partner event source that the new event bus will be matched with. Must match name." + type = string + default = null +} + +variable "kms_key_identifier" { + description = "The identifier of the AWS KMS customer managed key for EventBridge to use, to encrypt events on this event bus. The identifier can be the key Amazon Resource Name (ARN), KeyId, key alias, or key alias ARN." + type = string + nullable = false +} + +variable "dead_letter_config" { + description = <<-EOF + Configuration details of the Amazon SQS queue for EventBridge to use as a + dead-letter queue (DLQ). + + The type should really be + + object({ + arn = optional(string) + }) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = any + default = {} +} + +variable "schemas_discoverer_description" { + description = "Default schemas discoverer description" + type = string + default = "Auto schemas discoverer event" +} + +################################################################ +# EventBridge resources +################################################################ + +variable "rules" { + description = <<-EOF + A map of objects with EventBridge Rule definitions. + + The type should really be + + map(object({ + name_prefix = optional(string) + description = optional(string) + event_pattern = optional(string) + schedule_expression = optional(string) + role_arn = optional(bool) # the underlying module uses the role created by the wrapped module if true, or null if false + enabled = optional(bool) + state = optional(string) + force_destroy = optional(bool) + })) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = map(any) + default = {} +} + +variable "targets" { + description = <<-EOF + A map of objects with EventBridge Target definitions. + + The type should really be + + map(list(object({ + name = string + arn = optional(string) + destination = optional(string) + target_id = optional(string) + input = optional(string) + input_path = optional(string) + force_destroy = optional(bool) + attach_role_arn = optional(bool) + run_command_targets = optional(any) + ecs_target = optional(any) + batch_target = optional(any) + partition_key_path = optional(string) + message_group_id = optional(string) + http_target = optional(any) + appsync_target = optional(any) + input_transformer = optional(any) + dead_letter_arn = optional(string) + retry_policy = optional(any) + }))) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = any + default = {} +} + +variable "archives" { + description = <<-EOF + A map of objects with the EventBridge Archive definitions. + + The type should really be + + map(object({ + name = optional(string) + event_source_arn = optional(string) + description = optional(string) + event_pattern = optional(string) + retention_days = optional(number) + kms_key_identifier = string + })) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = map(any) + default = {} + + validation { + condition = alltrue([ + for archive in var.archives : + try(trimspace(archive.kms_key_identifier), "") != "" + ]) + + error_message = "Each archive must specify a non-empty kms_key_identifier." + } +} + +variable "permissions" { + description = <<-EOF + A map of objects with EventBridge Permission definitions. + + The type should really be + + map(object({ + action = optional(string) + event_bus_name = optional(string) + condition_org = optional(string) + })) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = map(any) + default = {} +} + +variable "schedule_groups" { + description = <<-EOF + A map of objects with EventBridge Schedule Group definitions. + + Names are derived from the object keys and cannot be overridden. + + The type should really be + + map(object({ + tags = optional(map(string), {}) + })) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = any + default = {} + + validation { + condition = alltrue([ + for k, v in var.schedule_groups : + try( + !( + contains(keys(v), "name") || contains(keys(v), "name_prefix") + ), + false, + ) + ]) + + error_message = <<-EOF + To ensure uniqueness, we disallow setting the `name` or `name_prefix` + attributes in `schedule_groups`. + EOF + } +} + +variable "schedules" { + description = <<-EOF + A map of objects with EventBridge Schedule definitions. + + The type should really be + + map(object({ + arn = string + schedule_expression = string + name_prefix = optional(string) + description = optional(string) + group_name = optional(string) + start_date = optional(string) + end_date = optional(string) + kms_key_arn = string + timezone = optional(string) + state = optional(bool, true) # the underlying module turns this into "ENABLED" if true or "DISABLED" if false + maximum_window_in_minutes = optional(number) + use_flexible_time_window = optional(bool, false) + role_arn = optional(string) + input = optional(string) + dead_letter_arn = optional(string) + ecs_parameters = optional(any) + eventbridge_parameters = optional(any) + partition_key = optional(string) + sagemaker_pipeline_parameters = optional(any) + message_group_id = optional(string) + retry_policy = optional(any) + })) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = map(any) + default = {} + + validation { + condition = alltrue([ + for schedule in var.schedules : + try(trimspace(schedule.kms_key_arn), "") != "" + ]) + + error_message = "Each schedule must specify a non-empty kms_key_arn." + } +} + +variable "pipes" { + description = <<-EOF + A map of EventBridge Pipe definitions. + + The type should really be + + map(object({ + role_arn = optional(string) + source = string + target = string + kms_key_identifier = string + description = optional(string) + desired_state = optional(string) + source_parameters = optional(any) + target_parameters = optional(any) + enrichment = optional(string) + enrichment_parameters = optional(any) + log_configuration = optional(any) + tags = optional(map(string), {}) + })) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = any + default = {} + + validation { + condition = alltrue([ + for pipe in var.pipes : + try(trimspace(pipe.kms_key_identifier), "") != "" + ]) + + error_message = "Each pipe must specify a non-empty kms_key_identifier." + } +} + +variable "schedule_group_timeouts" { + description = "A map of objects with EventBridge Schedule Group create and delete timeouts." + type = map(string) + default = {} +} + +################################################################ +# IAM role +################################################################ + +variable "role_name" { + description = "Name of IAM role to use for EventBridge" + type = string + default = null +} + +variable "role_description" { + description = "Description of IAM role to use for EventBridge" + type = string + default = null +} + +variable "role_path" { + description = "Path of IAM role to use for EventBridge" + type = string + default = null +} + +variable "policy_path" { + description = "Path of IAM policy to use for EventBridge" + type = string + default = null +} + +variable "role_force_detach_policies" { + description = "Specifies to force detaching any policies the IAM role has before destroying it." + type = bool + default = true +} + +variable "role_permissions_boundary" { + description = "The ARN of the policy that is used to set the permissions boundary for the IAM role used by EventBridge" + type = string + default = null +} + +variable "role_tags" { + description = "A map of tags to assign to IAM role" + type = map(string) + default = {} +} + +variable "ecs_pass_role_resources" { + description = "List of approved roles to be passed" + type = list(string) + default = [] +} + +################################################################ +# Target policies +################################################################ + +variable "attach_kinesis_policy" { + description = "Controls whether the Kinesis policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_kinesis_firehose_policy" { + description = "Controls whether the Kinesis Firehose policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_sqs_policy" { + description = "Controls whether the SQS policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_sns_policy" { + description = "Controls whether the SNS policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_ecs_policy" { + description = "Controls whether the ECS policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_lambda_policy" { + description = "Controls whether the Lambda Function policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_sfn_policy" { + description = "Controls whether the StepFunction policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_cloudwatch_policy" { + description = "Controls whether the Cloudwatch policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_api_destination_policy" { + description = "Controls whether the API Destination policy should be added to IAM role for EventBridge Target" + type = bool + default = false +} + +variable "attach_tracing_policy" { + description = "Controls whether X-Ray tracing policy should be added to IAM role for EventBridge" + type = bool + default = false +} + +variable "kinesis_target_arns" { + description = "The Amazon Resource Name (ARN) of the Kinesis Streams you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "kinesis_firehose_target_arns" { + description = "The Amazon Resource Name (ARN) of the Kinesis Firehose Delivery Streams you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "sqs_target_arns" { + description = "The Amazon Resource Name (ARN) of the AWS SQS Queues you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "sns_target_arns" { + description = "The Amazon Resource Name (ARN) of the AWS SNS's you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "sns_kms_arns" { + description = "Specific customer-managed KMS key ARNs used by SNS targets; required when attach_sns_policy is enabled" + type = list(string) + default = [] + + validation { + condition = alltrue([ + for arn in var.sns_kms_arns : + can(regex("^arn:[^:]+:kms:[^:]+:[0-9]{12}:key/[A-Za-z0-9-]+$", arn)) + ]) + error_message = "sns_kms_arns must contain only specific KMS key ARNs (not aliases or wildcards)." + } +} + +variable "ecs_target_arns" { + description = "The Amazon Resource Name (ARN) of the AWS ECS Tasks you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "lambda_target_arns" { + description = "The Amazon Resource Name (ARN) of the Lambda Functions you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "sfn_target_arns" { + description = "The Amazon Resource Name (ARN) of the StepFunctions you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "cloudwatch_target_arns" { + description = "The Amazon Resource Name (ARN) of the Cloudwatch Log Streams you want to use as EventBridge targets" + type = list(string) + default = [] +} + +variable "attach_policy_json" { + description = "Controls whether policy_json should be added to IAM role" + type = bool + default = false +} + +variable "attach_policy_jsons" { + description = "Controls whether policy_jsons should be added to IAM role" + type = bool + default = false +} + +variable "attach_policy" { + description = "Controls whether policy should be added to IAM role" + type = bool + default = false +} + +variable "attach_policies" { + description = "Controls whether list of policies should be added to IAM role" + type = bool + default = false +} + +variable "number_of_policy_jsons" { + description = "Number of policies JSON to attach to IAM role" + type = number + default = 0 +} + +variable "number_of_policies" { + description = "Number of policies to attach to IAM role" + type = number + default = 0 +} + +variable "attach_policy_statements" { + description = "Controls whether policy_statements should be added to IAM role" + type = bool + default = false +} + +variable "trusted_entities" { + description = "Additional trusted entities for assuming roles (trust relationship)" + type = list(string) + default = [] +} + +variable "policy_json" { + description = "An additional policy document as JSON to attach to IAM role" + type = string + default = null +} + +variable "policy_jsons" { + description = "List of additional policy documents as JSON to attach to IAM role" + type = list(string) + default = [] +} + +variable "policy" { + description = "An additional policy document ARN to attach to IAM role" + type = string + default = null +} + +variable "policies" { + description = "List of policy statements ARN to attach to IAM role" + type = list(string) + default = [] +} + +variable "policy_statements" { + description = <<-EOF + Map of dynamic policy statements to attach to IAM role + + The type should really be + + map(object({ + sid = optional(string) + effect = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(any) + not_principals = optional(any) + condition = optional(any) + })) + + but it causes problems in the community module when Terraform sets + omitted fields to null. + EOF + type = any + default = {} +} diff --git a/infrastructure/modules/eventbridge/versions.tf b/infrastructure/modules/eventbridge/versions.tf new file mode 100644 index 00000000..cb30fe5c --- /dev/null +++ b/infrastructure/modules/eventbridge/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.13" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.42" + } + } +} diff --git a/scripts/config/generate-available-modules.yaml b/scripts/config/generate-available-modules.yaml index d7d48418..21784811 100644 --- a/scripts/config/generate-available-modules.yaml +++ b/scripts/config/generate-available-modules.yaml @@ -81,6 +81,10 @@ elasticache: description: "ElastiCache cluster (Redis/Memcached)" wraps: "—" +eventbridge: + description: "AWS EventBridge Resources" + wraps: "terraform-aws-modules/eventbridge/aws" + github-config: description: "GitHub OIDC provider and runner configuration" wraps: "—"