Back to Blog
IoT Engineering

Zero-Touch IoT Device Provisioning: Scaling from 10 to 100,000 Devices

Manual provisioning breaks at scale. Learn how AWS IoT fleet provisioning templates, JITP, and claim certificates let you ship 100,000 devices without a single manual step.

April 10, 2024
13 min read
IoT ProvisioningAWS IoT CoreFleet ManagementJITP

Zero-Touch IoT Device Provisioning: Scaling from 10 to 100,000 Devices

Shipping ten devices is easy. You SSH in, paste a certificate, set a device ID, and call it done. Shipping 100,000 devices with that same workflow means hiring a team of humans to do nothing but copy-paste credentials — and you'll still end up with provisioning errors in the field.

IoT device provisioning at scale is one of those problems that punishes you later for cutting corners earlier. This guide covers every layer: from factory floor to cloud, and from your first pilot to full fleet rollout.

Why Manual Provisioning Breaks at Scale

Manual provisioning has three failure modes that compound as fleet size grows:

Human error. Wrong certificate on the wrong device. Duplicate device IDs. Credentials committed to a config file instead of flash storage. We've debugged all of these in production.

Factory throughput. If each device needs a human to plug it into a laptop, run a script, and verify a response, you're capped at maybe 200 devices per person per day. At 50,000 units, that's weeks of labor.

Certificate rotation. When your root CA expires or is compromised, you need to re-provision every device. If you can't do that remotely and automatically, you're flying technicians to every physical installation.

The solution is zero-touch provisioning — devices authenticate themselves at first connection and receive their identity automatically.

AWS IoT Fleet Provisioning: The Architecture

AWS IoT Core supports three provisioning patterns. Understanding which to use is the first decision:

| Pattern | Best for | Trust anchor | |---|---|---| | Fleet provisioning with claim certs | Factory-scale, consistent hardware | Shared claim certificate | | Just-in-time provisioning (JITP) | Diverse hardware, existing PKI | Per-device cert signed by your CA | | Just-in-time registration (JITR) | Maximum control via Lambda | Per-device cert, custom logic |

For most greenfield IoT products, fleet provisioning with claim certificates is the right choice.

Fleet Provisioning with Claim Certificates

The claim certificate is a shared credential burned into every device at the factory. It has minimal permissions — only enough to call the provisioning API. After provisioning, the device receives a unique certificate and the claim cert becomes useless for that device.

Step 1: Create the Fleet Provisioning Template

{
  "Parameters": {
    "SerialNumber": {
      "Type": "String"
    },
    "FirmwareVersion": {
      "Type": "String"
    },
    "AWS::IoT::Certificate::Id": {
      "Type": "String"
    }
  },
  "Resources": {
    "certificate": {
      "Properties": {
        "CertificateId": {
          "Ref": "AWS::IoT::Certificate::Id"
        },
        "Status": "Active"
      },
      "Type": "AWS::IoT::Certificate"
    },
    "policy": {
      "Properties": {
        "PolicyName": "FleetDevicePolicy"
      },
      "Type": "AWS::IoT::Policy"
    },
    "thing": {
      "OverrideSettings": {
        "AttributePayload": "MERGE",
        "ThingGroups": "DO_NOTHING",
        "ThingTypeName": "REPLACE"
      },
      "Properties": {
        "AttributePayload": {
          "firmwareVersion": {
            "Ref": "FirmwareVersion"
          }
        },
        "ThingGroups": ["unprovisioned-devices"],
        "ThingName": {
          "Fn::Join": ["", ["device-", {"Ref": "SerialNumber"}]]
        },
        "ThingTypeName": "SmartSensor"
      },
      "Type": "AWS::IoT::Thing"
    }
  }
}

Step 2: ESP32 Provisioning Flow

The device connects with the claim certificate, then calls the provisioning API to receive its permanent credentials:

#include 
#include 
#include 
#include 

// Claim certificate — same on every unit from the factory extern const char CLAIM_CERT[] asm("_binary_claim_cert_pem_start"); extern const char CLAIM_KEY[] asm("_binary_claim_key_pem_start"); extern const char ROOT_CA[] asm("_binary_root_ca_pem_start");

const char* PROVISION_TEMPLATE = "FleetProvisioningTemplate"; WiFiClientSecure net; PubSubClient client(net); Preferences prefs;

bool isProvisioned() { prefs.begin("device", true); bool result = prefs.getBool("provisioned", false); prefs.end(); return result; }

void onProvisionResponse(char* topic, byte* payload, unsigned int length) { StaticJsonDocument<2048> doc; deserializeJson(doc, payload, length);

const char* certId = doc["certificateId"]; const char* certPem = doc["certificatePem"]; const char* privateKey = doc["privateKey"]; const char* iotEndpoint = doc["thingName"];

// Persist to NVS flash — survives reboots prefs.begin("device", false); prefs.putString("cert_pem", certPem); prefs.putString("private_key", privateKey); prefs.putString("thing_name", iotEndpoint); prefs.putBool("provisioned", true); prefs.end();

// Publish ownership confirmation StaticJsonDocument<256> confirm; confirm["certificateOwnershipToken"] = doc["certificateOwnershipToken"]; char buf[256]; serializeJson(confirm, buf); client.publish( "$aws/provisioning-templates/FleetProvisioningTemplate/provision/json/accepted", buf ); }

void provisionDevice() { net.setCACert(ROOT_CA); net.setCertificate(CLAIM_CERT); net.setPrivateKey(CLAIM_KEY);

client.setServer(AWS_ENDPOINT, 8883); client.setCallback(onProvisionResponse); client.connect("claim-client");

// Subscribe to provisioning response client.subscribe( "$aws/certificates/create/json/accepted" );

// Request new certificate client.publish("$aws/certificates/create/json", "{}"); }

Just-in-Time Provisioning (JITP)

JITP is ideal when devices carry per-device certificates signed by your own CA — common in industrial or healthcare IoT where devices are manufactured by a third party but must be onboarded into your cloud.

The flow: device connects with its cert → AWS IoT detects an unknown cert signed by a registered CA → triggers a Lambda → Lambda creates the Thing, attaches the policy, and activates the cert.

Register your CA with AWS:

Generate CA private key

openssl genrsa -out rootCA.key 2048

Self-signed CA cert (10 year validity)

openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.pem -subj "/C=US/O=MyCompany/CN=IoT Root CA"

Get registration code from AWS (required for verification cert)

aws iot get-registration-code

Create verification cert signed with your CA

openssl req -new -key rootCA.key -out verification.csr -subj "/CN="

openssl x509 -req -in verification.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out verification.pem -days 1

Register CA with JITP template

aws iot register-ca-certificate --ca-certificate file://rootCA.pem --verification-cert file://verification.pem --set-as-active --allow-auto-registration --registration-config file://jitp-template.json

Provisioning Hooks: Custom Validation with Lambda

Both fleet provisioning and JITR support a pre-provisioning Lambda hook. This is where you validate the device before issuing credentials — checking your manufacturing database, enforcing license limits, or logging the provisioning event to your audit trail.

import { IoTClient, DescribeThingCommand } from '@aws-sdk/client-iot'

interface ProvisioningEvent { claimCertificateId: string certificateId: string certificatePem: string templateArn: string clientId: string parameters: Record }

export const handler = async (event: ProvisioningEvent) => { const { SerialNumber, FirmwareVersion } = event.parameters

// Validate serial number against manufacturing DB const isValid = await validateSerial(SerialNumber) if (!isValid) { throw new Error(Unknown serial: ${SerialNumber}) }

// Enforce fleet size limit per account const deviceCount = await getDeviceCount() if (deviceCount >= MAX_FLEET_SIZE) { throw new Error('Fleet limit reached') }

// Return allowProvisioning + any parameter overrides return { allowProvisioning: true, parameterOverrides: { Region: getRegionFromSerial(SerialNumber), ProductLine: lookupProductLine(SerialNumber) } } }

Factory Provisioning Workflow

The physical process matters as much as the software. Here's the workflow we implement for clients at the factory level:

  • 1. PCB comes off the line — firmware is flashed via JTAG with claim cert embedded in a dedicated NVS partition.
  • 2. Factory test station — device powers on, connects to a local factory Wi-Fi network that routes only to the provisioning endpoint.
  • 3. Provisioning API call — device provisions itself, receives unique cert + Thing name.
  • 4. QR code printed — contains Thing name for the customer app to pair the device.
  • 5. Device powers off — ships in box. First customer power-on triggers normal operation with the provisioned cert.
  • This means zero provisioning happens at the customer site — the device is fully cloud-registered before it leaves the factory.

    Monitoring the Provisioning Pipeline

    Add a CloudWatch dashboard tracking:

  • ProvisioningSuccess — devices successfully provisioned per hour
  • ProvisioningFailure — with error category breakdown
  • ClaimCertUseCount — detect if claim certs are being reused (indicates leaked creds)
  • HookRejections — validation failures from your pre-provisioning Lambda
  • Set an alarm if ProvisioningFailure / ProvisioningSuccess > 0.05 — a 5% failure rate at the factory needs immediate attention.

    The Payoff

    We implemented this pipeline for a client shipping 40,000 environmental sensors. Factory throughput went from 180 units/day (manual provisioning) to 3,000 units/day (zero-touch). Provisioning errors dropped to zero. And when they needed to rotate certificates after a security audit, re-provisioning all active devices took a single API call and an OTA update — no field visits required.

    IoT device provisioning at scale is not glamorous engineering, but it is the invisible foundation that everything else depends on.

    Need help? [Contact Code Caracal](/contact) — we've shipped these systems for clients across 15+ countries.

    Written by CodeCaracal Engineering

    We write from production experience — every technique in our articles has been deployed to real clients. No academic theory.

    More Articles

    Business · 12 min read

    IoT Device Compliance: FCC, CE, and Product Certification Guide for Hardware Startups

    Business · 11 min read

    What to Look for When Hiring an IoT Development Partner: 8 Critical Criteria

    Business · 11 min read

    IoT MVP to Production: Realistic Timeline and Budget for Hardware Startups

    Business · 11 min read

    IoT Development Agency vs Building In-House: A Decision Framework for Founders

    IoT Dashboard · 13 min read

    Next.js IoT Analytics Dashboard: From Sensor Data to Production App

    Business · 11 min read

    How Much Does It Cost to Build an IoT Product in 2024? A Realistic Breakdown

    IoT Dashboard · 11 min read

    IoT Dashboard UX: Design Principles for Industrial Monitoring Interfaces

    IoT Dashboard · 12 min read

    Node.js WebSocket Server: The Real-Time Backend for IoT Dashboards

    Cloud & DevOps · 12 min read

    Containerizing IoT Backend Services with Docker: From Dev to Production

    IoT Dashboard · 14 min read

    Grafana + InfluxDB IoT Monitoring: Complete Production Setup Guide

    IoT Dashboard · 12 min read

    Building Real-Time IoT Dashboards with React and Recharts

    Cloud & DevOps · 13 min read

    CI/CD for Embedded Firmware: Automated Build, Test, and OTA Release Pipeline

    Mobile Development · 12 min read

    Flutter Offline-First IoT Apps: Hive + Sync Architecture That Works in the Field

    Cloud & DevOps · 14 min read

    Terraform for IoT Infrastructure: Provisioning AWS IoT Core, Lambda, and InfluxDB as Code

    Mobile Development · 10 min read

    Flutter IoT Alerts: Firebase Push Notifications for Device Events

    Cloud & DevOps · 12 min read

    Deploying IoT Backends on AWS: ECS Fargate vs Lambda vs EC2 Decision Guide

    Mobile Development · 11 min read

    Flutter + MQTT: Building Production IoT Mobile Apps That Scale

    Mobile Development · 13 min read

    Flutter BLE: Building a Bluetooth IoT Controller App from Scratch

    Cloud & DevOps · 13 min read

    AWS IoT Core vs Azure IoT Hub vs Google Cloud IoT: 2024 Honest Comparison

    IoT Engineering · 13 min read

    Kafka vs RabbitMQ for IoT: Choosing the Right Message Queue for High-Volume Telemetry

    IoT Engineering · 14 min read

    IoT System Testing: Unit, Integration, Hardware-in-the-Loop, and End-to-End

    IoT Engineering · 14 min read

    Predictive Maintenance with IoT Sensor Data: From Threshold to Machine Learning

    Embedded Systems · 14 min read

    IoT Bootloader Design: Secure Boot, A/B Partitions, and Reliable OTA Recovery

    IoT Engineering · 14 min read

    Multi-Tenant IoT Platform Architecture: Isolation, Scaling, and Data Partitioning

    Embedded Systems · 14 min read

    Memory Management in Embedded Firmware: Avoiding Heap Fragmentation and Stack Overflows

    IoT Engineering · 13 min read

    IoT Cost Optimization: How We Cut AWS IoT Bills by 60% Without Sacrificing Reliability

    IoT Engineering · 12 min read

    Edge Computing in IoT: When to Process On-Device vs In the Cloud

    IoT Engineering · 13 min read

    Digital Twins for IoT: Building a Virtual Mirror of Your Physical Devices

    Embedded Systems · 14 min read

    ESP32 Deep Sleep Mastery: Cutting Power Consumption from 240mA to 10µA

    IoT Engineering · 10 min read

    MQTT QoS 0, 1, and 2 Explained: Choosing the Right Level for IoT

    IoT Engineering · 14 min read

    IoT Monitoring and Observability: Metrics, Logs, and Distributed Tracing

    Embedded Systems · 14 min read

    Debugging Embedded Firmware: JTAG, GDB, Logic Analyzers, and Serial Tracing

    IoT Engineering · 12 min read

    WebSocket vs MQTT vs Server-Sent Events: Real-Time IoT Protocol Deep Dive

    Embedded Systems · 13 min read

    STM32 HAL vs Low-Level Drivers: When the Abstraction Costs You Too Much

    IoT Engineering · 13 min read

    IoT Data Pipeline: From Raw Sensor Reading to Live Dashboard in Under 100ms

    Embedded Systems · 13 min read

    UART vs SPI vs I2C: Choosing the Right Protocol for Sensor Integration

    IoT Engineering · 12 min read

    Real-Time IoT Alerting: From Simple Thresholds to ML Anomaly Detection

    Embedded Systems · 12 min read

    ESP32 Partition Table: Designing Flash Layout for Production Firmware

    IoT Engineering · 12 min read

    IoT Architecture Patterns: Hub-and-Spoke, Mesh, and Edge-Cloud Hybrid

    Embedded Systems · 13 min read

    IoT Battery Life Optimization: Engineering Devices That Last Years on a Single Charge

    IoT Engineering · 13 min read

    Time-Series Databases for IoT: InfluxDB vs TimescaleDB vs AWS Timestream

    Security · 14 min read

    Zero-Trust Security for Embedded IoT: Why Your Devices Are Probably Vulnerable

    Embedded Systems · 14 min read

    FreeRTOS on ESP32: Task Scheduling, Queues, and Resource Management for IoT

    IoT Engineering · 12 min read

    Building a Production IoT Gateway with Raspberry Pi and Node.js

    Embedded Systems · 13 min read

    ESP32 vs STM32: Choosing the Right Microcontroller for Your IoT Project

    Mobile Development · 10 min read

    Flutter + WebSocket: Building Real-Time IoT Dashboards That Don't Stutter

    IoT Engineering · 13 min read

    IoT Fleet Management at Scale: AWS IoT Core Device Registry and Provisioning

    IoT Engineering · 11 min read

    MQTT vs HTTP for IoT: Which Protocol Wins in Production?

    IoT Engineering · 12 min read

    ESP32 → MQTT → AWS IoT Core: The Production-Grade Architecture Guide

    Let's Build Together

    Got an IoT challenge?
    We've shipped it.

    Whether you need a fleet to track, a factory to monitor, or a farm to automate — our team has done it before and we'd love to build it with you. Typical response time: under 24 hours.

    No upfront commitment99.9% uptime SLANDA on requestFixed-price options