Verify¶
Prove the shared enhanced bus with AWS CLI only: visible in both accounts,
FIFO Subscriber running, ordered delivery into health-alerts, retention, and
ingress/egress meters. Run after Deploy lab and Deploy dev.
CLI put-events below smokes the same envelope eb-bridge stamps
(Source=iot.lab, EventGroupId). Live devices reach the bus via the IoT
events/telemetry rules → bridge wire on Deploy lab.
No mutation gate — these calls are PutEvents / read APIs.
Load ARNs¶
export AWS_REGION=ap-southeast-2
BUS_ARN="$(cat out/lab/bus-arn.txt)"
SHARE_ARN="$(cat out/lab/share-arn.txt)"
SUB_ARN="$(cat out/dev/subscriber-arn.txt)"
echo "$BUS_ARN"
arn:aws:events:ap-southeast-2:111122223333:event-busv2/lab-events/6yfjf3x6kbnmeebj112dpxahu
Bus visible in lab (owner)¶
aws eventsv2 list-event-buses --profile lab \
--query "EventBuses[?EventBusArn=='$BUS_ARN']" --output json
[
{
"Name": "lab-events",
"EventBusArn": "arn:aws:events:ap-southeast-2:111122223333:event-busv2/lab-events/6yfjf3x6kbnmeebj112dpxahu",
"State": "ACTIVE",
"EventBusAccountId": "111122223333"
}
]
aws eventsv2 describe-event-bus --profile lab \
--event-bus-arn "$BUS_ARN" \
--query '{Arn:EventBusArn,Name:Name,State:State,RetentionDays:StorageConfiguration.RetentionPeriodInDays}' \
--output json
{
"Arn": "arn:aws:events:ap-southeast-2:111122223333:event-busv2/lab-events/6yfjf3x6kbnmeebj112dpxahu",
"Name": "lab-events",
"State": "ACTIVE",
"RetentionDays": 7
}
Bus visible in dev (shared via RAM)¶
list-event-buses returns owned and RAM-shared buses. Shared rows only
carry identity fields (Name, EventBusArn, EventBusAccountId).
aws eventsv2 list-event-buses --profile dev \
--query "EventBuses[?EventBusArn=='$BUS_ARN']" --output json
[
{
"Name": "lab-events",
"EventBusArn": "arn:aws:events:ap-southeast-2:111122223333:event-busv2/lab-events/6yfjf3x6kbnmeebj112dpxahu",
"EventBusAccountId": "111122223333"
}
]
EventBusAccountId must be the lab account — not dev.
aws eventsv2 describe-event-bus --profile dev \
--event-bus-arn "$BUS_ARN" \
--query '{Arn:EventBusArn,Name:Name,State:State}' --output json
{
"Arn": "arn:aws:events:ap-southeast-2:111122223333:event-busv2/lab-events/6yfjf3x6kbnmeebj112dpxahu",
"Name": "lab-events",
"State": "ACTIVE"
}
aws ram get-resource-share-associations --profile lab \
--association-type PRINCIPAL \
--resource-share-arns "$SHARE_ARN" \
--query 'resourceShareAssociations[0].{Principal:associatedEntity,Status:status}' \
--output json
{
"Principal": "444455556666",
"Status": "ASSOCIATED"
}
aws ram list-resources --profile dev \
--resource-owner OTHER-ACCOUNTS \
--query "resources[?arn=='$BUS_ARN'].{Arn:arn,Status:status}" --output json
[
{
"Arn": "arn:aws:events:ap-southeast-2:111122223333:event-busv2/lab-events/6yfjf3x6kbnmeebj112dpxahu",
"Status": "AVAILABLE"
}
]
Subscriber on the shared bus¶
aws eventsv2 describe-subscriber --profile dev \
--subscriber-arn "$SUB_ARN" \
--query '{State:State,Type:Type,EventBusArn:EventBusArn}' --output json
{
"State": "RUNNING",
"Type": "FIFO",
"EventBusArn": "arn:aws:events:ap-southeast-2:111122223333:event-busv2/lab-events/6yfjf3x6kbnmeebj112dpxahu"
}
EventBusArn must match BUS_ARN. Type is FIFO.
Ordered delivery (one entity)¶
Use a fresh entity id and set SystemMetadata.EventGroupId to that id.
Source must be iot.lab — that is what the dev Subscriber filter matches.
ENTITY="verify-$(date -u +%Y%m%d%H%M%S)"
TS="$(date +%s)"
echo "$ENTITY"
verify-20260926044300
OPEN — weak rssi¶
aws eventsv2 put-events --profile lab \
--event-bus-arn "$BUS_ARN" \
--entries "[
{
\"Source\": \"iot.lab\",
\"DetailType\": \"connectivity\",
\"Detail\": \"{\\\"type\\\":\\\"connectivity\\\",\\\"rssi\\\":-85,\\\"chip_temp_c\\\":40,\\\"heap_free\\\":200000,\\\"ts\\\":${TS},\\\"device_id\\\":\\\"${ENTITY}\\\"}\",
\"SystemMetadata\": {\"EventGroupId\": \"${ENTITY}\"}
}
]"
{
"FailedEntryCount": 0,
"Entries": [
{
"EventId": "d2f552ac-d0f2-4491-8de5-707730744f9c",
"SequenceNumber": "10000000000000015000",
"SuccessCode": "PUBLISHED"
}
]
}
Expect FailedEntryCount 0 and SuccessCode PUBLISHED.
sleep 5
aws dynamodb get-item --profile dev \
--table-name health-alerts \
--key "{\"entity_id\":{\"S\":\"${ENTITY}\"}}" \
--query 'Item.{status:status.S,reason:reason.S,rssi:metrics.M.rssi.N,ts:source_event_ts.N}' \
--output json
{
"status": "OPEN",
"reason": "weak_rssi",
"rssi": "-85",
"ts": "1790397835"
}
CLEAR — healthy¶
TS=$((TS + 1))
aws eventsv2 put-events --profile lab \
--event-bus-arn "$BUS_ARN" \
--entries "[
{
\"Source\": \"iot.lab\",
\"DetailType\": \"connectivity\",
\"Detail\": \"{\\\"type\\\":\\\"connectivity\\\",\\\"rssi\\\":-50,\\\"chip_temp_c\\\":40,\\\"heap_free\\\":200000,\\\"ts\\\":${TS},\\\"device_id\\\":\\\"${ENTITY}\\\"}\",
\"SystemMetadata\": {\"EventGroupId\": \"${ENTITY}\"}
}
]"
{
"FailedEntryCount": 0,
"Entries": [
{
"EventId": "3fafde24-9673-418c-9c12-40893d1e7bca",
"SequenceNumber": "10000000000000016000",
"SuccessCode": "PUBLISHED"
}
]
}
sleep 5
aws dynamodb get-item --profile dev \
--table-name health-alerts \
--key "{\"entity_id\":{\"S\":\"${ENTITY}\"}}" \
--query 'Item.{status:status.S,reason:reason.S,rssi:metrics.M.rssi.N,ts:source_event_ts.N}' \
--output json
{
"status": "CLEAR",
"reason": "healthy",
"rssi": "-50",
"ts": "1790397836"
}
ts must advance with the new publish.
OPEN — button¶
TS=$((TS + 1))
aws eventsv2 put-events --profile lab \
--event-bus-arn "$BUS_ARN" \
--entries "[
{
\"Source\": \"iot.lab\",
\"DetailType\": \"button\",
\"Detail\": \"{\\\"type\\\":\\\"button\\\",\\\"event\\\":\\\"press\\\",\\\"ts\\\":${TS},\\\"device_id\\\":\\\"${ENTITY}\\\"}\",
\"SystemMetadata\": {\"EventGroupId\": \"${ENTITY}\"}
}
]"
{
"FailedEntryCount": 0,
"Entries": [
{
"EventId": "…",
"SequenceNumber": "…",
"SuccessCode": "PUBLISHED"
}
]
}
sleep 5
aws dynamodb get-item --profile dev \
--table-name health-alerts \
--key "{\"entity_id\":{\"S\":\"${ENTITY}\"}}" \
--query 'Item.{status:status.S,reason:reason.S,detail_type:detail_type.S}' \
--output json
{
"status": "OPEN",
"reason": "attention_button",
"detail_type": "button"
}
Independent EventGroupId lane¶
Same batch, two entities — B opens while A stays on its own timeline.
ENTITY_B="${ENTITY}-b"
TS_B=$((TS + 1))
aws eventsv2 put-events --profile lab \
--event-bus-arn "$BUS_ARN" \
--entries "[
{
\"Source\": \"iot.lab\",
\"DetailType\": \"connectivity\",
\"Detail\": \"{\\\"type\\\":\\\"connectivity\\\",\\\"rssi\\\":-90,\\\"chip_temp_c\\\":40,\\\"heap_free\\\":200000,\\\"ts\\\":${TS_B},\\\"device_id\\\":\\\"${ENTITY_B}\\\"}\",
\"SystemMetadata\": {\"EventGroupId\": \"${ENTITY_B}\"}
},
{
\"Source\": \"iot.lab\",
\"DetailType\": \"connectivity\",
\"Detail\": \"{\\\"type\\\":\\\"connectivity\\\",\\\"rssi\\\":-45,\\\"chip_temp_c\\\":40,\\\"heap_free\\\":200000,\\\"ts\\\":$((TS_B + 1)),\\\"device_id\\\":\\\"${ENTITY}\\\"}\",
\"SystemMetadata\": {\"EventGroupId\": \"${ENTITY}\"}
}
]"
{
"FailedEntryCount": 0,
"Entries": [
{"EventId": "…", "SequenceNumber": "…", "SuccessCode": "PUBLISHED"},
{"EventId": "…", "SequenceNumber": "…", "SuccessCode": "PUBLISHED"}
]
}
sleep 5
aws dynamodb get-item --profile dev \
--table-name health-alerts \
--key "{\"entity_id\":{\"S\":\"${ENTITY_B}\"}}" \
--query 'Item.{status:status.S,reason:reason.S,rssi:metrics.M.rssi.N}' \
--output json
{
"status": "OPEN",
"reason": "weak_rssi",
"rssi": "-90"
}
aws dynamodb get-item --profile dev \
--table-name health-alerts \
--key "{\"entity_id\":{\"S\":\"${ENTITY}\"}}" \
--query 'Item.{status:status.S,reason:reason.S,rssi:metrics.M.rssi.N}' \
--output json
{
"status": "CLEAR",
"reason": "healthy",
"rssi": "-45"
}
DLQ empty¶
aws sqs get-queue-attributes --profile dev \
--queue-url "$(cat out/dev/dlq-url.txt)" \
--attribute-names ApproximateNumberOfMessages ApproximateNumberOfMessagesNotVisible \
--output json
{
"Attributes": {
"ApproximateNumberOfMessages": "0",
"ApproximateNumberOfMessagesNotVisible": "0"
}
}
Meters (ingress / egress)¶
CloudWatch namespace AWS/EventsV2 — the same meters as Cost.
Dimension values are the bus and subscriber name/id suffixes (not full ARNs).
Metrics can lag a few minutes after publish.
BUS_DIM="${BUS_ARN##*event-busv2/}"
SUB_DIM="${SUB_ARN##*subscriber/}"
echo "BUS_DIM=$BUS_DIM"
echo "SUB_DIM=$SUB_DIM"
BUS_DIM=lab-events/6yfjf3x6kbnmeebj112dpxahu
SUB_DIM=health-fifo/abq086i14frab44aykzxxqhgk
Lab — publish ingress¶
aws cloudwatch get-metric-statistics --profile lab \
--namespace AWS/EventsV2 \
--metric-name PublishEventsEntryCount \
--dimensions Name=EventBus,Value="$BUS_DIM" \
--start-time "$(date -u -d '6 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 3600 --statistics Sum --output json
{
"Label": "PublishEventsEntryCount",
"Datapoints": [
{
"Timestamp": "2026-09-26T16:09:00+12:00",
"Sum": 12.0,
"Unit": "Count"
}
]
}
aws cloudwatch get-metric-statistics --profile lab \
--namespace AWS/EventsV2 \
--metric-name PublishEventsIngressBytes \
--dimensions Name=EventBus,Value="$BUS_DIM" \
--start-time "$(date -u -d '6 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 3600 --statistics Sum --output json
{
"Label": "PublishEventsIngressBytes",
"Datapoints": [
{
"Timestamp": "2026-09-26T16:09:00+12:00",
"Sum": 14206.0,
"Unit": "Bytes"
}
]
}
Dev — filter match and egress¶
aws cloudwatch get-metric-statistics --profile dev \
--namespace AWS/EventsV2 \
--metric-name FilterMatched \
--dimensions Name=Subscriber,Value="$SUB_DIM" Name=EventBus,Value="$BUS_DIM" \
--start-time "$(date -u -d '6 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 3600 --statistics Sum --output json
{
"Label": "FilterMatched",
"Datapoints": [
{
"Timestamp": "2026-09-26T16:09:00+12:00",
"Sum": 12.0,
"Unit": "Count"
}
]
}
aws cloudwatch get-metric-statistics --profile dev \
--namespace AWS/EventsV2 \
--metric-name EventsDelivered \
--dimensions Name=Subscriber,Value="$SUB_DIM" Name=EventBus,Value="$BUS_DIM" \
--start-time "$(date -u -d '6 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 3600 --statistics Sum --output json
{
"Label": "EventsDelivered",
"Datapoints": [
{
"Timestamp": "2026-09-26T16:09:00+12:00",
"Sum": 12.0,
"Unit": "Count"
}
]
}
aws cloudwatch get-metric-statistics --profile dev \
--namespace AWS/EventsV2 \
--metric-name EgressBytes \
--dimensions Name=Subscriber,Value="$SUB_DIM" Name=EventBus,Value="$BUS_DIM" \
--start-time "$(date -u -d '6 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 3600 --statistics Sum --output json
{
"Label": "EgressBytes",
"Datapoints": [
{
"Timestamp": "2026-09-26T16:09:00+12:00",
"Sum": 8674.0,
"Unit": "Bytes"
}
]
}
Empty Datapoints?
Wait a few minutes and re-run, or widen --start-time. Empty usually means
lag, wrong BUS_DIM / SUB_DIM, or no publishes in the window yet.
If a get-item is empty, wait a few more seconds and re-run that command. Stuck? Troubleshooting. Clean up: Tear down.