0
# Fleet Provisioning
1
2
Device certificate provisioning and thing registration capabilities for automated fleet provisioning workflows including certificate creation from CSR, keys and certificate generation, and device thing registration through both V1 (callback-based) and V2 (Future-based) client interfaces.
3
4
## Capabilities
5
6
### V1 Identity Client (Callback-Based)
7
8
The V1 client provides callback-based operations following the traditional publish/subscribe pattern.
9
10
#### Client Creation
11
12
```python { .api }
13
class IotIdentityClient(MqttServiceClient):
14
"""
15
V1 client for AWS IoT Identity/Fleet Provisioning service with callback-based operations.
16
17
Parameters:
18
- mqtt_connection: MQTT connection (mqtt.Connection or mqtt5.Client)
19
"""
20
def __init__(self, mqtt_connection): ...
21
```
22
23
Usage example:
24
25
```python
26
from awsiot import mqtt_connection_builder, iotidentity
27
28
# Create MQTT connection with provisioning claim certificates
29
connection = mqtt_connection_builder.mtls_from_path(
30
endpoint="your-endpoint.iot.us-east-1.amazonaws.com",
31
cert_filepath="/path/to/provisioning-claim.pem.crt",
32
pri_key_filepath="/path/to/provisioning-claim.pem.key",
33
client_id="provisioning-client-123"
34
)
35
36
# Create identity client
37
identity_client = iotidentity.IotIdentityClient(connection)
38
```
39
40
#### Certificate Operations
41
42
##### Create Certificate from CSR
43
44
```python { .api }
45
def publish_create_certificate_from_csr(self, request, qos):
46
"""
47
Publish request to create certificate from Certificate Signing Request.
48
49
Parameters:
50
- request (CreateCertificateFromCsrRequest): CSR certificate request
51
- qos (awscrt.mqtt.QoS): Quality of service
52
53
Returns:
54
Future: Future that completes when request is published
55
"""
56
57
def subscribe_to_create_certificate_from_csr_accepted(self, request, qos, callback):
58
"""
59
Subscribe to successful create certificate from CSR responses.
60
61
Parameters:
62
- request (CreateCertificateFromCsrSubscriptionRequest): Subscription request
63
- qos (awscrt.mqtt.QoS): Quality of service
64
- callback: Function called with certificate response
65
66
Returns:
67
Tuple[Future, str]: Future for subscription, topic string
68
"""
69
70
def subscribe_to_create_certificate_from_csr_rejected(self, request, qos, callback):
71
"""
72
Subscribe to rejected create certificate from CSR responses.
73
74
Parameters:
75
- request (CreateCertificateFromCsrSubscriptionRequest): Subscription request
76
- qos (awscrt.mqtt.QoS): Quality of service
77
- callback: Function called when request is rejected
78
79
Returns:
80
Tuple[Future, str]: Future for subscription, topic string
81
"""
82
```
83
84
##### Create Keys and Certificate
85
86
```python { .api }
87
def publish_create_keys_and_certificate(self, request, qos):
88
"""
89
Publish request to create new private key and certificate.
90
91
Parameters:
92
- request (CreateKeysAndCertificateRequest): Keys and certificate request
93
- qos (awscrt.mqtt.QoS): Quality of service
94
95
Returns:
96
Future: Future that completes when request is published
97
"""
98
99
def subscribe_to_create_keys_and_certificate_accepted(self, request, qos, callback):
100
"""
101
Subscribe to successful create keys and certificate responses.
102
103
Parameters:
104
- request (CreateKeysAndCertificateSubscriptionRequest): Subscription request
105
- qos (awscrt.mqtt.QoS): Quality of service
106
- callback: Function called with keys and certificate response
107
108
Returns:
109
Tuple[Future, str]: Future for subscription, topic string
110
"""
111
112
def subscribe_to_create_keys_and_certificate_rejected(self, request, qos, callback):
113
"""
114
Subscribe to rejected create keys and certificate responses.
115
"""
116
```
117
118
##### Register Thing
119
120
```python { .api }
121
def publish_register_thing(self, request, qos):
122
"""
123
Publish request to register device thing with provisioning template.
124
125
Parameters:
126
- request (RegisterThingRequest): Thing registration request
127
- qos (awscrt.mqtt.QoS): Quality of service
128
129
Returns:
130
Future: Future that completes when request is published
131
"""
132
133
def subscribe_to_register_thing_accepted(self, request, qos, callback):
134
"""
135
Subscribe to successful register thing responses.
136
137
Parameters:
138
- request (RegisterThingSubscriptionRequest): Subscription request
139
- qos (awscrt.mqtt.QoS): Quality of service
140
- callback: Function called with thing registration response
141
142
Returns:
143
Tuple[Future, str]: Future for subscription, topic string
144
"""
145
146
def subscribe_to_register_thing_rejected(self, request, qos, callback):
147
"""
148
Subscribe to rejected register thing responses.
149
"""
150
```
151
152
### V2 Identity Client (Future-Based)
153
154
The V2 client provides Future-based operations with request-response semantics.
155
156
#### Client Creation
157
158
```python { .api }
159
class IotIdentityClientV2:
160
"""
161
V2 client for AWS IoT Identity/Fleet Provisioning service with Future-based operations.
162
"""
163
def __init__(self, connection): ...
164
```
165
166
#### Certificate Operations
167
168
```python { .api }
169
def create_certificate_from_csr(self, request):
170
"""
171
Create certificate from CSR using request-response pattern.
172
173
Parameters:
174
- request (CreateCertificateFromCsrRequest): CSR certificate request
175
176
Returns:
177
Future[CreateCertificateFromCsrResponse]: Future containing certificate response
178
"""
179
180
def create_keys_and_certificate(self, request):
181
"""
182
Create private key and certificate using request-response pattern.
183
184
Parameters:
185
- request (CreateKeysAndCertificateRequest): Keys and certificate request
186
187
Returns:
188
Future[CreateKeysAndCertificateResponse]: Future containing keys and certificate
189
"""
190
191
def register_thing(self, request):
192
"""
193
Register device thing using request-response pattern.
194
195
Parameters:
196
- request (RegisterThingRequest): Thing registration request
197
198
Returns:
199
Future[RegisterThingResponse]: Future containing registration response
200
"""
201
```
202
203
### Data Model Classes
204
205
#### Request Classes
206
207
```python { .api }
208
@dataclass
209
class CreateCertificateFromCsrRequest:
210
"""Request to create certificate from CSR."""
211
certificate_signing_request: str
212
213
@dataclass
214
class CreateKeysAndCertificateRequest:
215
"""Request to create private key and certificate."""
216
pass # No additional parameters required
217
218
@dataclass
219
class RegisterThingRequest:
220
"""Request to register device thing."""
221
template_name: str
222
certificate_ownership_token: str
223
parameters: Optional[Dict[str, str]] = None
224
```
225
226
#### Response Classes
227
228
```python { .api }
229
@dataclass
230
class CreateCertificateFromCsrResponse:
231
"""Response from create certificate from CSR operation."""
232
certificate_pem: Optional[str] = None
233
certificate_arn: Optional[str] = None
234
certificate_id: Optional[str] = None
235
certificate_ownership_token: Optional[str] = None
236
237
@dataclass
238
class CreateKeysAndCertificateResponse:
239
"""Response from create keys and certificate operation."""
240
certificate_pem: Optional[str] = None
241
private_key: Optional[str] = None
242
certificate_arn: Optional[str] = None
243
certificate_id: Optional[str] = None
244
certificate_ownership_token: Optional[str] = None
245
246
@dataclass
247
class RegisterThingResponse:
248
"""Response from register thing operation."""
249
device_configuration: Optional[Dict[str, str]] = None
250
thing_name: Optional[str] = None
251
```
252
253
#### Subscription Request Classes
254
255
```python { .api }
256
@dataclass
257
class CreateCertificateFromCsrSubscriptionRequest:
258
"""Subscription request for create certificate from CSR responses."""
259
pass
260
261
@dataclass
262
class CreateKeysAndCertificateSubscriptionRequest:
263
"""Subscription request for create keys and certificate responses."""
264
pass
265
266
@dataclass
267
class RegisterThingSubscriptionRequest:
268
"""Subscription request for register thing responses."""
269
template_name: str
270
```
271
272
#### Error Response Classes
273
274
```python { .api }
275
@dataclass
276
class ErrorResponse:
277
"""Error response from identity operations."""
278
error_code: Optional[str] = None
279
error_message: Optional[str] = None
280
status_code: Optional[int] = None
281
```
282
283
## Usage Examples
284
285
### V1 Client - Complete Fleet Provisioning Workflow
286
287
```python
288
from awsiot import mqtt_connection_builder, iotidentity
289
from awscrt import mqtt
290
import json
291
import os
292
293
# Step 1: Connect with provisioning claim certificate
294
connection = mqtt_connection_builder.mtls_from_path(
295
endpoint="your-endpoint.iot.us-east-1.amazonaws.com",
296
cert_filepath="/path/to/provisioning-claim.pem.crt",
297
pri_key_filepath="/path/to/provisioning-claim.pem.key",
298
client_id="fleet-provisioning-123"
299
)
300
301
identity_client = iotidentity.IotIdentityClient(connection)
302
connection.connect().result()
303
304
# Global variables to store provisioning results
305
device_certificate = None
306
device_private_key = None
307
thing_name = None
308
certificate_ownership_token = None
309
310
def on_create_keys_accepted(response):
311
"""Handle successful keys and certificate creation."""
312
global device_certificate, device_private_key, certificate_ownership_token
313
314
print("Keys and certificate created successfully!")
315
device_certificate = response.certificate_pem
316
device_private_key = response.private_key
317
certificate_ownership_token = response.certificate_ownership_token
318
319
# Save certificate and key to files
320
with open("/tmp/device-certificate.pem.crt", "w") as f:
321
f.write(device_certificate)
322
with open("/tmp/device-private.pem.key", "w") as f:
323
f.write(device_private_key)
324
325
print(f"Certificate ID: {response.certificate_id}")
326
print(f"Certificate ARN: {response.certificate_arn}")
327
328
# Step 2: Register the thing using the certificate ownership token
329
register_request = iotidentity.RegisterThingRequest(
330
template_name="MyFleetProvisioningTemplate",
331
certificate_ownership_token=certificate_ownership_token,
332
parameters={
333
"DeviceLocation": "Building-A-Floor-2",
334
"DeviceType": "TemperatureSensor"
335
}
336
)
337
identity_client.publish_register_thing(register_request, mqtt.QoS.AT_LEAST_ONCE)
338
339
def on_create_keys_rejected(error):
340
"""Handle keys and certificate creation rejection."""
341
print(f"Keys and certificate creation rejected: {error}")
342
343
def on_register_thing_accepted(response):
344
"""Handle successful thing registration."""
345
global thing_name
346
347
print("Thing registered successfully!")
348
thing_name = response.thing_name
349
350
print(f"Thing name: {thing_name}")
351
print(f"Device configuration: {response.device_configuration}")
352
353
# Save device configuration
354
if response.device_configuration:
355
with open("/tmp/device-config.json", "w") as f:
356
json.dump(response.device_configuration, f, indent=2)
357
358
print("Fleet provisioning completed successfully!")
359
360
# Now you can disconnect from the provisioning endpoint
361
# and connect using the new device certificate
362
setup_device_connection()
363
364
def on_register_thing_rejected(error):
365
"""Handle thing registration rejection."""
366
print(f"Thing registration rejected: {error}")
367
368
def setup_device_connection():
369
"""Setup permanent device connection with new certificate."""
370
print("Setting up device connection with new certificate...")
371
372
# Disconnect from provisioning connection
373
connection.disconnect().result()
374
375
# Create new connection with device certificate
376
device_connection = mqtt_connection_builder.mtls_from_path(
377
endpoint="your-endpoint.iot.us-east-1.amazonaws.com",
378
cert_filepath="/tmp/device-certificate.pem.crt",
379
pri_key_filepath="/tmp/device-private.pem.key",
380
client_id=thing_name,
381
clean_session=False,
382
keep_alive_secs=30
383
)
384
385
device_connection.connect().result()
386
print(f"Device {thing_name} connected successfully!")
387
388
# Your device application logic starts here
389
# ...
390
391
device_connection.disconnect().result()
392
393
# Subscribe to provisioning responses
394
identity_client.subscribe_to_create_keys_and_certificate_accepted(
395
iotidentity.CreateKeysAndCertificateSubscriptionRequest(),
396
mqtt.QoS.AT_LEAST_ONCE,
397
on_create_keys_accepted
398
).result()
399
400
identity_client.subscribe_to_create_keys_and_certificate_rejected(
401
iotidentity.CreateKeysAndCertificateSubscriptionRequest(),
402
mqtt.QoS.AT_LEAST_ONCE,
403
on_create_keys_rejected
404
).result()
405
406
identity_client.subscribe_to_register_thing_accepted(
407
iotidentity.RegisterThingSubscriptionRequest(template_name="MyFleetProvisioningTemplate"),
408
mqtt.QoS.AT_LEAST_ONCE,
409
on_register_thing_accepted
410
).result()
411
412
identity_client.subscribe_to_register_thing_rejected(
413
iotidentity.RegisterThingSubscriptionRequest(template_name="MyFleetProvisioningTemplate"),
414
mqtt.QoS.AT_LEAST_ONCE,
415
on_register_thing_rejected
416
).result()
417
418
# Step 1: Request keys and certificate creation
419
create_keys_request = iotidentity.CreateKeysAndCertificateRequest()
420
identity_client.publish_create_keys_and_certificate(create_keys_request, mqtt.QoS.AT_LEAST_ONCE)
421
422
print("Fleet provisioning started. Waiting for responses...")
423
```
424
425
### V1 Client - Certificate from CSR Workflow
426
427
```python
428
from awsiot import mqtt_connection_builder, iotidentity
429
from awscrt import mqtt
430
from cryptography import x509
431
from cryptography.x509.oid import NameOID
432
from cryptography.hazmat.primitives import hashes, serialization
433
from cryptography.hazmat.primitives.asymmetric import rsa
434
435
def generate_csr():
436
"""Generate a Certificate Signing Request."""
437
# Generate private key
438
private_key = rsa.generate_private_key(
439
public_exponent=65537,
440
key_size=2048
441
)
442
443
# Create CSR
444
subject = x509.Name([
445
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
446
x509.NameAttribute(NameOID.LOCALITY_NAME, "Seattle"),
447
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MyCompany"),
448
x509.NameAttribute(NameOID.COMMON_NAME, "my-iot-device"),
449
])
450
451
csr = x509.CertificateSigningRequestBuilder().subject_name(
452
subject
453
).sign(private_key, hashes.SHA256())
454
455
# Serialize CSR and private key
456
csr_pem = csr.public_bytes(serialization.Encoding.PEM).decode()
457
private_key_pem = private_key.private_bytes(
458
encoding=serialization.Encoding.PEM,
459
format=serialization.PrivateFormat.PKCS8,
460
encryption_algorithm=serialization.NoEncryption()
461
).decode()
462
463
return csr_pem, private_key_pem
464
465
# Generate CSR and private key
466
csr_pem, device_private_key = generate_csr()
467
468
# Save private key (you'll need this with the certificate)
469
with open("/tmp/device-private.pem.key", "w") as f:
470
f.write(device_private_key)
471
472
# Connect with provisioning claim certificate
473
connection = mqtt_connection_builder.mtls_from_path(
474
endpoint="your-endpoint.iot.us-east-1.amazonaws.com",
475
cert_filepath="/path/to/provisioning-claim.pem.crt",
476
pri_key_filepath="/path/to/provisioning-claim.pem.key",
477
client_id="csr-provisioning-123"
478
)
479
480
identity_client = iotidentity.IotIdentityClient(connection)
481
connection.connect().result()
482
483
def on_create_cert_from_csr_accepted(response):
484
"""Handle successful certificate creation from CSR."""
485
print("Certificate created from CSR successfully!")
486
487
# Save certificate
488
with open("/tmp/device-certificate.pem.crt", "w") as f:
489
f.write(response.certificate_pem)
490
491
print(f"Certificate ID: {response.certificate_id}")
492
print(f"Certificate ARN: {response.certificate_arn}")
493
494
# Register thing with certificate ownership token
495
register_request = iotidentity.RegisterThingRequest(
496
template_name="MyFleetProvisioningTemplate",
497
certificate_ownership_token=response.certificate_ownership_token,
498
parameters={"SerialNumber": "ABC123456"}
499
)
500
identity_client.publish_register_thing(register_request, mqtt.QoS.AT_LEAST_ONCE)
501
502
def on_create_cert_from_csr_rejected(error):
503
"""Handle certificate creation rejection."""
504
print(f"Certificate creation from CSR rejected: {error}")
505
506
# Subscribe to responses
507
identity_client.subscribe_to_create_certificate_from_csr_accepted(
508
iotidentity.CreateCertificateFromCsrSubscriptionRequest(),
509
mqtt.QoS.AT_LEAST_ONCE,
510
on_create_cert_from_csr_accepted
511
).result()
512
513
identity_client.subscribe_to_create_certificate_from_csr_rejected(
514
iotidentity.CreateCertificateFromCsrSubscriptionRequest(),
515
mqtt.QoS.AT_LEAST_ONCE,
516
on_create_cert_from_csr_rejected
517
).result()
518
519
# Request certificate creation from CSR
520
csr_request = iotidentity.CreateCertificateFromCsrRequest(
521
certificate_signing_request=csr_pem
522
)
523
identity_client.publish_create_certificate_from_csr(csr_request, mqtt.QoS.AT_LEAST_ONCE)
524
525
print("CSR provisioning started. Waiting for certificate...")
526
```
527
528
### V2 Client - Request-Response Pattern
529
530
```python
531
from awsiot import mqtt_connection_builder, iotidentity
532
import asyncio
533
534
async def fleet_provisioning_v2():
535
# Create connection with provisioning claim certificate
536
connection = mqtt_connection_builder.mtls_from_path(
537
endpoint="your-endpoint.iot.us-east-1.amazonaws.com",
538
cert_filepath="/path/to/provisioning-claim.pem.crt",
539
pri_key_filepath="/path/to/provisioning-claim.pem.key",
540
client_id="fleet-v2-client"
541
)
542
543
# Create V2 identity client
544
identity_client = iotidentity.IotIdentityClientV2(connection)
545
await connection.connect()
546
547
try:
548
# Step 1: Create keys and certificate
549
print("Creating keys and certificate...")
550
keys_request = iotidentity.CreateKeysAndCertificateRequest()
551
keys_response = await identity_client.create_keys_and_certificate(keys_request)
552
553
print(f"Certificate created: {keys_response.certificate_id}")
554
555
# Save certificate and private key
556
with open("/tmp/device-certificate.pem.crt", "w") as f:
557
f.write(keys_response.certificate_pem)
558
with open("/tmp/device-private.pem.key", "w") as f:
559
f.write(keys_response.private_key)
560
561
# Step 2: Register thing
562
print("Registering thing...")
563
register_request = iotidentity.RegisterThingRequest(
564
template_name="MyFleetProvisioningTemplate",
565
certificate_ownership_token=keys_response.certificate_ownership_token,
566
parameters={
567
"DeviceSerial": "DEV123456789",
568
"DeviceModel": "TempSensor-v2"
569
}
570
)
571
572
register_response = await identity_client.register_thing(register_request)
573
574
print(f"Thing registered: {register_response.thing_name}")
575
print(f"Device configuration: {register_response.device_configuration}")
576
577
# Save device configuration
578
if register_response.device_configuration:
579
import json
580
with open("/tmp/device-config.json", "w") as f:
581
json.dump(register_response.device_configuration, f, indent=2)
582
583
print("Fleet provisioning completed successfully!")
584
585
return {
586
"thing_name": register_response.thing_name,
587
"certificate_path": "/tmp/device-certificate.pem.crt",
588
"private_key_path": "/tmp/device-private.pem.key",
589
"device_config": register_response.device_configuration
590
}
591
592
except iotidentity.V2ServiceException as e:
593
print(f"Provisioning failed: {e.message}")
594
if e.modeled_error:
595
print(f"Error details: {e.modeled_error}")
596
return None
597
598
finally:
599
await connection.disconnect()
600
601
# Run async provisioning
602
provisioning_result = asyncio.run(fleet_provisioning_v2())
603
604
if provisioning_result:
605
print(f"Device {provisioning_result['thing_name']} provisioned successfully!")
606
607
# Connect with new device certificate
608
device_connection = mqtt_connection_builder.mtls_from_path(
609
endpoint="your-endpoint.iot.us-east-1.amazonaws.com",
610
cert_filepath=provisioning_result["certificate_path"],
611
pri_key_filepath=provisioning_result["private_key_path"],
612
client_id=provisioning_result["thing_name"]
613
)
614
615
device_connection.connect().result()
616
print("Device connected with new certificate!")
617
device_connection.disconnect().result()
618
```
619
620
### Provisioning Template Setup
621
622
Before using fleet provisioning, you need to create a provisioning template in AWS IoT Core:
623
624
```json
625
{
626
"templateName": "MyFleetProvisioningTemplate",
627
"description": "Template for provisioning temperature sensors",
628
"templateBody": {
629
"Parameters": {
630
"DeviceLocation": {
631
"Type": "String"
632
},
633
"DeviceType": {
634
"Type": "String"
635
},
636
"SerialNumber": {
637
"Type": "String"
638
}
639
},
640
"Resources": {
641
"certificate": {
642
"Properties": {
643
"CertificateId": {
644
"Ref": "AWS::IoT::Certificate::Id"
645
},
646
"Status": "Active"
647
},
648
"Type": "AWS::IoT::Certificate"
649
},
650
"policy": {
651
"Properties": {
652
"PolicyName": "TemperatureSensorPolicy"
653
},
654
"Type": "AWS::IoT::Policy"
655
},
656
"thing": {
657
"OverrideSettings": {
658
"AttributePayload": "MERGE",
659
"ThingGroups": "DO_NOTHING",
660
"ThingTypeName": "REPLACE"
661
},
662
"Properties": {
663
"AttributePayload": {
664
"location": {
665
"Ref": "DeviceLocation"
666
},
667
"deviceType": {
668
"Ref": "DeviceType"
669
},
670
"serialNumber": {
671
"Ref": "SerialNumber"
672
}
673
},
674
"ThingName": {
675
"Fn::Join": [
676
"",
677
[
678
"TempSensor-",
679
{
680
"Ref": "SerialNumber"
681
}
682
]
683
]
684
},
685
"ThingTypeName": "TemperatureSensor"
686
},
687
"Type": "AWS::IoT::Thing"
688
}
689
}
690
},
691
"enabled": true,
692
"provisioningRoleArn": "arn:aws:iam::123456789012:role/IoTFleetProvisioningRole"
693
}
694
```