public addTarget()

in packages/aws-cdk-lib/aws-events/lib/rule.ts [153:262]


  public addTarget(target?: IRuleTarget): void {
    if (!target) { return; }

    // Simply increment id for each `addTarget` call. This is guaranteed to be unique.
    const autoGeneratedId = `Target${this.targets.length}`;

    const targetProps = target.bind(this, autoGeneratedId);
    const inputProps = targetProps.input && targetProps.input.bind(this);

    const roleArn = targetProps.role?.roleArn;
    const id = targetProps.id || autoGeneratedId;

    if (targetProps.targetResource) {
      const targetStack = Stack.of(targetProps.targetResource);

      const targetAccount = (targetProps.targetResource as IResource).env?.account || targetStack.account;
      const targetRegion = (targetProps.targetResource as IResource).env?.region || targetStack.region;

      const sourceStack = Stack.of(this);
      const sourceAccount = sourceStack.account;
      const sourceRegion = sourceStack.region;

      // if the target is in a different account or region and is defined in this CDK App
      // we can generate all the needed components:
      // - forwarding rule in the source stack (target: default event bus of the receiver region)
      // - eventbus permissions policy (creating an extra stack)
      // - receiver rule in the target stack (target: the actual target)
      if (!this.sameEnvDimension(sourceAccount, targetAccount) || !this.sameEnvDimension(sourceRegion, targetRegion)) {
        // cross-account and/or cross-region event - strap in, this works differently than regular events!
        // based on:
        // https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cross-account.html

        // for cross-account or cross-region events, we require a concrete target account and region
        if (!targetAccount || Token.isUnresolved(targetAccount)) {
          throw new ValidationError('You need to provide a concrete account for the target stack when using cross-account or cross-region events', this);
        }
        if (!targetRegion || Token.isUnresolved(targetRegion)) {
          throw new ValidationError('You need to provide a concrete region for the target stack when using cross-account or cross-region events', this);
        }
        if (Token.isUnresolved(sourceAccount)) {
          throw new ValidationError('You need to provide a concrete account for the source stack when using cross-account or cross-region events', this);
        }

        // Don't exactly understand why this code was here (seems unlikely this rule would be violated), but
        // let's leave it in nonetheless.
        const sourceApp = this.node.root;
        if (!sourceApp || !App.isApp(sourceApp)) {
          throw new ValidationError('Event stack which uses cross-account or cross-region targets must be part of a CDK app', this);
        }
        const targetApp = Node.of(targetProps.targetResource).root;
        if (!targetApp || !App.isApp(targetApp)) {
          throw new ValidationError('Target stack which uses cross-account or cross-region event targets must be part of a CDK app', this);
        }
        if (sourceApp !== targetApp) {
          throw new ValidationError('Event stack and target stack must belong to the same CDK app', this);
        }

        // The target of this Rule will be the default event bus of the target environment
        this.ensureXEnvTargetEventBus(targetStack, targetAccount, targetRegion, id);

        // The actual rule lives in the target stack. Other than the account, it's identical to this one,
        // but only evaluated at render time (via a special subclass).
        //
        // FIXME: the MirrorRule is a bit silly, forwarding the exact same event to another event bus
        // and trigger on it there (there will be issues with construct references, for example). Especially
        // in the case of scheduled events, we will just trigger both rules in parallel in both environments.
        //
        // A better solution would be to have the source rule add a unique token to the the event,
        // and have the mirror rule trigger on that token only (thereby properly separating triggering, which
        // happens in the source env; and activating, which happens in the target env).
        //
        // Don't have time to do that right now.
        const mirrorRuleScope = this.obtainMirrorRuleScope(targetStack, targetAccount, targetRegion);
        new MirrorRule(mirrorRuleScope, `${Names.uniqueId(this)}-${id}`, {
          targets: [target],
          eventPattern: this.eventPattern,
          schedule: this.scheduleExpression ? Schedule.expression(this.scheduleExpression) : undefined,
          description: this.description,
        }, this);

        return;
      }
    }

    // Here only if the target does not have a targetResource defined.
    // In such case we don't have to generate any extra component.
    // Note that this can also be an imported resource (i.e: EventBus target)

    this.targets.push({
      id,
      arn: targetProps.arn,
      roleArn,
      ecsParameters: targetProps.ecsParameters,
      httpParameters: targetProps.httpParameters,
      kinesisParameters: targetProps.kinesisParameters,
      runCommandParameters: targetProps.runCommandParameters,
      batchParameters: targetProps.batchParameters,
      deadLetterConfig: targetProps.deadLetterConfig,
      retryPolicy: targetProps.retryPolicy,
      sqsParameters: targetProps.sqsParameters,
      redshiftDataParameters: targetProps.redshiftDataParameters,
      appSyncParameters: targetProps.appSyncParameters,
      input: inputProps && inputProps.input,
      inputPath: inputProps && inputProps.inputPath,
      inputTransformer: inputProps?.inputTemplate !== undefined ? {
        inputTemplate: inputProps.inputTemplate,
        inputPathsMap: inputProps.inputPathsMap,
      } : undefined,
    });
  }