Back to Blog
3 min read

Azure Time Series Insights: IoT Analytics at Scale

Time Series Insights (TSI) is purpose-built for IoT data. Store, visualize, and analyze billions of events from connected devices.

Architecture

IoT Devices → IoT Hub → Time Series Insights → Power BI / Apps

           Event Hubs

Creating TSI Environment

# Create Gen2 environment
az tsi environment gen2 create \
    --name my-tsi \
    --resource-group myRG \
    --location eastus \
    --sku name=L1 capacity=1 \
    --time-series-id-properties "[{\"name\":\"deviceId\",\"type\":\"String\"}]" \
    --storage-configuration account-name=mystorageaccount management-key=xxx

Time Series ID

Choose carefully—immutable after creation:

// Single property
"timeSeriesIdProperties": [
    {"name": "deviceId", "type": "String"}
]

// Composite (up to 3 properties)
"timeSeriesIdProperties": [
    {"name": "plantId", "type": "String"},
    {"name": "lineId", "type": "String"},
    {"name": "deviceId", "type": "String"}
]

Event Source (IoT Hub)

az tsi event-source iothub create \
    --environment-name my-tsi \
    --name iothub-source \
    --resource-group myRG \
    --location eastus \
    --consumer-group tsi-consumer \
    --iot-hub-name my-iothub \
    --key-name iothubowner \
    --shared-access-key xxx \
    --timestamp-property-name eventTimestamp

Time Series Model

Types (Templates)

{
    "id": "sensor-type-001",
    "name": "Temperature Sensor",
    "description": "Standard temperature sensor",
    "variables": {
        "temperature": {
            "kind": "numeric",
            "value": {"tsx": "$event.temperature"},
            "aggregation": {"tsx": "avg($value)"}
        },
        "status": {
            "kind": "categorical",
            "value": {"tsx": "$event.status"},
            "categories": [
                {"label": "Normal", "values": [0]},
                {"label": "Warning", "values": [1]},
                {"label": "Critical", "values": [2]}
            ]
        }
    }
}

Hierarchies

{
    "id": "facility-hierarchy",
    "name": "Facility Location",
    "source": {
        "instanceFieldNames": ["facility", "building", "floor", "room"]
    }
}

Instances

{
    "timeSeriesId": ["device-001"],
    "name": "Sensor A",
    "typeId": "sensor-type-001",
    "hierarchyIds": ["facility-hierarchy"],
    "instanceFields": {
        "facility": "Seattle",
        "building": "Building A",
        "floor": "1",
        "room": "Server Room"
    }
}

Query API

import requests

# Get access token
token_url = "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"
token_response = requests.post(token_url, data={
    "client_id": client_id,
    "client_secret": client_secret,
    "scope": "https://api.timeseries.azure.com/.default",
    "grant_type": "client_credentials"
})
access_token = token_response.json()["access_token"]

# Query time series
query_url = f"https://{env_fqdn}/timeseries/query?api-version=2020-07-31"
query = {
    "getSeries": {
        "timeSeriesId": ["device-001"],
        "searchSpan": {
            "from": "2020-11-01T00:00:00Z",
            "to": "2020-11-09T00:00:00Z"
        },
        "inlineVariables": {
            "avgTemp": {
                "kind": "numeric",
                "value": {"tsx": "$event.temperature"},
                "aggregation": {"tsx": "avg($value)"}
            }
        },
        "projectedVariables": ["avgTemp"]
    }
}

response = requests.post(query_url,
    headers={"Authorization": f"Bearer {access_token}"},
    json=query
)

Aggregate Queries

{
    "aggregateSeries": {
        "timeSeriesId": ["device-001"],
        "searchSpan": {"from": "2020-11-01", "to": "2020-11-09"},
        "interval": "PT1H",
        "inlineVariables": {
            "avgTemp": {
                "kind": "numeric",
                "value": {"tsx": "$event.temperature"},
                "aggregation": {"tsx": "avg($value)"}
            },
            "maxTemp": {
                "kind": "numeric",
                "value": {"tsx": "$event.temperature"},
                "aggregation": {"tsx": "max($value)"}
            }
        }
    }
}

Warm vs Cold Store

Warm StoreCold Store
7-31 days retentionUnlimited
Fast queriesLower cost
Included in SKUADLS Gen2

Time Series Insights: IoT analytics made accessible.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.