AWS Lesson 115 of 123

AWS Enterprise Architecture: IoT Analytics

In a nutshell

Imagine a giant airport for data. Millions of travelers (your devices) arrive every second. Each one needs a passport check at the gate (secure identity), a way to shout a short status update without queuing (an MQTT publish), and a sorting hall that instantly routes each message to the right destination — some to the “act on this right now” desk, some to permanent records storage, some to the analytics office (the Rules Engine fan-out). This lesson is the blueprint for that airport, built entirely from managed AWS services so you never run the building yourself.

Put plainly: an IoT analytics architecture takes a flood of tiny sensor readings and turns it into two things a business actually wants — instant alerts when something is wrong right now, and trustworthy history you can chart, audit, and learn from later. The catch is that the same reading has to travel two very different roads at once: a fast “hot” road measured in seconds, and a cheap, durable “cold” road measured in years. Most of this lesson is about building both roads and the junction — AWS IoT Core — that splits traffic between them.

Why should a beginner care? Because this is the shape of nearly every “connected thing” system — a smart thermostat, a delivery van, a factory robot, a hospital infusion pump. Learn this one pattern and you can reason about all of them. And the five ideas underneath it — secure identity, decoupled ingestion, stream processing, a durable lake, and a serving layer — reappear in almost every data architecture on AWS, IoT or not.

Level: Advanced (now with a beginner on-ramp) · Time: ~55 min

Prerequisites — helpful, not mandatory:

After this lesson you will be able to:

Industrial and connected-product companies sit on a paradox: their machines emit more telemetry than ever, yet the data lands in a dozen disconnected silos — a SCADA historian here, a CSV export there, a vendor cloud nobody can query. The result is that the most operationally valuable signal in the business (what the physical assets are actually doing, second by second) is the hardest to analyze. This article lays out a reusable AWS architecture that turns raw device telemetry into governed, queryable, dashboard-ready analytics, scaling from a single pilot line to a global fleet of millions of devices.

The business scenario

Picture three companies that look different but share the same shape of problem.

A mid-size HVAC manufacturer ships 40,000 rooftop units a year, each with a cellular modem. Warranty claims are killing margins, and the only way to know a compressor is failing is when a customer calls. They want predictive maintenance, but the telemetry never leaves the unit’s flash memory.

A regional water utility runs 1,200 pumping stations with PLCs speaking Modbus and OPC-UA. Their historian retains 90 days at full resolution, then downsamples and effectively forgets. Regulators now demand multi-year auditable records of flow, pressure, and chlorine dosing, and the operations team wants a single pane of glass instead of 1,200 vendor HMIs.

A logistics operator has 8,000 refrigerated trailers (reefers) with GPS, temperature, door, and fuel sensors reporting every 30 seconds. Cold-chain compliance fines are real money, and a single spoiled load can cost more than a year of cloud bills.

Every one of these is the same architecture under the hood: high-cardinality device telemetry, ingested securely at the edge, processed in motion, stored as time-series, and surfaced to both engineers and business users. What differs is scale (thousands vs. millions of devices), protocol (MQTT vs. industrial fieldbus), and latency tolerance (a reefer alarm needs seconds; a warranty trend can wait minutes).

The non-negotiable requirements that recur across all three:

This is the canonical AWS IoT analytics stack: IoT Core → Kinesis → Timestream / SiteWise → QuickSight, with S3 as the durable backbone.

Architecture overview

The end-to-end data path moves left to right, from physical asset to business insight, with a clean split between a low-latency “hot path” and a high-throughput “warm/cold path.”

AWS IoT analytics reference architecture: devices and IoT Greengrass ingest to IoT Core; the Rules Engine fans out to a Kinesis hot path (Managed Flink, DynamoDB, SNS/IoT Events), a Timestream and IoT SiteWise time-series tier, and a Firehose-to-S3 cold-path data lake (Glue, Athena, SageMaker), surfaced via QuickSight and SiteWise Monitor.

1. Edge & connectivity. Devices connect to AWS IoT Core over MQTT (TLS 1.3, mutual auth with X.509 certificates) or, for constrained cellular fleets, MQTT-over-WebSockets. Industrial sites that speak Modbus/OPC-UA don’t talk MQTT natively, so a gateway running AWS IoT Greengrass sits on-prem, normalizes fieldbus protocols, buffers during WAN outages, and forwards to the cloud. SiteWise can also ingest directly from the SiteWise Edge gateway at the plant.

2. Ingestion & routing. IoT Core’s message broker receives every publish. The IoT Rules Engine is the fan-out hub: a single inbound message can be routed by SQL-like rules to multiple destinations simultaneously. One rule pushes the full firehose to Amazon Kinesis Data Streams for stream processing; another sends a filtered subset (e.g., only alarm=true messages) to AWS IoT Events or directly to an SNS topic; a third writes structured measurements into AWS IoT SiteWise asset properties.

3. Hot path (seconds). Kinesis Data Streams feeds a stream processor — either Kinesis Data Analytics for Apache Flink (Managed Service for Apache Flink) for windowed aggregations and anomaly detection, or a Lambda consumer for simpler enrichment. Anomalies and threshold breaches fan out to SNS / IoT Events for alerting and to DynamoDB for the “current device state” lookup that operators query. Latency from sensor to alert is single-digit seconds.

4. Warm path / time-series store. Cleaned, enriched records land in Amazon Timestream — a purpose-built, serverless time-series database with a memory tier (fast, recent) and a magnetic tier (cheap, historical), and automatic tiering between them. In parallel, IoT SiteWise models the data against an asset hierarchy (Site → Line → Machine → Sensor), computes derived metrics (“transforms” and “metrics” like rolling OEE or average pressure), and serves both engineers and dashboards.

5. Cold path / data lake. Kinesis Data Firehose continuously batches raw and processed telemetry into Amazon S3 in Parquet, partitioned by date and device group. This is the immutable system of record. AWS Glue catalogs it; Athena queries it ad hoc; the lake feeds ML training (SageMaker) and long-term compliance retention via S3 lifecycle policies into Glacier.

6. Visualization & consumption. Amazon QuickSight is the business-facing layer. It connects natively to Timestream and Athena, uses SPICE (its in-memory engine) for snappy dashboards, and supports embedded analytics so the utility’s operators or the manufacturer’s dealers see dashboards inside their own portals. SiteWise also offers SiteWise Monitor portals for plant engineers who think in assets, not SQL.

In one sentence: IoT Core authenticates and ingests, the Rules Engine fans data into Kinesis (hot) and SiteWise/Firehose (warm/cold), Timestream and SiteWise store and model it, and QuickSight + SiteWise Monitor present it — with S3 as the durable lake underneath everything.

A naming clarification (read this first). This lesson is titled “IoT Analytics”, but it does not use — and you should not use — the old standalone service literally named AWS IoT Analytics. That product reached end of support on 15 December 2025 and is no longer a recommended path. The current, AWS-recommended pattern is exactly what this article builds: IoT Core rules → Kinesis / Firehose → S3 → Athena / QuickSight, plus purpose-built stores like Timestream and SiteWise. So “IoT analytics” here means the architecture pattern, composed from general-purpose managed services — not the retired product. If you inherit a system still wired to AWS IoT Analytics channels/pipelines/datasets, plan a migration onto this composition.

How devices talk to the cloud: MQTT and device shadows

The diagram shows arrows leaving the devices, but a beginner’s first honest question is: what actually travels along that first arrow, and how? The answer is MQTT, and understanding it removes most of the mystery from everything downstream.

MQTT in five minutes

MQTT is a lightweight publish/subscribe messaging protocol built for exactly this job: tiny devices, flaky networks, low bandwidth. AWS IoT Core is, at heart, a massively scaled managed MQTT broker that speaks MQTT 3.1.1 and MQTT 5 over TLS (port 8883), plus MQTT-over-WebSockets (port 443) for clients stuck behind restrictive firewalls.

The mental model has three moving parts:

A handful of MQTT details matter in production — and interviewers love them:

Feature What it means AWS IoT Core specifics
QoS 0 “At most once” — fire and forget Supported. Cheapest, lowest latency; a dropped packet is simply lost.
QoS 1 “At least once” — redelivered until acknowledged Supported. Use for telemetry you cannot lose; expect occasional duplicates, so make consumers idempotent.
QoS 2 “Exactly once” Not supported by AWS IoT Core. Design for QoS 1 + idempotency instead.
Retained message Broker keeps the last message on a topic and hands it to any new subscriber immediately Supported; handy for “last known config/state” on a topic.
Last Will & Testament (LWT) A message the broker publishes automatically if the device drops without a clean disconnect Supported; the classic way to detect “a device just went dark.”
Keep-alive Heartbeat interval; miss it and the broker declares the client gone 30–1200 seconds. Tune against your cellular idle timeouts.

The maximum MQTT payload AWS IoT Core accepts is 128 KB per message — a deliberate nudge toward many small readings rather than giant blobs (put large files in S3 and publish a pointer instead).

Device shadows: talking to something that is usually asleep

Cellular and battery devices are offline most of the time. So how do you “set the target temperature to 0°C” on a reefer that only wakes for four seconds every five minutes? You don’t talk to the device — you talk to its device shadow.

A shadow is a JSON document that AWS IoT Core stores on behalf of each device, with two halves that matter:

When the two differ, IoT Core computes a delta and publishes it to a reserved topic. The next time the device connects, it reads the delta, applies the change, and reports its new state — which clears the delta. Device and cloud converge eventually, even though they are never online at the same instant.

{
  "state": {
    "desired":  { "setpoint_c": 0 },
    "reported": { "setpoint_c": 2, "actual_c": 4.2 },
    "delta":    { "setpoint_c": 0 }
  },
  "metadata": { "reported": { "actual_c": { "timestamp": 1749480000 } } },
  "version": 47
}

Shadows live under reserved MQTT topics ($aws/things/<thingName>/shadow/update, .../update/delta, .../get, .../update/accepted). The version field gives you optimistic concurrency — a stale updater is rejected — and each Thing can carry one classic (unnamed) shadow plus multiple named shadows (say, firmware and climate) to model independent sub-systems. Keep the two jobs mentally separate: the shadow is current state (“what is this device’s desired/actual configuration right now?”); the Kinesis/Timestream pipeline is the event history (“what has this device been doing over time?”). Using a shadow to store history, or the telemetry stream to hold current config, is a classic beginner tangle.

The Rules Engine, read slowly

The IoT Rules Engine is the busiest junction in the whole design, and its SQL trips people up because it looks like database SQL but runs over a stream of individual messages. Each inbound MQTT message is one “row.” A rule has three parts: a SELECT (reshape the payload), a FROM (a topic filter deciding which messages the rule even sees), and an optional WHERE (drop the ones you don’t want). Whatever survives is handed to one or more actions.

Walk through a realistic rule for the reefer fleet. A trailer publishes to dt/site42/reefer/TRL-7731/telemetry:

SELECT
  topic(4)                 AS deviceId,     -- 4th topic segment = TRL-7731
  temperature              AS temp_c,
  (temperature - setpoint) AS temp_error,
  timestamp()              AS ingest_ts
FROM 'dt/+/reefer/+/telemetry'
WHERE temperature > setpoint + 3

Read it line by line:

The same inbound message is usually matched by several rules at once — one unfiltered rule shoveling everything to Kinesis, this filtered alarm rule, and a third writing to SiteWise. That parallel fan-out from a single publish is the Rules Engine’s superpower. Two production habits you should never skip: attach an error action (a dead-letter SQS/CloudWatch target) to every rule so failures are visible, and prefer basic ingest ($aws/rules/<ruleName>/...) for high-volume telemetry to skip per-message broker messaging charges.

The path of one reading

Let’s follow a single temperature reading from trailer TRL-7731 the whole way, so every box on the diagram turns concrete. Assume the reefer’s evaporator coil is drifting warm.

  1. Sense & publish (t = 0 ms). The controller reads 4.2°C, wraps it in a small JSON payload with a timestamp and the 2°C setpoint, and publishes over MQTT (QoS 1, TLS, mutual-auth cert) to dt/site42/reefer/TRL-7731/telemetry. Bytes on the wire: a few hundred.
  2. Authenticate & ingest (t ≈ 20 ms). IoT Core validates the device’s X.509 certificate, checks that the attached IoT policy allows publishing to exactly that topic, and accepts the message. A stolen cert from a different trailer could not have published here, because the policy is scoped with ${iot:Connection.Thing.ThingName}.
  3. Fan out via rules (t ≈ 25 ms). Three rules match at once. Rule 1 forwards the raw message to Kinesis Data Streams, partitioned by deviceId. Rule 2 (the WHERE temperature > setpoint + 3 alarm rule) evaluates 4.2 > 2 + 34.2 > 5false, so the hard-threshold alarm stays quiet. (Note this carefully: the coil is failing, but it has not breached the crude threshold yet — which is exactly why we also run the ML anomaly path in the next step.) Rule 3 writes the measurement into SiteWise against the “Evaporator Coil” asset property.
  4. Hot path (t ≈ 1–3 s). A Managed Service for Apache Flink job reads the Kinesis shard, keeps a per-trailer 5-minute window, and runs a Random Cut Forest anomaly model. The coil’s slow warm-up nudges the anomaly score upward. Minutes later, when the score crosses its threshold, Flink emits an event to IoT Events / SNS — the dispatcher is paged before the load spoils — and updates DynamoDB so the ops screen flags TRL-7731 as “at risk.”
  5. Warm path (t ≈ seconds). The cleaned reading is also written to Timestream, landing in the fast memory store. An operator’s live dashboard, and any WHERE time > ago(1h) query, reads it from there.
  6. Cold path (t ≈ up to a minute). In parallel, Firehose buffers this reading with thousands of others and, when the buffer fills (say 128 MB or 60 s), writes a Parquet object to S3, partitioned region=.../date=.... This reading is now part of the permanent record — the exact row a compliance auditor or an ML training job will read next year.
  7. Age & tier (t = days → years). In Timestream, the reading falls from the memory store to the cheap magnetic store once its retention window passes. In S3, a lifecycle rule eventually transitions the object to Glacier. Nothing is deleted; it just gets cheaper.

One reading, six services, three time-horizons — and the same byte of data ends up powering a real-time page, a live dashboard, and a two-year audit record. That triple-use, from a single publish, is the whole point of the pattern.

Component breakdown

Component Role Why it’s here Key configuration choices
AWS IoT Core Managed MQTT broker + device registry + Rules Engine Secure, scalable front door for millions of devices with per-device identity X.509 cert per device via fleet provisioning; Thing Groups for fleet management; basic ingest topics to skip broker billing on high-volume rule traffic
AWS IoT Greengrass Edge runtime on on-prem gateways Protocol translation (Modbus/OPC-UA→MQTT), local buffering, edge ML inference during WAN loss Stream Manager for store-and-forward; component-based deployment; local Lambda/container compute
IoT Rules Engine SQL routing of inbound messages Single message → many destinations without custom code SQL SELECT with functions; error action to SQS/CloudWatch; route to Kinesis, SiteWise, Firehose, Lambda in parallel
Kinesis Data Streams Ordered, replayable telemetry pipe Decouples ingestion from processing; absorbs spikes; multiple consumers On-demand mode for unpredictable IoT load, or provisioned shards (1 MB/s or 1,000 records/s each); partition key = deviceId; 24h–365d retention
Managed Service for Apache Flink Stateful stream processing Windowed aggregations, sessionization, real-time anomaly detection (RANDOM_CUT_FOREST) Tumbling/sliding windows; checkpointing to S3; autoscaling parallelism
Amazon Timestream Serverless time-series database Purpose-built for trillions of events/day with automatic hot→cold tiering Memory store retention (hours/days) + magnetic store (years); scheduled queries for rollups; partition by dimension
AWS IoT SiteWise Industrial asset modeling + time-series Gives raw signals asset context and computes OEE/derived metrics; engineer-friendly Asset models + hierarchies; transforms & metrics; SiteWise Edge for on-prem; data buffering
Kinesis Data Firehose Batched delivery to the lake Zero-code, serverless landing of data into S3/Redshift Parquet conversion via Glue schema; dynamic partitioning by deviceGroup/date; buffering 64–128 MB
Amazon S3 Durable data lake / system of record Immutable raw store, ML source, compliance archive Partitioned Parquet; lifecycle to Glacier; Object Lock for WORM compliance
Amazon DynamoDB “Current state” / device shadow store Single-digit-ms lookup of latest reading per device for operator screens deviceId PK; TTL on stale entries; on-demand capacity
Amazon QuickSight BI & embedded dashboards Self-service analytics for business users; embeddable in portals SPICE for speed, direct query for freshness; row-level security per tenant; embedding via OIDC
SiteWise Monitor Asset-centric operational portals Plant engineers visualize hierarchies without BI tooling Portals, projects, dashboards scoped by asset; SSO via IAM Identity Center

A note on why two time-series destinations exist. Timestream is a general-purpose time-series database you query with SQL — ideal for cross-device analytics, ML feature engineering, and QuickSight. SiteWise is opinionated around the industrial asset model: it natively understands a hierarchy of equipment and computes engineering metrics. Many enterprises run both — SiteWise for the OT/engineering audience and the asset graph, Timestream (and the S3 lake) for the data-science and BI audience. Smaller deployments pick one. We call this out explicitly so the choice is deliberate, not accidental.

Implementation guidance

Device identity and provisioning. Never ship devices with a shared certificate. Use IoT Core fleet provisioning: each device presents a bootstrap claim certificate, calls the provisioning template, and receives a unique X.509 certificate bound to a Thing. Attach an IoT policy scoped with policy variables so a device can only publish/subscribe to its own topic namespace:

{
  "Effect": "Allow",
  "Action": ["iot:Publish"],
  "Resource": "arn:aws:iot:*:*:topic/dt/${iot:Connection.Thing.ThingName}/telemetry"
}

This is the cornerstone of Zero Trust here — a compromised device cannot impersonate or eavesdrop on any other.

Topic design. Adopt a hierarchical topic taxonomy, e.g. dt/<site>/<assetType>/<deviceId>/telemetry for data and cmd/<deviceId>/# for commands. Use basic ingest ($aws/rules/<ruleName>/...) for high-volume telemetry so you pay Rules Engine and downstream costs but skip per-message broker messaging charges.

IaC with Terraform. Manage everything as code. A representative module layout:

# IoT Core ingestion rule -> Kinesis (hot) + Firehose (cold) + SiteWise
resource "aws_iot_topic_rule" "telemetry_fanout" {
  name        = "telemetry_fanout"
  enabled     = true
  sql         = "SELECT *, topic(3) AS deviceId FROM 'dt/+/+/+/telemetry'"
  sql_version = "2016-03-23"

  kinesis {
    role_arn      = aws_iam_role.iot_to_kinesis.arn
    stream_name   = aws_kinesis_stream.telemetry.name
    partition_key = "$${deviceId}"
  }

  firehose {
    role_arn           = aws_iam_role.iot_to_firehose.arn
    delivery_stream_name = aws_kinesis_firehose_delivery_stream.lake.name
    separator          = "\n"
  }

  error_action {
    sqs {
      role_arn   = aws_iam_role.iot_dlq.arn
      queue_url  = aws_sqs_queue.rule_dlq.id
      use_base64 = false
    }
  }
}

resource "aws_kinesis_stream" "telemetry" {
  name             = "telemetry"
  stream_mode_details { stream_mode = "ON_DEMAND" }
  retention_period = 24
}

resource "aws_timestreamwrite_database" "iot" { database_name = "iot_analytics" }
resource "aws_timestreamwrite_table" "telemetry" {
  database_name = aws_timestreamwrite_database.iot.database_name
  table_name    = "device_telemetry"
  retention_properties {
    memory_store_retention_period_in_hours  = 24
    magnetic_store_retention_period_in_days = 1825   # 5 years
  }
}

Note the error action on every rule — IoT rule failures are silent unless you route them to a dead-letter queue. For SiteWise, model asset hierarchies with aws_iotsitewise_asset_model and aws_iotsitewise_asset, then wire the SiteWise rule action to map MQTT payload fields to asset property IDs. (Bicep/Deployment Manager don’t apply here — this is AWS-native; the Terraform AWS provider is the standard choice, with the CDK as an alternative for teams that prefer TypeScript/Python.)

Stream processing. For the Flink job, checkpoint to S3, key streams by deviceId, and use tumbling windows for periodic rollups (1-min averages) plus RANDOM_CUT_FOREST for unsupervised anomaly scores. Emit anomalies to a second Kinesis stream or directly to IoT Events for the alarm state machine.

Networking and identity wiring. Keep processing private: run Flink/Lambda consumers in a VPC and reach AWS services over VPC interface endpoints (PrivateLink) for Kinesis, Timestream, and S3 (gateway endpoint) so analytics traffic never traverses the public internet. IoT Core’s data plane is a public TLS endpoint by design, but you can use IoT Core VPC endpoints for device traffic from within your network or via Direct Connect for fixed industrial sites. Use IAM Identity Center for human SSO into QuickSight and SiteWise Monitor; use IAM roles (never long-lived keys) for every service-to-service hop. QuickSight embedding uses the GenerateEmbedUrlForRegisteredUser API behind your app’s auth.

Enterprise considerations

Security & Zero Trust. Identity is per-device, per-user, and per-service — never shared. Device certs are revocable instantly via the registry and continuously audited by AWS IoT Device Defender, which detects anomalous behavior (a device suddenly publishing 100x its baseline, or to topics outside its policy) and can quarantine it. Encrypt everywhere: TLS in transit; KMS-managed keys at rest in Kinesis, Timestream, S3, and DynamoDB. Scope IoT policies with policy variables (above). For the data lake, Lake Formation enforces column- and row-level permissions so the BI team sees aggregates but not raw GPS traces if policy forbids. QuickSight row-level security ensures a dealer sees only their own units in an embedded dashboard.

Cost optimization. IoT cost scales with message count and size, so the biggest lever is edge aggregation — have Greengrass or the device batch and pre-aggregate rather than firehosing every raw reading. Use basic ingest to drop broker messaging charges on telemetry that only feeds rules. Pick Kinesis on-demand for spiky fleets but switch to provisioned shards once load is predictable (often 40–60% cheaper at steady state). Lean on Timestream’s tiering: keep memory-store retention short (hours/days) and let data fall to the cheap magnetic tier; use scheduled queries to pre-aggregate so dashboards hit small rollup tables, not raw events. Land the lake in Parquet (5–10x cheaper to scan in Athena than JSON) and lifecycle cold partitions to Glacier. In QuickSight, SPICE caching avoids re-querying Timestream on every dashboard view, and the per-reader pricing keeps embedded analytics economical at scale.

Scalability. Every tier is horizontally elastic: IoT Core scales to millions of connections; Kinesis to thousands of shards (or on-demand auto-scale); Timestream and Firehose are serverless. Partition keys (deviceId) keep ordering per-device while spreading load. The architecture’s scaling is sub-linear in cost because of aggregation, tiering, and Parquet — doubling device count does not double the bill.

Reliability & DR (RTO/RPO). Kinesis replication is multi-AZ by default; set stream retention to 24h–7d so a downstream outage is replayable (this gives a near-zero RPO for the hot path — you can reprocess). The S3 lake is the durable source of truth (eleven nines); enable Cross-Region Replication for the buckets and Glacier archives if you need regional DR. Timestream is multi-AZ within a region; for cross-region resilience, the S3 lake is your rebuild source. A practical target: RPO ≈ minutes (bounded by Firehose buffer + Kinesis retention) and RTO of a few hours to stand up the processing/serving tier in a second region from IaC, with the lake replicated. IoT Core devices should be configured with exponential-backoff reconnection and offline buffering (Greengrass Stream Manager / device-side queue) so a regional blip doesn’t lose data.

Observability. CloudWatch metrics on IoT Core (Connect.Success, RulesExecuted, RuleMessageThrottled), Kinesis (IteratorAge — the canary for consumer lag), Firehose delivery success, and Timestream write/query latency. Alarm on IteratorAge rising (processing falling behind) and on IoT rule error-action DLQ depth. Device Defender feeds a security dashboard. Trace the pipeline end-to-end and surface the operational view in CloudWatch dashboards, while business KPIs live in QuickSight.

Governance. Tag every resource by cost center and environment. Use AWS Organizations + SCPs to enforce encryption and region restrictions. Catalog the lake in Glue Data Catalog and govern access with Lake Formation. Retain raw telemetry per regulatory mandate using S3 Object Lock (WORM) for tamper-proof compliance records — critical for the utility and cold-chain cases.

Reference enterprise example

ColdHaul Logistics operates 8,000 refrigerated trailers across North America. Each reefer reports GPS, setpoint vs. actual temperature, ambient temperature, door open/close events, and fuel level every 30 seconds — roughly 23 million messages per day, spiking to a reconnection storm of 8,000 simultaneous connects whenever trailers exit a dead zone. A single spoiled produce load costs the customer ~$45,000 and triggers a chargeback; regulators (FSMA, FDA cold-chain) require 2 years of auditable temperature records.

Their build:

Numbers and outcome. At ~23M msgs/day with edge batching (devices send a 10-reading bundle every 5 minutes rather than each reading raw), monthly costs land near IoT Core ingest ~$600, Kinesis on-demand ~$700, Flink ~$900, Timestream ~$1,400, Firehose+S3+Glacier ~$500, QuickSight ~$1,200, totaling roughly $5,300/month — under $0.70 per trailer per month to monitor a $150,000 asset hauling perishable freight. In the first year, early compressor-failure detection prevented an estimated 31 spoiled loads (~$1.4M avoided), cold-chain chargebacks fell 78%, and the compliance team replaced a quarterly fire drill of CSV exports with a one-click audit export from the WORM archive. The data scientists later trained a remaining-useful-life model on the S3 lake with zero new pipeline work — the lake was already there.

When to use it

Use this architecture when you have genuine device telemetry at scale, need both real-time alerting and long-horizon historical analytics, require per-device security and revocability, and want business users (not just engineers) to self-serve insights. It shines for connected products, industrial/OT modernization, fleet and cold-chain monitoring, and energy/utility telemetry.

Trade-offs and decisions:

Anti-patterns to avoid:

The pattern’s strength is that each layer is independently serverless or elastic, the data path degrades gracefully (devices buffer, Kinesis replays, the lake never forgets), and cost scales sub-linearly — so the same blueprint serves a 40-unit pilot and an 8-million-device global fleet with only configuration changes.

Going deeper

This section is for the reader who will actually operate one of these systems. It assumes you followed the path-of-one-reading above.

Kinesis internals and the hot-shard trap

A Kinesis Data Stream is an ordered log split into shards. Each shard ingests up to 1 MB/s or 1,000 records/s and emits up to 2 MB/s, shared across standard consumers. Two numbers govern your life here:

To fan out to many independent consumers without dividing that shared 2 MB/s, use enhanced fan-out (each consumer gets its own 2 MB/s pipe via a push model). Set retention (24 h default, up to 365 days) long enough that a downstream outage is replayable rather than lost — this is what gives the hot path a near-zero RPO.

Firehose, Parquet, and the small-file problem

Firehose is deliberately dumb and cheap: buffer, then flush. Two buffer triggers race — size (1–128 MB) and time (60–900 s) — whichever fires first. Tune them against a real tension: large buffers make big, scan-efficient Parquet files but add latency to the lake; small buffers feel fresh but spawn millions of tiny objects that make Athena slow and S3 request costs balloon (the small-file problem). Enable record-format conversion to Parquet (Firehose reads a Glue table’s schema) and dynamic partitioning so paths look like s3://lake/region=us-east-1/date=2026-06-09/. Point a Firehose error-output prefix at a separate S3 location so failed records are quarantined, never silently dropped.

The batch and warehouse tier (Glue, EMR, Redshift)

The hot path answers “what’s happening now”; the lake answers “what happened, in bulk.” Three tools sit over the S3 lake:

A useful rule of thumb: Athena for ad-hoc exploration over the lake, Redshift for repeated high-concurrency BI, EMR for heavy custom batch, Glue for the ETL glue between them. For the full pattern, see the AWS lakehouse lesson.

Timestream, precisely

Timestream — specifically Timestream for LiveAnalytics (a separate Timestream for InfluxDB exists for teams standardized on InfluxDB) — models data as dimensions (metadata identifying a series: device_id, site), a measure (name + value), and a time. Ingestion lands in the memory store (fast, indexed, recent); after its retention it tiers automatically to the magnetic store (cheap, historical). Query cost is driven by the volume of data scanned, so the winning move is scheduled queries that continuously pre-aggregate raw events into small rollup tables (hourly min/max/avg per device); dashboards then hit the rollups, not the firehose of raw points. Watch for rejected records: a reading whose timestamp is older than the memory-store retention window is refused, so genuinely late-arriving data needs either a longer memory window or magnetic-store writes enabled.

Device security, beyond “use a cert”

The X.509 story has depth worth knowing:

The edge: Greengrass internals

When the WAN dies, a naive fleet loses data. Greengrass turns the on-prem gateway into a mini-cloud: components (versioned, deployed like packages) run local Lambda/containers; Stream Manager does durable store-and-forward so readings survive an outage and sync when the link returns; a local MQTT broker and shadow let devices keep working offline; and edge ML runs inference (say, the anomaly model) next to the machine for millisecond reactions and to avoid shipping raw high-rate data upstream. SiteWise Edge is the industrial cousin, collecting OPC-UA/Modbus at the plant and computing metrics locally.

Reconnection storms, quotas, and failure modes

Version and naming caveats (2026)

Practice challenges

Work these in order — they climb from beginner to advanced. Try each before opening the solution.

1. (Beginner) Write the subscription filter. Given the topic design dt/<site>/<assetType>/<deviceId>/telemetry, write one MQTT topic filter that receives telemetry from every reefer at site42, regardless of device id.

<details> <summary>Solution</summary>

dt/site42/reefer/+/telemetry — the + matches exactly one level (the device id). dt/site42/reefer/# also works but is broader (it would also match deeper sub-topics like .../TRL-7731/telemetry/raw).

Why: + matches a single level, # matches the whole remaining subtree — pick the narrowest filter that still catches what you need. </details>

2. (Beginner) Pick the QoS. You cannot afford to lose alarm messages, but the occasional duplicate is harmless. Which MQTT QoS do you use on AWS IoT Core, and what property must the consumer have?

<details> <summary>Solution</summary>

QoS 1 (at least once). AWS IoT Core does not support QoS 2, so “exactly once” isn’t on the menu; you get it in practice by making the consumer idempotent — dedupe on a (deviceId, timestamp) key so a redelivered message is a no-op.

Why: QoS 1 guarantees delivery but permits duplicates; idempotency is the standard substitute for the missing QoS 2. </details>

3. (Intermediate) Author a rule. Write an IoT Rules Engine SQL statement that, for topic dt/<site>/<assetType>/<deviceId>/telemetry, selects the device id from the topic plus temperature, and only forwards messages where a boolean field door_open is true.

<details> <summary>Solution</summary>

SELECT topic(4) AS deviceId, temperature
FROM 'dt/+/+/+/telemetry'
WHERE door_open = true

topic(4) is the device-id segment (dt=1, site=2, assetType=3, deviceId=4). The FROM filter subscribes; the WHERE drops non-matching messages before any action runs.

Why: FROM chooses which messages the rule sees, WHERE filters per message, and topic(n) is 1-based including the literal root segment. </details>

4. (Intermediate) Do the cost math. A fleet of 8,000 devices samples every 30 s. (a) How many readings per day? (b) If each device instead bundles 10 readings into one MQTT publish every 5 minutes, how many publishes per day, and what is the reduction factor? Name the AWS mechanism that makes the batched design cheaper still on the broker.

<details> <summary>Solution</summary>

(a) 8,000 × (86,400 / 30) = 8,000 × 2,880 = 23.04M readings/day. (b) Each device now publishes 1,440 / 5 = 288 times/day → 8,000 × 288 = 2.304M publishes/day — a 10× reduction in MQTT messages. Use basic ingest ($aws/rules/...) so those publishes skip per-message broker messaging charges entirely, and let edge aggregation (Greengrass or on-device batching) do the bundling.

Why: IoT cost scales with message count and size; batching at the edge plus basic ingest attacks both — the single biggest cost lever in the whole architecture. </details>

5. (Advanced) Diagnose the hot shard. Your Kinesis stream is partitioned by deviceId. CloudWatch shows one shard pinned at 100% WriteProvisionedThroughputExceeded while the others sit near idle, and IteratorAge on that shard is climbing. What is happening, and give two fixes with their trade-offs.

<details> <summary>Solution</summary>

A hot shard: a few very chatty deviceId keys hash to one shard (or a bug is sending a constant partition key, collapsing everything onto one shard). Fix 1 — use a composite partition key (deviceId#bucket) to spread the hot devices across shards, at the cost of losing strict global ordering within a device across buckets. Fix 2 — switch the stream to on-demand mode, which auto-reshards to absorb skew, at a higher per-GB price than well-tuned provisioned shards. Also confirm you aren’t accidentally sending one static partition key.

Why: same key → same shard, so skewed keys overload one shard while others idle; you trade ordering or cost to rebalance. </details>

6. (Advanced) Design the Timestream retention + query plan. You need fast live dashboards, cheap 2-year history for compliance, and you must not reject records that arrive up to 10 minutes late. Specify memory-store and magnetic-store retention, how dashboards should query, and the setting that keeps late data from being dropped.

<details> <summary>Solution</summary>

Set memory-store retention comfortably above your worst-case lateness — e.g. 12–48 h (anything ≥ ~1 h clears the 10-minute requirement with margin) so late records still fall inside the memory window and aren’t rejected. Set magnetic-store retention to 730 days for the 2-year mandate. Build scheduled queries that roll raw points into hourly min/max/avg tables, and point dashboards at those rollups (fewer bytes scanned = cheaper + faster). For pathologically late data beyond the memory window, enable magnetic-store writes. Keep S3 + Object Lock as the true compliance archive; Timestream is the serving layer. A sister walk-through of this exact compliance shape lives in the cold-chain monitoring lesson.

Why: Timestream rejects records older than the memory-store window, and query cost scales with data scanned — so the memory window must exceed max lateness, and dashboards must read rollups, not raw events. </details>

Common beginner mistakes

These are misconceptions, not symptom-and-fix tickets — each one is a wrong mental model that quietly steers a design off a cliff.

Glossary

AWSArchitectureEnterpriseReference Architecture
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments