mohantyArpit
mohantyArpit

Reputation: 431

How do i add Input transformation to a target using aws cdk for a cloudwatch event rule?

After i create a cloud-watch event rule i am trying to add a target to it but i am unable to add a input transformation. Previously the add target had the props allowed for input transformation but it does not anymore.

codeBuildRule.addTarget(new SnsTopic(props.topic));

The aws cdk page provides this solution but i dont exactly understand what it says

You can add additional targets, with optional input transformer using eventRule.addTarget(target[, input]). For example, we can add a SNS topic target which formats a human-readable message for the commit.

Upvotes: 13

Views: 12706

Answers (4)

Jels Boulangier
Jels Boulangier

Reputation: 854

I ran into the same issue, trying to figure out where to define inputPath and inputTemplate. There's only some CDK abstraction available with one input field (message, event, or input based on the target object). Turns out, the abstraction already contains everything that's required to create inputPath and inputTemplate, it's just a matter of insufficient documentation.

This abstraction is pretty neat because it automatically creates the inputPath and inputTemplate for you when you specify the message (or input, event, ... ) in the CDK target object. For each unique EventField.fromPath() that you use, it creates an inputPath key-value pair for you and uses that key in the inputTemplate.

For myself, I've made it clearer by mimicking the input transformer in the code:

import * as events from 'aws-cdk-lib/aws-events'
import * as targets from 'aws-cdk-lib/aws-events-targets'
...

const eventInputMapping = {
  account: events.EventField.account,
  region: events.EventField.region,
  alarmName: events.EventField.fromPath('$.detail.alarmName'),
  description: events.EventField.fromPath('$.detail.configuration.description'),
  startTime: events.EventField.fromPath('$.detail.state.timestamp'),
  state: events.EventField.fromPath('$.detail.state.value'),
  metric: events.EventField.fromPath('$.detail.configuration.metrics[0].metricStat.metric.name'),
  clusterName: events.EventField.fromPath('$.detail.configuration.metrics[0].metricStat.metric.dimensions.ClusterName'),
  serviceName: events.EventField.fromPath('$.detail.configuration.metrics[0].metricStat.metric.dimensions.ServiceName'),
}

const eventInputTemplate = {
  title: `Service ${eventInputMapping.serviceName} went into ${eventInputMapping.state} state for ${eventInputMapping.metric} ${eventInputMapping.description}.`,
  text: `The ECS scaling alarm ${eventInputMapping.alarmName} got triggered at ${eventInputMapping.startTime}` +
  ` This impacts the scaling of ECS service ${eventInputMapping.serviceName} in ECS cluster ${eventInputMapping.clusterName}. `,
  source_type_name: 'amazon ecs',
  tags: [
    `account-id:${eventInputMapping.account}`,
    `region:${eventInputMapping.region}`,
    `servicename:${eventInputMapping.serviceName}`,
    `clustername:${eventInputMapping.clusterName}`,
  ],
}

new events.Rule(this, 'DefaultEcsMetricsScalingAlarms', {
  ...
  targets: [new targets.ApiDestination(eventsDestination, {
    event: events.RuleTargetInput.fromObject(eventInputTemplate)
  }),
  ],
})

This will result in the following inputPath:

{
  "account": "$.account",
  "detail-alarmName": "$.detail.alarmName",
  "detail-configuration-description": "$.detail.configuration.description",
  "detail-configuration-metrics-0--metricStat-metric-dimensions-ClusterName": "$.detail.configuration.metrics[0].metricStat.metric.dimensions.ClusterName",
  "detail-configuration-metrics-0--metricStat-metric-dimensions-ServiceName": "$.detail.configuration.metrics[0].metricStat.metric.dimensions.ServiceName",
  "detail-configuration-metrics-0--metricStat-metric-name": "$.detail.configuration.metrics[0].metricStat.metric.name",
  "detail-state-timestamp": "$.detail.state.timestamp",
  "detail-state-value": "$.detail.state.value",
  "region": "$.region"
}

and inputTemplate

{
    "title": "Service <detail-configuration-metrics-0--metricStat-metric-dimensions-ServiceName> went into <detail-state-value> state for <detail-configuration-metrics-0--metricStat-metric-name> <detail-configuration-description>.",
    "text": "The ECS scaling alarm <detail-alarmName> got triggered at <detail-state-timestamp> This impacts the scaling of ECS service <detail-configuration-metrics-0--metricStat-metric-dimensions-ServiceName> in ECS cluster <detail-configuration-metrics-0--metricStat-metric-dimensions-ClusterName>. ",
    "source_type_name": "amazon ecs",
    "tags": [
        "account-id:<account>",
        "region:<region>",
        "service:<detail-configuration-metrics-0--metricStat-metric-dimensions-ServiceName>",
        "cluster:<detail-configuration-metrics-0--metricStat-metric-dimensions-ClusterName>"
    ]
}

Upvotes: 0

Anatol Bivol
Anatol Bivol

Reputation: 983

This would work for CDK Python. CodeBuild to SNS notifications.

sns_topic = sns.Topic(...)
codebuild_project = codebuild.Project(...)
sns_topic.grant_publish(codebuild_project)
codebuild_project.on_build_failed(
            f'rule-on-failed',
            target=events_targets.SnsTopic(
                sns_topic,
                message=events.RuleTargetInput.from_multiline_text(
                    f"""
                    Name: {events.EventField.from_path('$.detail.project-name')} 
                    State: {events.EventField.from_path('$.detail.build-status')}
                    Build: {events.EventField.from_path('$.detail.build-id')}
                    Account: {events.EventField.from_path('$.account')}
                """
                )
            )
        )

Credits to @pruthvi-raj comment on an answer above

Upvotes: 1

bravogolfgolf
bravogolfgolf

Reputation: 377

I had the same question trying to implement this tutorial in CDK: Tutorial: Set up a CloudWatch Events rule to receive email notifications for pipeline state changes

I found this helpful as well: Detect and react to changes in pipeline state with Amazon CloudWatch Events

NOTE: I could not get it to work using the Pipeline's class method onStateChange().

I ended up writing a Rule:

const topic = new Topic(this, 'topic', {topicName: 'codepipeline-notes-failure',
});

const description = `Generated by the CDK for stack: ${this.stackName}`;
new Rule(this, 'failed', {
  description: description,
  eventPattern: {
    detail: {state: ['FAILED'], pipeline: ['notes']},
    detailType: ['CodePipeline Pipeline Execution State Change'],
    source: ['aws.codepipeline'],
  },
  targets: [
    new SnsTopic(topic, {
      message: RuleTargetInput.fromText(
        `The Pipeline '${EventField.fromPath('$.detail.pipeline')}' has ${EventField.fromPath(
          '$.detail.state',
        )}`,
      ),
    }),
  ],
});

After implementing, if you navigate to Amazon EventBridge -> Rules, then select the rule, then select the Target(s) and then click View Details you will see the Target Details with the Input transformer & InputTemplate.

Input transformer: {"InputPathsMap":{"detail-pipeline":"$.detail.pipeline","detail-state":"$.detail.state"},"InputTemplate":"\"The Pipeline '<detail-pipeline>' has <detail-state>\""}

Upvotes: 8

jogold
jogold

Reputation: 7397

You should specify the message prop and use RuleTargetInput static methods. Some of these methods can use strings returned by EventField.fromPath():

// From a path
codeBuildRule.addTarget(new SnsTopic(props.topic, {
  message: events.RuleTargetInput.fromEventPath('$.detail')
}));

// Custom object
codeBuildRule.addTarget(new SnsTopic(props.topic, {
  message: RuleTargetInput.fromObject({
    foo: EventField.fromPath('$.detail.bar')
  })
}));

Upvotes: 19

Related Questions