Skip to content

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
}
State = ACTIVEName = lab-eventsRetentionDays = 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.

EventBusAccountId = lab (111122223333)
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"
}
Principal = dev (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.

State = RUNNINGType = FIFOEventBusArn = BUS_ARN

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.

FailedEntryCount 0SuccessCode = 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"
}
status = OPENreason = weak_rssirssi −85 < −80 threshold

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.

status = CLEARreason = healthyts > previous

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"
}
status = OPENreason = attention_buttondetail_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"
}
entity Bstatus = OPENrssi = −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"
}
entity Astatus = CLEARrssi = −45B did not disturb A's lane

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"
    }
}
ApproximateNumberOfMessages 0NotVisible 0no failed deliveries

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"
        }
    ]
}
Sum > 0publishes landed on the bus
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"
        }
    ]
}
Sum > 0ingress bytes (lab / publisher side of the bill)

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"
        }
    ]
}
Sum > 0DATA filter selected events
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"
        }
    ]
}
Sum > 0delivered to the Subscriber
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"
        }
    ]
}
Sum > 0egress bytes (dev / consumer side of the bill)

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.