Skill 111 · Amazon Eventbridge Event Bus
Subchapter 111.16
references/target-configuration.mdMarkdown15 KBView on GitHub
The short version: which parameter block goes with which target, and the five rules that prevent most delivery failures.
For anything more than that, load target-contract.md. It carries the validation ownership model, the required-field lists, a complete working example per target type, literal-versus-JSONata rules, the runtime error taxonomy, and a troubleshooting flow.
InvokeConfiguration carries three things: the TargetArn, the RoleArn the service assumes to
invoke it, and at most one parameter block matching the target type. A universal target requires its
block. Every typed block may be omitted, including Kinesis’s: an absent or empty KinesisParameters is
stored, and Kinesis reports the missing PartitionKey when it is called.
The delivery role MUST belong to the account creating the subscriber, and its trust policy MUST allow the service principal to call .
events.amazonaws.comsts:AssumeRoleThat trust policy SHOULD also condition on aws:SourceAccount and aws:SourceArn, so the role can be
assumed only on behalf of your own subscribers rather than by the service acting for anyone:
"Condition": {
"StringEquals": { "aws:SourceAccount": "111122223333" },
"ArnLike": { "aws:SourceArn": "arn:aws:events:us-east-1:111122223333:subscriber/*" }
}The service sets aws:SourceArn to the subscriber ARN and aws:SourceAccount to that subscriber’s
account, so a pattern ending subscriber/* matches every subscriber you own and nothing else. Narrow it to
one subscriber by name if you want the role usable by only that one.
| Block | Target |
|---|---|
SqsParameters | SQS queue, standard or FIFO |
SnsParameters | SNS topic, standard or FIFO |
LambdaParameters | Lambda function |
KinesisParameters | Kinesis stream |
StepFunctionsParameters | Step Functions state machine |
HttpParameters | API Gateway route or API destination |
EventBusV2Parameters | another new custom event bus |
UniversalTargetParameters | any supported AWS API |
| none permitted | classic event-bus target, Firehose delivery stream |
Firehose is a supported target, and it takes no parameter block. Its InvokeConfiguration is exactly
TargetArn plus RoleArn, the ARN’s resource part MUST be deliverystream/{name}, and delivery calls
PutRecordBatch, so the role MUST hold firehose:PutRecordBatch. Records are written as the raw
payload bytes with no separator or envelope added, so a consumer expecting newline-delimited JSON receives
them concatenated. This target has less operational mileage than the others, so you SHOULD verify a
delivery end to end before depending on it.
Supplying a block that does not match the resolved target type is rejected at create, naming both the
block you sent and the one expected, for example SnsParameters is not supported for target arn:aws:sqs:us-east-1:111122223333:orders. Expected SqsParameters. Each target type accepts exactly one
block, so supplying two trips the same rule rather than a separate count check. A target that takes no
block reads Expected no target parameters.
Two per-target facts:
HttpParameters serves two target kinds: an API Gateway execute-api ARN and an EventBridge
API destination ARN. Within this block InvocationTimeoutSeconds is accepted only for the API Gateway
kind, though the member also exists on the Lambda, Step Functions and universal-target blocks. The
header rules below are likewise API Gateway only.These apply to every target type. They prevent the same failure mode in every case: a configuration accepted at create time that the target service rejects at delivery time, where the publish response cannot tell you.
An omitted or empty typed block is accepted at create. KinesisParameters: {} is stored, and Kinesis
reports the missing PartitionKey during delivery. A FIFO SQS queue or SNS topic likewise needs a
MessageGroupId to deliver, but that is the target’s requirement, so omitting its block is accepted
at create and fails on every delivery.
A deduplication id, a message group id, a partition key, and an execution name are per-message by nature. A literal constant for any of them is accepted at create and is almost never right.
The consequence for a deduplication id: the target treats every message after the first as a duplicate and discards it. The symptom is exactly one message arriving and then nothing.
You MUST use a JSONata expression for anything per-message, where the member supports expressions.
Most scalar target parameters accept one in {% %} form, evaluated per event. The canonical FIFO pair:
"MessageGroupId": "{% $events.SystemMetadata.EventGroupId %}"
"MessageDeduplicationId": "{% $events.SystemMetadata.DeduplicationId %}"Neither form is checked against the target’s own range, requiredness, or applicability. A DelaySeconds
of 999999 and of {% 999999 %} are both accepted at create and both rejected by SQS at delivery,
because the accepted range belongs to SQS and the service does not keep a copy of it. The same holds for
a MessageGroupId you omitted on a FIFO target and a MessageDeduplicationId you set on a standard one.
An expression additionally MUST parse, and that is the only extra check it gets. So create-time
acceptance of either form is not evidence the value is correct.
The service checks the request. The target checks its own resource, and it does so at delivery.
Everything below is the target’s, not this service’s: whether the resource exists, whether the role may reach it, the payload size after enveloping, the value ranges, and the required fields of the API being called. See target-contract.md §5 for how each failure class is reported.
Three that catch people:
Type
for a .fifo queue, and accepts a missing or malformed group or deduplication id. SQS or SNS
enforces the target behaviour during delivery, so you SHOULD check that target’s length and
character limits rather than assuming a value the bus accepted will be accepted there.You SHOULD read the queue, scan the table, list the executions, check the function’s invocation
count. A PUBLISHED response is not delivery, and neither is an HTTP 200 from a batch API. See
delivery-troubleshooting.md for a rig that counts correctly.
OnFailureConfiguration: { "Arn": "arn:aws:sqs:..." }. The ARN’s shape is checked at create, so a
malformed value is rejected. You MUST read the subscriber back and confirm the field is populated,
because other nestings of it are accepted and stored empty.
The delivery role MUST have sqs:SendMessage on that queue in addition to the target’s own
action. A denied dead-letter queue turns every delivery failure into total silent loss, which is
strictly worse than having no dead-letter queue at all, because you believe you have one.
Set LogConfiguration: { "Level": "INFO", "IncludePayload": "FULL" } at the same time. It defaults to
OFF, and it is faster than the dead-letter queue: a log record appears on the first failed attempt,
while a dead-letter record waits for the retry window to close.
This list decides where you look when something is wrong. The service resolves the
target type from the ARN and then rejects, with InvalidInputException and nothing persisted:
MessageAttributes entry whose value is a null objectBinaryValue containing {% or %}, since it is forwarded unevaluatedAuthorization, Host, X-Amz*). An API destination takes the same block and gets neither checkInvocationTimeoutSeconds inside HttpParameters for an API destination rather than API Gateway, or
a literal out of range. The member is also valid on the Lambda, Step Functions and universal-target
blocksLogConfiguration.Level or IncludePayload outside its enumWhat it does not check is longer than the list above. It does not check that the target exists, is
reachable, or is writable by the role — those are delivery-time facts, so a well-formed ARN naming
nothing is accepted. And it does not hold a copy of the target API’s parameter grammar: value
ranges, required members, whether a member applies to the standard or FIFO resource you named,
identifier lengths and character sets, non-empty identifiers, message-attribute names against the
target’s own naming rules, data-type vocabularies and the requiredness of a DataType, attribute
counts, the Base64 contract, and reserved system-attribute names all stay with the target and are
enforced when it is invoked. The ARN shape rules are structural too, so no character is reserved: a
queue named queue?name is accepted. A create call that succeeds tells you the service can route and
build the request, not that the target will accept it.
A universal target invokes an AWS API directly, with no function in between.
arn:{partition}:events:::aws-sdk:{service}:{apiAction}Four requirements, each with the verbatim rejection you get for breaking it:
| Requirement | Get it wrong |
|---|---|
apiAction is camelCase, lowercase first letter | PutItem → The api PutItem is not valid for the service dynamodb; put_item → apiAction component must be alphanumeric camelCase |
service is the SDK service id, not always the obvious word | Step Functions is sfn, not states |
UniversalTargetParameters.Input is required | omitted → UniversalTargetParameters is required when TargetArn is a USI target; supplied on a non-universal target → only supported when TargetArn is a USI target. The validation text abbreviates universal target as “USI target” |
BatchConfiguration with valid MaxBatchSize and MaxBatchWindowInSeconds is required | create fails |
A malformed service namespace is rejected with not a supported subscriber target.
Input is validated at create time against the target API’s required fields. This is stricter
than most of the surface and it is useful: the message names the exact fields the API expects.
The field names inside Input are the target API’s Smithy member names in PascalCase
(TableName, Item, StateMachineArn), and the validator names missing fields in that casing, even
where the target API’s own wire format is camelCase.
DynamoDB putItem with empty input
-> UniversalTargetParameters.Input is missing required field(s): Item, TableName
SNS publish with empty input
-> UniversalTargetParameters.Input is missing required field(s): MessageWorked example, one DynamoDB row per event, key taken from the payload:
TargetArn: arn:aws:events:::aws-sdk:dynamodb:putItem
BatchConfiguration: { MaxBatchSize: 1, MaxBatchWindowInSeconds: 0 }
RoleArn: arn:aws:iam::111122223333:role/my-delivery-role
UniversalTargetParameters.Input:
{% { "TableName": "my-table", "Item": { "pk": { "S": $events.Data.detail.m } } } %}The addressing matters more here than anywhere else, because the expression builds the whole API
request. That example publishes with PutEvents, so the payload is at Data.detail. The same
expression written as $events.Data.m yields an empty attribute, and the delivery fails with a
dead-letter record reading errorCode: CUSTOMER_VALIDATION, errorMessage: "Supplied AttributeValue is empty, must contain exactly one of the supported datatypes". In that case the target and the role are
both correct and only the expression is wrong.
InvokeConfiguration.RoleArn MUST be a role the caller can pass. CreateSubscriber enforces
iam:PassRole on it during authorization, so a role you cannot pass fails with AccessDeniedException
before target validation runs.
Minimum policy contents:
sqs:SendMessage, sns:Publish, lambda:InvokeFunction,
kinesis:PutRecord, states:StartExecution, or the API your universal target callssqs:SendMessage on the dead-letter queueCorrections to that first bullet. Kinesis delivery calls PutRecords, so the action is
kinesis:PutRecords. Step Functions needs states:StartSyncExecution as well when you set
InvocationType: REQUEST_RESPONSE. Firehose delivery calls PutRecordBatch, so the action is
firehose:PutRecordBatch. And the HTTP and bus targets are missing from it: an API Gateway
target needs execute-api:Invoke, an API destination needs events:InvokeApiDestination, and
another new custom bus needs events:PutEvents and events:PutRawEvents.
The target ARN is checked at create for shape, target type, region, partition, and account, but not
for resource subtype existence or reachability. So a Lambda ARN in another region or partition is
rejected at create, and one whose resource is not function:{name} fails the shape rule at create,
while a well-formed ARN naming a function that does not exist is stored and becomes a delivery-time
failure. A qualifier is accepted without checking that the version or alias exists.