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,
});
}