How can i use same cloudwatch logs and same sns target for each scheduled events in terraform?
I have 2 events:
in event-bridge/events/event1/main.tf
module "event1" {
source = "../../rule"
name = "event1"
description = "event1"
schedule_expression = "cron(0 4 * * ? *)"
input = jsonencode({"job-type": "event1"})
cloudwatch_arn = module.targets.cloudwatch_arn
}
module "targets" {
source = "../../targets"
}
in event-bridge/events/event2/main.tf
module "event2" {
source = "../../rule"
name = "event2"
description = "event2"
schedule_expression = "cron(0 5 * * ? *)"
input = jsonencode({"job-type": "event2"})
cloudwatch_arn = module.targets.cloudwatch_arn
}
module "targets" {
source = "../../targets"
}
in event-bridge/targets/main.tf
resource "aws_cloudwatch_log_group" "log_group_target" {
name = "/aws/events/${local.rule_name}"
}
resource "aws_sns_topic" "sns_target" {
name = local.rule_name
}
resource "aws_sns_topic_policy" "sns-target-policy" {
arn = aws_sns_topic.sns_target.arn
policy = data.aws_iam_policy_document.events_to_sns_topic_policy.json
}
data "aws_iam_policy_document" "events_to_sns_topic_policy" {
statement {
effect = "Allow"
actions = ["SNS:Publish"]
principals {
type = "Service"
identifiers = ["events.amazonaws.com"]
}
resources = [aws_sns_topic.sns_target.arn]
}
}
in event-bridge/targets/variables.tf
locals {
rule_name = "scheduled-events"
}
in event-bridge/targets/output.tf
output "cloudwatch_arn" {
value = aws_cloudwatch_log_group.log_group_target.arn
}
output "sns_topic_arn" {
value = aws_sns_topic.sns_target.arn
}
in event-bridge/rule/main.tf
resource "aws_cloudwatch_event_rule" "rule" {
name = var.name
description = var.description
schedule_expression = var.schedule_expression
}
resource "aws_cloudwatch_event_target" "cloud_watch_target" {
rule = local.rule_name
arn = var.cloudwatch_arn
}
resource "aws_cloudwatch_event_target" "sns_target" {
rule = local.rule_name
arn = var.sns_arn
input = var.input
}
in event-bridge/rule/variables.tf
locals {
rule_name = "scheduled-events"
}
variable "name" {
description = "A name of the event bridge rule to be created"
type = string
}
variable "description" {
description = "A description of the event bridge rule to be created"
type = string
}
variable "input" {
description = "A message for the events to be processed by this rule"
type = any
}
variable "schedule_expression" {
description = "A scheduled time interval for the events to be processed"
type = any
}
variable "cloudwatch_arn" {
description = "Logging cloudwatch arn for the events to be processed"
type = any
}
if i plan like this for each event another cloudwatch log group and sns target is created and when apply there is an error due to same name, but i want them to use same cloudwatch log group and sns target.. do you have any idea how i can achieve that? thanks a lot!!
Solution 1:
A resource can only be managed at one address in the state. You are creating two instances of the targets module: module.event1.module.targets
and module.event2.module.targets
.
If you want to use the same values created in one targets module in both events modules, then you must either pass the module outputs in as variables or use data sources in the events modules to reference the targets. In both cases, only create one targets module.
As requested, I am expanding the answer to clarify.
Consider the following directory structure.
- rule/
- main.tf
- variables.tf
- targets/
- main.tf
- outputs.tf
- variables.tf
- ./main.tf
Here main.tf at the root is the provider for the state. Here are the contents of the files, though I haven't tested this.
rule/main.tf
resource "aws_cloudwatch_event_rule" "rule" {
name = var.name
description = var.description
schedule_expression = var.schedule_expression
}
resource "aws_cloudwatch_event_target" "cloud_watch_target" {
rule = aws_cloudwatch_event_rule.rule.name
arn = var.cloudwatch_arn
}
resource "aws_cloudwatch_event_target" "sns_target" {
rule = aws_cloudwatch_event_rule.rule.name
arn = var.sns_topic_arn
input = var.input
}
rule/variables.tf
variable "name" {
description = "A name of the event bridge rule to be created"
type = string
}
variable "description" {
description = "A description of the event bridge rule to be created"
type = string
}
variable "input" {
description = "A message for the events to be processed by this rule"
type = any
}
variable "schedule_expression" {
description = "A scheduled time interval for the events to be processed"
type = string
}
variable "cloudwatch_arn" {
description = "Logging cloudwatch arn for the events to be processed"
type = string
}
variable "sns_topic_arn" {
type = string
}
targets/main.tf
resource "aws_cloudwatch_log_group" "log_group_target" {
name = "/aws/events/${local.rule_name}"
}
resource "aws_sns_topic" "sns_target" {
name = local.rule_name
}
resource "aws_sns_topic_policy" "sns-target-policy" {
arn = aws_sns_topic.sns_target.arn
policy = data.aws_iam_policy_document.events_to_sns_topic_policy.json
}
data "aws_iam_policy_document" "events_to_sns_topic_policy" {
statement {
effect = "Allow"
actions = ["SNS:Publish"]
principals {
type = "Service"
identifiers = ["events.amazonaws.com"]
}
resources = [aws_sns_topic.sns_target.arn]
}
}
targets/outputs.tf
output "cloudwatch_arn" {
value = aws_cloudwatch_log_group.log_group_target.arn
}
output "sns_topic_arn" {
value = aws_sns_topic.sns_target.arn
}
targets/variables.tf
locals {
rule_name = "scheduled-events"
}
the main top-level main.tf
module "targets" {
source = "./targets"
}
module "event1" {
source = "./rule"
name = "event1"
description = "event1"
schedule_expression = "cron(0 4 * * ? *)"
input = jsonencode({ "job-type" : "event1" })
cloudwatch_arn = module.targets.cloudwatch_arn
sns_topic_arn = module.targets.sns_topic_arn
}
module "event2" {
source = "./rule"
name = "event2"
description = "event2"
schedule_expression = "cron(0 5 * * ? *)"
input = jsonencode({ "job-type" : "event2" })
cloudwatch_arn = module.targets.cloudwatch_arn
sns_topic_arn = module.targets.sns_topic_arn
}