Skip to content
IoT Cloud Platforms — AWS IoT Core, Azure IoT Hub, Device Shadows, and Fleet Management

IoT Cloud Platforms — AWS IoT Core, Azure IoT Hub, Device Shadows, and Fleet Management

DodaTech Updated Jun 15, 2026 7 min read

IoT cloud platforms are managed services that provide device connectivity, authentication, data ingestion, storage, and processing at cloud scale, abstracting the complexity of managing millions of simultaneous device connections.

Why IoT Cloud Platforms Matter

Running your own MQTT broker and backend for even 1,000 devices is feasible. Doing it for 100,000 devices across 50 countries, with TLS termination, device authentication, firmware OTA updates, and 99.9% uptime, is extremely difficult. AWS IoT Core handles billions of device messages per month. Azure IoT Hub connects millions of devices with built-in security. These platforms provide what would take years to build in-house: global connectivity, device management, rules engines, and integrations with analytics and ML services.

Plain-Language Explanation

Think of an IoT cloud platform as a telephone exchange for your devices. Devices don’t call each other directly — they call the exchange, which routes the call appropriately. The exchange keeps a directory (device registry), authenticates callers (device certificates), logs calls (message routing), and provides additional services (voicemail = device shadow, call forwarding = rules engine).

Each device connects to the cloud platform securely. The platform authenticates the device using X.509 certificates, accepts telemetry data, commands, and state updates. Application code subscribes to device data through the same platform without knowing where devices are physically located.


graph TD
    subgraph "IoT Devices"
        D1[Temp Sensor 1]
        D2[Temp Sensor 2]
        D3[Temp Sensor N]
    end
    D1 -->|MQTT/TLS| AWS[AWS IoT Core]
    D2 --> AWS
    D3 --> AWS
    AWS --> Rules[Rules Engine]
    Rules --> S3[Amazon S3
Raw Data] Rules --> DynamoDB[DynamoDB
Device State] Rules --> Lambda[Lambda
Process & Alert] Rules --> SNS[SNS
SMS Alert] App[Web Dashboard] -->|HTTP/WebSocket| AWS style AWS fill:#ff9900,color:#000 style Rules fill:#3498db,color:#fff style Lambda fill:#e67e22,color:#fff

Device Shadows (AWS IoT)

A device shadow is a JSON document that stores the current state (reported) and desired state (desired) of a device. Applications read the shadow to know device state without querying the device directly.

# device_shadow.py — ESP32-style device updating its shadow
import json, time, random

# Simulate MQTT shadow update
class DeviceShadow:
    SHADOW_TOPIC = "$aws/things/MyDevice/shadow/update"

    def __init__(self, device_id: str):
        self.device_id = device_id
        self.state = {
            "reported": {
                "temperature": 24.0,
                "humidity": 55.0,
                "status": "online",
                "firmware": "v2.1.0"
            },
            "desired": {}
        }

    def update_shadow(self):
        self.state["reported"]["temperature"] = round(random.uniform(18.0, 32.0), 1)
        self.state["reported"]["humidity"] = round(random.uniform(40.0, 70.0), 1)
        # Publish to shadow topic
        payload = json.dumps({"state": {"reported": self.state["reported"]}})
        print(f"Shadow update: {payload}")
        return payload

    def check_desired_state(self, desired: dict):
        """Check if cloud wants us to change something"""
        if "led_color" in desired:
            print(f"Changing LED to: {desired['led_color']}")
        if "sample_rate" in desired:
            print(f"Setting sample rate to: {desired['sample_rate']}s")

# Simulate device running
shadow = DeviceShadow("TempSensor-42")
for _ in range(3):
    payload = shadow.update_shadow()
    time.sleep(0.5)

# Simulate receiving desired state from cloud
shadow.check_desired_state({"led_color": "blue", "sample_rate": 30})

Expected output:

Shadow update: {"state": {"reported": {"temperature": 24.7, "humidity": 55.2, "status": "online", "firmware": "v2.1.0"}}}
Shadow update: {"state": {"reported": {"temperature": 25.3, "humidity": 54.1, "status": "online", "firmware": "v2.1.0"}}}
Shadow update: {"state": {"reported": {"temperature": 23.8, "humidity": 56.0, "status": "online", "firmware": "v2.1.0"}}}
Changing LED to: blue
Setting sample rate to: 30s

Rules Engine (AWS IoT)

Route device data to other AWS services for processing:

-- AWS IoT Rule SQL: send high-temperature alerts to SNS
SELECT device_id, temperature, timestamp
FROM 'devices/+/telemetry'
WHERE temperature > 30.0
# Simulating the rule engine action
def process_telemetry(topic: str, payload: dict):
    print(f"Received: {topic} -> {payload}")
    if payload.get("temperature", 0) > 30.0:
        print(f"ALERT sent to SNS: {payload['device_id']} is hot ({payload['temperature']}°C)")
    # Store in database
    print(f"Stored to DynamoDB: {payload['device_id']} at {payload.get('timestamp', 'now')}")

# Simulated device messages
process_telemetry("devices/sensor-01/telemetry", {"device_id": "sensor-01", "temperature": 24.5, "timestamp": "2026-06-15T12:00:00Z"})
process_telemetry("devices/sensor-02/telemetry", {"device_id": "sensor-02", "temperature": 32.1, "timestamp": "2026-06-15T12:00:01Z"})

Expected output:

Received: devices/sensor-01/telemetry -> {'device_id': 'sensor-01', 'temperature': 24.5, 'timestamp': '2026-06-15T12:00:00Z'}
Stored to DynamoDB: sensor-01 at 2026-06-15T12:00:00Z
Received: devices/sensor-02/telemetry -> {'device_id': 'sensor-02', 'temperature': 32.1, 'timestamp': '2026-06-15T12:00:01Z'}
ALERT sent to SNS: sensor-02 is hot (32.1°C)
Stored to DynamoDB: sensor-02 at 2026-06-15T12:00:01Z

Fleet Management

Managing millions of devices requires:

Device registry: Each device has a unique identity (certificate ID, thing name). Group devices by type, location, or firmware version.

Fleet indexing: Search devices by attributes (location: “Building A”, firmware: “< 2.0”). Find all devices needing updates.

Bulk OTA updates: Push firmware to 10,000 devices at once. Roll back if too many fail.

Monitoring: Track connected/disconnected devices, message counts, error rates. Alert on unexpected disconnection waves.

# fleet_manager.py — simulated fleet management
import json

fleet = {
    "device-001": {"location": "Building A", "firmware": "v2.0.0", "status": "online"},
    "device-002": {"location": "Building A", "firmware": "v1.5.0", "status": "offline"},
    "device-003": {"location": "Building B", "firmware": "v2.0.0", "status": "online"},
    "device-004": {"location": "Building B", "firmware": "v1.8.0", "status": "online"},
}

def find_outdated(target_firmware: str):
    outdated = [d for d, info in fleet.items() if info["firmware"] < target_firmware]
    print(f"Devices needing update to {target_firmware}:")
    for device_id in outdated:
        print(f"  {device_id}: {fleet[device_id]['firmware']} at {fleet[device_id]['location']}")
    return outdated

def push_update(device_ids: list, new_firmware: str):
    print(f"\nPushing {new_firmware} to {len(device_ids)} devices...")
    for device_id in device_ids:
        fleet[device_id]["firmware"] = new_firmware
        print(f"  Updated {device_id}")
    print(f"Success: {len(device_ids)} devices updated")

outdated = find_outdated("v2.0.0")
push_update(outdated, "v2.0.0")

Expected output:

Devices needing update to v2.0.0:
  device-002: v1.5.0 at Building A
  device-004: v1.8.0 at Building B

Pushing v2.0.0 to 2 devices...
  Updated device-002
  Updated device-004
Success: 2 devices updated

Platform Comparison

FeatureAWS IoT CoreAzure IoT HubGoogle Cloud IoT
ProtocolMQTT, HTTP, WebSocketMQTT, AMQP, HTTPSMQTT, HTTP
Device authX.509, CognitoX.509, SAS tokensJWT, RSA keys
Device shadowYes (Device Shadow)Yes (Device Twin)Yes (Device Config)
Rules engineSQL-basedRoutes + endpointsCloud Pub/Sub
OTA updatesYes (IoT Jobs)Yes (Device Update)No built-in
Edge runtimeGreengrassIoT EdgeEdge TPU

Common Mistakes

  1. Not using device shadows: Polling devices directly for state wastes battery and bandwidth. Use shadows — the device updates its shadow, applications read the shadow.

  2. Hardcoding device credentials: Credentials burned into firmware can’t be rotated. Use a secure element or certificate enrollment system.

  3. No device grouping: Managing 10,000 devices individually is impossible. Use device groups by fleet, location, type, and firmware version.

  4. Ignoring platform costs: IoT platform costs scale with messages. A device sending data every second costs 10x more than one sending every 60 seconds. Optimize message frequency.

  5. No offline handling: When connectivity drops, devices should buffer data locally and sync on reconnection. Platforms like AWS IoT Core can queue messages for offline devices.

Practice Questions

  1. What is a device shadow and why use it? A device shadow is a JSON document storing the device’s reported state and desired state. It decouples applications from devices — apps read the shadow, not the device directly.

  2. How does AWS IoT authenticate devices? Using X.509 certificates. Each device has a unique certificate and private key. The cloud verifies the certificate during TLS handshake.

  3. What is the purpose of a rules engine in IoT cloud? It processes incoming device messages and routes them to other services (storage, analytics, alerts) based on SQL-like rules, without writing custom code.

  4. How do you push firmware updates to thousands of devices? Using fleet management features (AWS IoT Jobs, Azure Device Update). The platform manages rollout, success/failure tracking, and rollback.

  5. What happens when an IoT device goes offline? The device shadow retains the last reported state. The cloud can set desired state changes. When the device reconnects, it receives the pending desired state.

Mini Project

Simulate a fleet of devices sending telemetry to a cloud platform:

import random, time, json

class IoTDevice:
    def __init__(self, device_id: str, location: str):
        self.device_id = device_id
        self.location = location
        self.temperature = 25.0

    def read_sensors(self) -> dict:
        self.temperature += random.uniform(-0.5, 0.5)
        self.temperature = max(18.0, min(35.0, self.temperature))
        return {
            "device_id": self.device_id,
            "location": self.location,
            "temperature": round(self.temperature, 1),
            "humidity": round(random.uniform(40, 70)),
            "battery": round(random.uniform(85, 100)),
        }

class CloudPlatform:
    def __init__(self):
        self.devices = {}
        self.shadows = {}

    def connect(self, device: IoTDevice):
        self.devices[device.device_id] = device
        self.shadows[device.device_id] = {"reported": {}, "desired": {}}
        print(f"Device connected: {device.device_id}")

    def ingest_telemetry(self, data: dict):
        device_id = data["device_id"]
        self.shadows[device_id]["reported"] = data
        if data["temperature"] > 30:
            print(f"ALERT [{device_id}]: High temperature {data['temperature']}°C")

# Simulate
platform = CloudPlatform()
fleet = [
    IoTDevice("sensor-1", "Building A - Floor 1"),
    IoTDevice("sensor-2", "Building A - Floor 2"),
    IoTDevice("sensor-3", "Building B"),
]

for device in fleet:
    platform.connect(device)

print("\nSimulating telemetry (10 rounds)...")
for _ in range(10):
    for device in fleet:
        data = device.read_sensors()
        platform.ingest_telemetry(data)
    time.sleep(0.3)

Cross-References

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro