diff --git a/src/hugo/content/en-us/implementations/aws/command/command1 copy/_index.md b/src/hugo/content/en-us/implementations/aws/command/command1 copy/_index.md new file mode 100644 index 0000000..b811efe --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/command/command1 copy/_index.md @@ -0,0 +1,419 @@ +--- +title: "Command - IoT Jobs" +weight: 10 +summary: "Command and control of a device using MQTT topics" +--- + +Command and control is the operation of sending a message to a device requesting it to perform some action. Optionally, the device can acknowledge that is has succeeded or failed to complete the action. + +{{% notice note %}} +This implementation focuses on the use of MQTT topics for performing request and response patterns (e.g. command and control). Please refer to the [Designing MQTT Topic for AWS IoT Core](https://docs.aws.amazon.com/whitepapers/latest/designing-mqtt-topics-aws-iot-core/designing-mqtt-topics-aws-iot-core.html), specifically the _Using the MQTT Topics for Commands_ section. This white paper provides alternative topic patterns that go beyond the scope of this implementation. +{{% /notice %}} + +## Use Cases + +- Near real-time action taken when command issued to a device + - _I want the light to turn on when I press a button from my mobile app_ +- Interact with a peripheral, actuator, or operation of a device + - _I want to turn the LED on or off on my device_ + - _I want to remotely restart the device_ +- Request device data or state + - _I want the device to publish the latest log file to a topic_ +- Update device operating or saving configuration + - _I want to increase the current frequency of telemetry emitted on my device_ + - _I want to change my devices saved configuration that will take effect on next restart_ + +## Reference Architecture + +![Command and control via MQTT topics](architecture.svg) + +- _AWS IoT Core_ is the MQTT message broker processing messages on behalf of the clients +- _Device_ is the IoT thing to be controlled +- _Application_ is the remote logic that issues commands + +1. The _Device_ establishes an MQTT connection to the _AWS IoT Core_ endpoint, and then subscribes to the `cmd/device1/req` (request) topic. This is the topic where incoming messages will be received and processed. +1. The _Application_ establishes an MQTT connection to the _AWS IoT Core_ endpoint, and then subscribes to the `cmd/device1/resp` (response) topic. This is the topic where the acknowledgement messages from the device will be received. +1. To send a command, the _Application_ publishes a message on the `cmd/device1/req` topic, and the device receives the message on its subscription to that topic and take some action. +1. (Optional) Once the command has been processed, the device then publishes the result of the action onto the `cmd/device1/resp` topic. The _Application_ receives the response message and reconciles the outstanding action. + +{{% center %}} + +```plantuml +@startuml +!define AWSPuml https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v7.0/dist +!includeurl AWSPuml/AWSCommon.puml +!includeurl AWSPuml/InternetOfThings/all.puml +!includeurl AWSPuml/General/Client.puml + +'Comment out to use default PlantUML sequence formatting +skinparam participant { + BackgroundColor AWS_BG_COLOR + BorderColor AWS_BORDER_COLOR +} +'Hide the bottom boxes +hide footbox + +participant "<$IoTGeneric>\nDevice" as device +participant "<$IoTCore>\nMQTT Broker" as broker +participant "<$Client>\nApplication" as app + +== Connect and subscribe == +device -> broker : connect(iot_endpoint) +device -> broker : subscribe("cmd/device1/req") +app -> broker : connect(iot_endpoint) +app -> broker : subscribe("cmd/device1/resp") + +== Command operation and (optional) response == +app -> broker : publish("cmd/device1/req", "light: on", "id: 1111") +broker -> device : publish("cmd/device1/req", "light: on", "id: 1111") +device -> device : Turn on light +device -> broker : publish("cmd/device1/resp", "success", id: "1111") +broker -> app : publish("cmd/device1/resp", "success", id: "1111") +app -> app : reconcile("id: 1111") +@enduml +``` + +{{% /center %}} + +### Assumptions + +This implementation approach assumes the _Device_ is connected at all times, subscribed to a topic for incoming commands, and can receive and process the command. It also assumes that the _Device_ can notify the sending _Application_ that the command was received and processed if needed. Finally, it assumed that all three participants successfully receive, forward, and act up the message flows in either direction. + +## Implementation + +Both the _Application_ and the _Device_ use similar approaches to connecting, sending, and receiving messages. The code samples below are complete for each participant. + +{{% notice note %}} +The code samples focus on the _command_ design in general. Please refer to the [Getting started with AWS IoT Core](https://docs.aws.amazon.com/iot/latest/developerguide/iot-gs.html) for details on creating things, certificates, and obtaining your endpoint. The code samples below are used to demonstrate the basic capability of the _Command_ pattern. +{{% /notice %}} + +### Device + +The _Device_ code focuses on connecting to _Broker_ and then subscribing to a topic to receive commands. Upon receipt of a command, the _Device_ performs some local action and then publishes the outcome (success, failure) of the command back to the _Application_, which then can reconcile the command. + +The _Device_ will continue to receive and respond to commands until stopped. + +{{< tabs groupId="device-code">}} +{{% tab name="python" %}} +Please refer to this [pubsub sample](https://github.com/aws/aws-iot-device-sdk-python-v2/blob/main/samples/pubsub.py) for more details. + +- Install SDK from PyPI: `python3 -m pip install awsiotsdk` +- Replace the global variables with valid endpoint, clientId, and credentials +- Start in a separate terminal session before running the _Application_: `python3 client.py` + +```python +# client.py - Demonstrates waiting for a command to be evaluated and processed +from awscrt import io, mqtt +from awsiot import mqtt_connection_builder +import sys +import time + + +# This sample uses the Message Broker for AWS IoT to send and receive messages +# through an MQTT connection. On startup, the device connects to the server, +# subscribes to a request topic, invokes a callback upon receipt of a message, and +# then responds on the response topic back to the calling application. + +io.init_logging(getattr(io.LogLevel, "Info"), "stderr") + +# Using globals to simplify sample code +client_id = "device1" +endpoint = "REPLACE_WITH_YOUR_ENDPOINT_FQDN" +client_certificate = "PATH_TO_CLIENT_CERTIFICATE_FILE" +client_private_key = "PATH_TO_CLIENT_PRIVATE_KEY_FILE" +root_ca = "PATH_TO_ROOT_CA_CERTIFICATE_FILE" + + +# Topic to subscribe for incoming commands +request_topic = "cmd/device1/req" +# Topic to send result of command +response_topic = "cmd/device1/resp" + + +# Callback's are the main method to asynchronously process MQTT events +# using the device SDKs. + +# Callback when connection is accidentally lost. +def on_connection_interrupted(connection, error, **kwargs): + print(f"Connection interrupted. error: {error}") + + +# Callback when an interrupted connection is re-established. +def on_connection_resumed(connection, return_code, session_present, **kwargs): + print( + f"Connection resumed. return_code: {return_code} session_present: {session_present}" + ) + + if return_code == mqtt.ConnectReturnCode.ACCEPTED and not session_present: + print("Session did not persist. Resubscribing to existing topics...") + resubscribe_future, _ = connection.resubscribe_existing_topics() + + # Cannot synchronously wait for resubscribe result because we're on the connection's event-loop thread, + # evaluate result with a callback instead. + resubscribe_future.add_done_callback(on_resubscribe_complete) + + +def on_resubscribe_complete(resubscribe_future): + resubscribe_results = resubscribe_future.result() + print(f"Resubscribe results: {resubscribe_results}") + + for topic, qos in resubscribe_results["topics"]: + if qos is None: + sys.exit(f"Server rejected resubscribe to topic: {topic}") + + +# Callback when the subscribed topic receives a command message +def on_message_received(topic, payload, dup, qos, retain, **kwargs): + global response_topic + print(f"Received command from topic '{topic}': {payload}") + + # Action to perform on receiving a command, assert True for this example + ################################################# + ############### COMMAND CODE HERE ############### + ################################################# + # result = do_something(payload) + result = True + + if result: + message = f"SUCCESS: command ran successfully from payload: {payload}" + else: + message = f"FAILURE: command did not run successfully from payload: {payload}" + print(f"Publishing message to topic '{response_topic}': {message}") + mqtt_connection.publish( + topic=response_topic, payload=message, qos=mqtt.QoS.AT_LEAST_ONCE + ) + + +if __name__ == "__main__": + # Create SDK-based resources + event_loop_group = io.EventLoopGroup(1) + host_resolver = io.DefaultHostResolver(event_loop_group) + client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver) + + # Create native MQTT connection from credentials on path (filesystem) + mqtt_connection = mqtt_connection_builder.mtls_from_path( + endpoint=endpoint, + cert_filepath=client_certificate, + pri_key_filepath=client_private_key, + client_bootstrap=client_bootstrap, + ca_filepath=root_ca, + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=client_id, + clean_session=False, + keep_alive_secs=6, + ) + + print(f"Connecting to {endpoint} with client ID '{client_id}'...") + + connect_future = mqtt_connection.connect() + + # Future.result() waits until a result is available + connect_future.result() + print("Connected!") + + # Subscribe + print(f"Subscribing to topic '{request_topic}'...") + subscribe_future, packet_id = mqtt_connection.subscribe( + topic=request_topic, qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_message_received + ) + + subscribe_result = subscribe_future.result() + print(f"Subscribed with {str(subscribe_result['qos'])}") + + # All logic happens in the on_message_received() callback, loop until + # program stopped (e.g., CTRL+C) + print(f"Listening for commands on topic: {request_topic}") + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + # Disconnect + print("Disconnecting...") + disconnect_future = mqtt_connection.disconnect() + disconnect_future.result() + print("Disconnected!") +``` + +{{% /tab %}} +{{< /tabs >}} + +### Application + +The _Application_ code connects to the Broker and subscribes to the _Device_'s response topic. It then waits for a command to be entered and sends that to the _Device_'s request topic. + +{{< tabs groupId="device-code">}} +{{% tab name="python" %}} + +Please refer to this [pubsub sample](https://github.com/aws/aws-iot-device-sdk-python-v2/blob/main/samples/pubsub.py) for more details. + +- Install SDK from PyPI: `python3 -m pip install awsiotsdk` +- Replace the global variables with valid endpoint, clientId, and credentials +- Start in a separate terminal session after the _Device_ code is running: `python3 application.py` + +```python +# application.py - Demonstrates sending commands to a device +from awscrt import io, mqtt +from awsiot import mqtt_connection_builder +import sys +import time + + +# This sample uses the Message Broker for AWS IoT to send and receive messages +# through an MQTT connection. On startup, the app connects to the server, +# subscribes to a response topic which is invoked via a callback upon receipt of a message. +# Commands are sent + +io.init_logging(getattr(io.LogLevel, "Info"), "stderr") + +# Using globals to simplify sample code +client_id = "app1" +endpoint = "REPLACE_WITH_YOUR_ENDPOINT_FQDN" +client_certificate = "PATH_TO_CLIENT_CERTIFICATE_FILE" +client_private_key = "PATH_TO_CLIENT_PRIVATE_KEY_FILE" +root_ca = "PATH_TO_ROOT_CA_CERTIFICATE_FILE" + +# Topics for the test target device +target_device = "device1" +# Topic to send a command to the device +request_topic = f"cmd/{target_device}/req" +# Topic to subscribe for command responses +response_topic = f"cmd/{target_device}/resp" + + +# Callback's are the main method to asynchronously process MQTT events +# using the device SDKs. + +# Callback when connection is accidentally lost. +def on_connection_interrupted(connection, error, **kwargs): + print(f"Connection interrupted. error: {error}") + + +# Callback when an interrupted connection is re-established. +def on_connection_resumed(connection, return_code, session_present, **kwargs): + print( + f"Connection resumed. return_code: {return_code} session_present: {session_present}" + ) + + if return_code == mqtt.ConnectReturnCode.ACCEPTED and not session_present: + print("Session did not persist. Resubscribing to existing topics...") + resubscribe_future, _ = connection.resubscribe_existing_topics() + + # Cannot synchronously wait for resubscribe result because we're on the connection's event-loop thread, + # evaluate result with a callback instead. + resubscribe_future.add_done_callback(on_resubscribe_complete) + + +def on_resubscribe_complete(resubscribe_future): + resubscribe_results = resubscribe_future.result() + print(f"Resubscribe results: {resubscribe_results}") + + for topic, qos in resubscribe_results["topics"]: + if qos is None: + sys.exit(f"Server rejected resubscribe to topic: {topic}") + + +# Callback when the subscribed topic receives a command message +def on_message_received(topic, payload, dup, qos, retain, **kwargs): + global response_topic, target_device + + print(f"Received command response on topic '{topic}' with payload: {payload}") + + # Action to perform on device completing the command + ################################################# + ############### COMMAND CODE HERE ############### + ################################################# + # result = reconcile_command(payload) + + +if __name__ == "__main__": + # Create SDK-based resources + event_loop_group = io.EventLoopGroup(1) + host_resolver = io.DefaultHostResolver(event_loop_group) + client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver) + + # Create native MQTT connection from credentials on path (filesystem) + mqtt_connection = mqtt_connection_builder.mtls_from_path( + endpoint=endpoint, + cert_filepath=client_certificate, + pri_key_filepath=client_private_key, + client_bootstrap=client_bootstrap, + ca_filepath=root_ca, + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=client_id, + clean_session=False, + keep_alive_secs=6, + ) + + print(f"Connecting to {endpoint} with client ID '{client_id}'...") + + connect_future = mqtt_connection.connect() + + # Future.result() waits until a result is available + connect_future.result() + print("Connected!") + + # Subscribe to the device response topic + print(f"Subscribing to topic '{response_topic}'...") + subscribe_future, packet_id = mqtt_connection.subscribe( + topic=response_topic, qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_message_received + ) + + subscribe_result = subscribe_future.result() + print(f"Subscribed with {str(subscribe_result['qos'])}") + + # All logic happens in the on_message_received() callback, loop until + # program stopped (e.g., CTRL+C) + try: + while True: + command = input(f"Enter command to send to {target_device}: ") + print(f"Publishing command to topic '{request_topic}': {command}") + mqtt_connection.publish( + topic=request_topic, payload=command, qos=mqtt.QoS.AT_LEAST_ONCE + ) + # Wait for response text before requesting next input + time.sleep(2) + + except KeyboardInterrupt: + # Disconnect + print("Disconnecting...") + disconnect_future = mqtt_connection.disconnect() + disconnect_future.result() + print("Disconnected!") +``` + +{{% /tab %}} +{{< /tabs >}} + +## Considerations + +This implementation covers the basics of a command and control pattern. It does not cover certain aspects that may arise in production use. + +### Duplicate, or Out-of-Order Messages + +In normal operation, MQTT command messages may be lost between the _application_ and the _broker_, or between the _broker_ and the _device_. This can be caused by QoS settings, retransmissions or retries from the publishers or _broker_, or other internal application errors such as a blocked thread. + +For duplicate or out-of-order message, a command should nominally only be processed once, the inclusion of a unique transaction id (TID) [(see _Command_ pattern examples)]({{< ref "/patterns/command/#examples" >}}) in both the request and response provides the _application_ to resolve the message. For duplicate messages, the _device_ can keep a list of recently processed messages and only act on new TIDs. + +For out-of-order messages, the TID can encompass either an incrementing number or sequence, and the _device_ can track the order of messages received. Another approach could be to use a [timestamp](https://en.wikipedia.org/wiki/Timestamp) for a TID where the _device_ tracks the last processed message and if it receives one with a timestamp earlier, can discard or otherwise act on the message out of order. + +When dealing with _command_ messages that need to be processed in a certain order (e.g., **A**, then **B**, then **C**), both the _device_ and _application_ should agree on a state of commands and respond accordingly if they come out of order. For instance, if a _device_ receives command **A** (valid) and then command **C** (invalid as the next command should have been **B**), it should report back a failure and notify the calling _application_ of it's current state. + +### Lost Messages or Device Disconnected During Delivery + +Another consideration is how a command message should be tracked and processed if it is never delivered. It is common for IoT devices to be offline to the _broker_ for periods of time. This could be due to the device being turned off, local or widespread ISP outages, or due to intermittent network issues. Also, even with the use of a [QoS of 1](https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html#mqtt-qos), there is still the potential for message loss. + +The _application_ should have logic to track outstanding commands, such as a timer to retransmit the command if it does not receive a response in a certain period of time with [retry logic](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/). In the event the device does not respond within a set period of time, the _application_ can raise an error for device connectivity. + +From a _device_ perspective, if it is disconnected during the processing of command or series of commands, the _device_ should publish an error once it reconnects. This can give the _application_ making the request context for how to proceed. + +### Fan-out to a Group of Related Devices + +This implementation is focused on a single _application_ and _device_ interaction. Some use cases may have a single command affecting multiple devices. For instance, an _application_ may request all warning lights to turn on a factory floor in the event of an emergency. Or, it could be requesting a fleet of devices to report back their current configuration and status. + +For a small group of _devices_, an approach could be for the _application_ to send an individual command to each device and reconcile the response. + +However, when the target _device_ group is large, this would require the _application_ to send 1 per-_device_. In this case, _devices_ could subscribe to a common topic for fan-out types of messages, and the application only needs to send out a single message and reconcile the individual _device_ responses. + +For instance, `device1`, `device2`, and `device3`, can all subscribe to the topic `device_status/request`. When the _application_ publishes a message on that topic, each device will receive a copy of the message, can act upon it, and each can the publish their response to a common `device_status/response` topic. The _application_ or [AWS IoT Rules Engine](https://docs.aws.amazon.com/iot/latest/developerguide/iot-rules.html) can then reconcile the responses. This pattern is covered in the [Designing MQTT Topics for AWS IoT Core whitepaper](https://docs.aws.amazon.com/whitepapers/latest/designing-mqtt-topics-aws-iot-core/mqtt-design-best-practices.html). diff --git a/src/hugo/content/en-us/implementations/aws/command/command1 copy/architecture.svg b/src/hugo/content/en-us/implementations/aws/command/command1 copy/architecture.svg new file mode 100644 index 0000000..8a18854 --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/command/command1 copy/architecture.svg @@ -0,0 +1,3 @@ + + +
AWS Cloud
AWS Cloud
 SUB: cmd/device1/resp 
 SUB: cmd/device1/resp 
 SUB: cmd/device1/req 
 SUB: cmd/device1/req 
 PUB: cmd/device1/resp 
 PUB: cmd/device1/resp 
Device
Device
AWS IoT Core
MQTT Broker
AWS IoT Core...
 PUB: cmd/device1/req 
 PUB: cmd/device1/req 
Application
1
1
2
2
3
3
4
4
MQTT Connection
(for all publish and subscribe operations)
MQTT Connection...
3
3
4
4
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/JITP.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/JITP.png new file mode 100644 index 0000000..04b2f78 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/JITP.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/_index.md b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/_index.md new file mode 100644 index 0000000..883a5e3 --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/_index.md @@ -0,0 +1,39 @@ +--- +title: Just in Time Provisioning (JITP) +weight: 10 +hidden: true +--- + +AWS IoT provides options to provision and onboard a large number of devices based +on the capabilities of the device and if the devices have their unique X.509 certificate +and private keys on them before being sold to the end customer. +If the manufacturing chain allows the device maker to provision unique credentials into +the device at manufacturing time or in distribution, device makers can use Just in Time +Provisioning, Just in Time Registration, or Multi-Account Registration. + +{{% notice note %}} +Devices that use JITP have certificates and private keys present on the device before +onboarding to AWS IoT. The certificates must be signed with the customer’s designated +CA, and that CA must be registered in AWS IoT. The customer must know which +account the device will connect to before provisioning. +{{% /notice %}} + +## Setup + +Using JITP, the device connects to AWS IoT, and the certificate’s signature is verified +against the registered CA. After verification, a provisioning template registers the Thing, +certificate, and assigns a policy to the device. The device maker is responsible for +registering the signer CA and attaching a provisioning template to the CA. + +## Device Logic + +When the device connects to AWS IoT Core for the first time, the device certificate, and +the signer CA that is registered with AWS IoT must be sent during the TLS handshake. +The TLS handshake will fail at the first connection. This happens because the certificate +has not been pre-loaded into the AWS IoT account. The device-supplied certificate is +registered and activated in AWS IoT during the provisioning process. The device must +have logic to reconnect to AWS IoT after a short time period. If the provisioning +operation has succeeded, the device will connect to AWS IoT successfully. + +![JITP](JITP.png) + diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/_index.md b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/_index.md new file mode 100644 index 0000000..0057f90 --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/_index.md @@ -0,0 +1,73 @@ +--- +hidden: false +# Placeholder to organize patterns - will be hidden when menu navigation becomes untenable +title: "1. Register root CA" +--- + +To set up a JITP environment with AWS IoT Core, first register your CA with AWS IoT Core, then attach a provisioning template to your CA. In this step we will create a self-signed root CA and register it with AWS IoT Core. +1. Ensure that you have OpenSSL installed. +2. Run the following OpenSSL command to create a device root CA private key +```json +openssl genrsa -out deviceRootCA.key 2048 +``` +3. Create a custom OpenSSL.conf file by running following command +```json +sudo nano deviceRootCA_openssl.conf +``` +4. Copy and Paste the following configuration and save the .conf file by pressing control + X and type Y to save. +```json +[ req ] +distinguished_name = Distinguished_Name +extensions = v3_ca +req_extensions = v3_ca + +[ v3_ca ] +basicConstraints = CA:TRUE + +[ Distinguished_Name ] +countryName = Country Name (2 letter code) +countryName_default = IN +countryName_min = 2 +countryName_max = 2 +organizationName = Organization Name (eg, company) +organizationName_default = AWS +``` +5. You can then run the following Linux command to confirm that the OpenSSL.conf file was created +```json +cat deviceRootCA_openssl.conf +``` +4. Run the following OpenSSL command to create a device root CA certificate signing request (CSR) +```json +openssl req -new -sha256 -key deviceRootCA.key -nodes -out deviceRootCA.csr -config deviceRootCA_openssl.conf +``` +5. Run the following OpenSSL command to create a device root CA certificate using the CSR generated +```json +openssl x509 -req -days 365 -extfile deviceRootCA_openssl.conf -extensions v3_ca -in deviceRootCA.csr -signkey deviceRootCA.key -out deviceRootCA.pem +``` +6. Run the following AWS CLI command to get the registration code for the AWS Region that you want to use JITP in. Make sure to change region to your region of choice. +```json +aws iot get-registration-code --region ap-south-1 +``` +`Note: Make sure you have IAM role attached (if using EC2 to run these commands) or AWS Credentials configured with necessary permissions to get the registration code.` +`Save the registration code for next step.` + +7. Run the following OpenSSL command to create a verification key +```json +openssl genrsa -out verificationCert.key 2048 +``` +8. Run the following OpenSSL command to create a verification certificate CSR +```json +openssl req -new -key verificationCert.key -out verificationCert.csr +``` + +`Note: Enter the Registration Code in the Common Name field. For example: Common Name (server FQDN or YOUR name) []: xxxxxxxx8a33da. Leave the other fields blank.` +![register](register.png) +9. Run the following OpenSSL command to create the verification certificate: +```json +openssl x509 -req -in verificationCert.csr -CA deviceRootCA.pem -CAkey deviceRootCA.key -CAcreateserial -out verificationCert.crt -days 500 -sha256 +``` + +`Note: The registration code of your root CA’s Region is required for the verification certificate to be certified by AWS IoT Core.` + +## Checkpoint +![Checkpoint](checkpoint.png) \ No newline at end of file diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/checkpoint.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/checkpoint.png new file mode 100644 index 0000000..0814a00 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/checkpoint.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/register.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/register.png new file mode 100644 index 0000000..829404d Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step1/register.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/_index.md b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/_index.md new file mode 100644 index 0000000..9ea201d --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/_index.md @@ -0,0 +1,30 @@ +--- +hidden: false +# Placeholder to organize patterns - will be hidden when menu navigation becomes untenable +title: "2. Create and register a JITP template" +--- + +1. Create an IAM role for your AWS IoT Core service and name it JITP_demo. Attach Policy AWSIoTThingsRegistration to this role. +![iam](iam.png) +2. Create a JITP template JSON file by running following commands +```json +sudo nano jitp_template.json +``` +3. Paste the following JSON and save the file using control + X and type Y + +`Replace , and with your AWS Region, Account_ID and Role ARN for JITPRole respectively` +```json +{ + "templateBody":"{ \"Parameters\" : { \"AWS::IoT::Certificate::CommonName\" : { \"Type\" : \"String\" },\"AWS::IoT::Certificate::Country\" : { \"Type\" : \"String\" }, \"AWS::IoT::Certificate::Id\" : { \"Type\" : \"String\" }}, \"Resources\" : { \"thing\" : { \"Type\" : \"AWS::IoT::Thing\", \"Properties\" : { \"ThingName\" : {\"Ref\" : \"AWS::IoT::Certificate::CommonName\"}, \"AttributePayload\" : { \"version\" : \"v1\", \"country\" : {\"Ref\" : \"AWS::IoT::Certificate::Country\"}} } }, \"certificate\" : { \"Type\" : \"AWS::IoT::Certificate\", \"Properties\" : { \"CertificateId\": {\"Ref\" : \"AWS::IoT::Certificate::Id\"}, \"Status\" : \"ACTIVE\" } }, \"policy\" : {\"Type\" : \"AWS::IoT::Policy\", \"Properties\" : { \"PolicyDocument\" : \"{ \\\"Version\\\": \\\"2012-10-17\\\", \\\"Statement\\\": [ { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Connect\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:::client\\\/*\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Publish\\\", \\\"iot:Receive\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:::topic\\\/*\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Subscribe\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:::topicfilter\\\/*\\\" ] } ] }\" } } } }", + "roleArn":"" +} +``` +4. Run the following register-ca-certificate command to register the device root CA as a CA certificate in AWS IoT Core. Make sure to set correct region at --region flag +```json +aws iot register-ca-certificate --ca-certificate file://deviceRootCA.pem --verification-cert file://verificationCert.crt --set-as-active --allow-auto-registration --registration-config file://jitp_template.json --region ap-south-1 +``` + +## Checkpoint +Adding the parameter --registration-config attaches the JITP template that you created to the CA certificate. The command response will return the ARN of the CA certificate. + +![Checkpoint](checkpointARN.png) \ No newline at end of file diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/checkpointARN.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/checkpointARN.png new file mode 100644 index 0000000..4eb26c9 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/checkpointARN.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/iam.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/iam.png new file mode 100644 index 0000000..aaa97f9 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step2/iam.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/_index.md b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/_index.md new file mode 100644 index 0000000..ef75865 --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/_index.md @@ -0,0 +1,58 @@ +--- +hidden: false +# Placeholder to organize patterns - will be hidden when menu navigation becomes untenable +title: "3. Perform JITP" +--- +In this step we will create a Device certificate using self-signed root CA and on-board the device using JITP. + +1. Download the RootCA1 and save it with the file name AmazonRootCA1.pem. +`Note: The RootCA1 is used for server-side authentication of publish requests to AWS IoT Core. For more information, see CA certificates for server authentication.` + +2. Run the following OpenSSL command to create a device private key +```json +wget https://www.amazontrust.com/repository/AmazonRootCA1.pem +openssl genrsa -out deviceCert.key 2048 +``` + +3. Run the following OpenSSL command to create a device CSR +```json +openssl req -new -key deviceCert.key -out deviceCert.csr +``` + +`Note: The example JITP template requires the ThingName value to equal the certificate’s CommonName value. The template also requires the CountryName value to equal the Country value in the CA certificate. Refer to the following example:` +![ThingName](thingName.png) + +4. Run the following OpenSSL command to create a device certificate: +```json +openssl x509 -req -in deviceCert.csr -CA deviceRootCA.pem -CAkey deviceRootCA.key -CAcreateserial -out deviceCert.crt -days 365 -sha256 +``` +5. Run the following command to combine the root CA certificate and device certificate +```json +cat deviceCert.crt deviceRootCA.pem > deviceCertAndCACert.crt +``` +6. Now connect to your AWS IoT Core endpoint using deviceCertAndCACert.crt (Device certificate), deviceCert.key (Device key) and AmazonRootCA1.pem (root CA). +Download and setup the python sdk +```json +git clone https://github.com/aws/aws-iot-device-sdk-python.git +cd aws-iot-device-sdk-python +sudo python setup.py install +``` + +7. Navigate to Samples folder and execute the basicPubSub. +Change the endpoint as per your AWS IoT Core endpoint, also ensure that you state correct paths and names for certificate files. +```json +cd ~/aws-iot-device-sdk-python/samples/basicPubSub/ +python basicPubSub.py -e a2x566pxx9rcsu-ats.iot.ap-south-1.amazonaws.com -r ~/AmazonRootCA1.pem -c ~/deviceCertAndCACert.crt -k ~/deviceCert.key -id "JITP_Demo_Device" -t "test" -M "Hello World" +``` + +8. The First connection will timeout and fail. But JITP will now create a new Thing named JITP_Demo_Device (as per the Common Name set in CSR), register the device certificate and attach the security policy as per the JITP_template. +The subsequent connections should be successful. + +## Checkpoint +Verify that you use correct file names and paths. +Check if the Country Name parameter in certificates is consistent. +Check if you have deviceCertAndCACert.crt and deviceCert.key files generated in this step. +Finally verify if a Thing named JITP_Demo_Device is created in your AWS IoT console. + + +![things](things.png) \ No newline at end of file diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/thingName.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/thingName.png new file mode 100644 index 0000000..219af42 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/thingName.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/things.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/things.png new file mode 100644 index 0000000..3e373c2 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/JITP/step3/things.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP.png new file mode 100644 index 0000000..04b2f78 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP_arch.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP_arch.png new file mode 100644 index 0000000..432e047 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP_arch.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP_arch_1.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP_arch_1.png new file mode 100644 index 0000000..0aa26c5 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/JITP_arch_1.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/Untitled-1 b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/Untitled-1 new file mode 100644 index 0000000..866c0f5 --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/Untitled-1 @@ -0,0 +1,4 @@ +{ + "templateBody":"{ \"Parameters\" : { \"AWS::IoT::Certificate::CommonName\" : { \"Type\" : \"String\" },\"AWS::IoT::Certificate::Country\" : { \"Type\" : \"String\" }, \"AWS::IoT::Certificate::Id\" : { \"Type\" : \"String\" }}, \"Resources\" : { \"thing\" : { \"Type\" : \"AWS::IoT::Thing\", \"Properties\" : { \"ThingName\" : {\"Ref\" : \"AWS::IoT::Certificate::CommonName\"}, \"AttributePayload\" : { \"version\" : \"v1\", \"country\" : {\"Ref\" : \"AWS::IoT::Certificate::Country\"}} } }, \"certificate\" : { \"Type\" : \"AWS::IoT::Certificate\", \"Properties\" : { \"CertificateId\": {\"Ref\" : \"AWS::IoT::Certificate::Id\"}, \"Status\" : \"ACTIVE\" } }, \"policy\" : {\"Type\" : \"AWS::IoT::Policy\", \"Properties\" : { \"PolicyDocument\" : \"{ \\\"Version\\\": \\\"2012-10-17\\\", \\\"Statement\\\": [ { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Connect\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:ap-south-1:651398803543:client\\\/*\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Publish\\\", \\\"iot:Receive\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:ap-south-1:651398803543:topic\\\/*\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Subscribe\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:ap-south-1:651398803543:topicfilter\\\/*\\\" ] } ] }\" } } } }", + "roleArn":"arn:aws:iam::651398803543:role/service-role/jitp_demo" +} diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/_index.md b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/_index.md new file mode 100644 index 0000000..1e03edf --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/_index.md @@ -0,0 +1,143 @@ +--- +title: "Provisioning - JITP" +weight: 10 +summary: "Command and control of a device using MQTT topics" +--- + +In an IoT deployment, security is often the number one concern from both device +customers and device manufacturers. To protect and encrypt data in transit from an IoT +device to AWS IoT Core, AWS IoT Core supports TLS-based mutual authentication +using X.509 certificates. AWS IoT provides options to provision and onboard a large number of devices based +on the capabilities of the device and if the devices have their unique X.509 certificate +and private keys on them before being sold to the end customer. +If the manufacturing chain allows the device maker to provision unique credentials into +the device at manufacturing time, device makers can use Just in Time +Provisioning. + +To use JITP, devices should have certificates and private keys present on the device before onboarding to AWS IoT. Certificate authority (CA) is an entity that issues digital certificates. The device certificates saved on your devices must be signed with your designated CA, and that CA must be registered in AWS IoT. +{{% notice note %}} +To use JITP you have to maintain your PKI infrastructure, this can also be done using managed service like Amazon Certificate Manager. The manufacturing facility should have capability to create a private key, CA signed certificate and store the key-certificate pair on the device memory. +{{% /notice %}} + +## Use cases +Just-in-time provisioning (JITP) is the recommended approach when the manufacturing chain allows the device maker to provision unique credentials into the device at manufacturing time. To implement JITP the manufacturing chain has to be trusted and should have the capability to create a device certificate signed by a self-managed CA. You can have your devices provisioned when they first attempt to connect to AWS IoT with just-in-time provisioning (JITP). + +Example use cases where JITP should be used are: + +1. When device originates from an original device manufacturer and you have the ability to include unique key material. +2. When maintaining public key infrastructure (PKI) is feasible. +3. There is a trust channel between your PKI and the manufacturing facility. + +## Reference architecture + +![JITP](JITP_arch_1.png) +The details of this flow are as follows: +1. Private key and signed certificate pair is created using PKI. PKI can be self-managed or using a managed service like Amazon ACM. +2. This pair is securely copied and stored on the device memory. +3. Using JITP, the device connects to AWS IoT, and the certificate’s signature is verified +against the registered CA. After verification, a provisioning template registers the Thing, +certificate, and assigns a policy to the device. A provisioning template is a JSON document that uses parameters to describe the resources your device must use to interact with AWS IoT. +4. When the device connects to AWS IoT Core for the first time, the device certificate, and +the signer CA that is registered with AWS IoT must be sent during the TLS handshake. +5. The TLS handshake will fail at the first connection. This happens because the certificate +has not been pre-loaded into the AWS IoT account. +6. The device-supplied certificate is +registered and activated in AWS IoT during the provisioning process. +7. The device must +have logic to reconnect to AWS IoT after a short time period. +8. If the provisioning +operation has succeeded, the device will connect to AWS IoT successfully. + +Below diagram describes end-to-end just-in-time provisioning flow: + +![JITP](JITP.png) + + +## Implementation +To use JITP you need to have control over your Certificate Authority. This can be either done by self-managed root CA or using ACM Private CA. ACM Private CA enables creation of private certificate authority (CA) hierarchies, including root and subordinate CAs, without the investment and maintenance costs of operating an on-premises CA. Your private CAs can issue end-entity X.509 certificates useful in scenarios including: + +1. Creating encrypted TLS communication channels +2. Authenticating users, computers, API endpoints, and IoT devices +3. Cryptographically signing code +4. Implementing Online Certificate Status Protocol (OCSP) for obtaining certificate revocation status + +ACM Private CA operations can be accessed from the AWS Management Console, using the ACM Private CA API, or using the AWS CLI. + +#### Register Certificate Authority with AWS IoT Core + +To set up a JITP environment with AWS IoT Core, first register your CA (Certificate Authority) with AWS IoT Core. +You can either [register a self-managed CA](https://docs.aws.amazon.com/iot/latest/developerguide/register-CA-cert.html) or [register ACM managed private CA](https://iot-device-management.workshop.aws/en/provisioning-options/bring-your-own-ca.html) with AWS IoT. + +--- +#### Create and register a provisioning template +--- +A [provisioning template](https://docs.aws.amazon.com/iot/latest/developerguide/provision-template.html) is a JSON document that uses parameters to describe the resources your device must use to interact with AWS IoT. IoT Core will use this template to register a new device with certificate signed by a registered CA. + +1. Create an IAM role for your AWS IoT Core service and name it JITP_demo. Attach Policy AWSIoTThingsRegistration to this role. +![iam](iam.png) +2. Create a JITP template JSON file by running following commands +```json +sudo nano jitp_template.json +``` +3. Paste the following serialized JSON and save the file using control + X and type Y + +`Replace , and with your AWS Region, Account_ID and Role ARN for JITPRole respectively` +```json +{ + "templateBody":"{ \"Parameters\" : { \"AWS::IoT::Certificate::CommonName\" : { \"Type\" : \"String\" },\"AWS::IoT::Certificate::Country\" : { \"Type\" : \"String\" }, \"AWS::IoT::Certificate::Id\" : { \"Type\" : \"String\" }}, \"Resources\" : { \"thing\" : { \"Type\" : \"AWS::IoT::Thing\", \"Properties\" : { \"ThingName\" : {\"Ref\" : \"AWS::IoT::Certificate::CommonName\"}, \"AttributePayload\" : { \"version\" : \"v1\", \"country\" : {\"Ref\" : \"AWS::IoT::Certificate::Country\"}} } }, \"certificate\" : { \"Type\" : \"AWS::IoT::Certificate\", \"Properties\" : { \"CertificateId\": {\"Ref\" : \"AWS::IoT::Certificate::Id\"}, \"Status\" : \"ACTIVE\" } }, \"policy\" : {\"Type\" : \"AWS::IoT::Policy\", \"Properties\" : { \"PolicyDocument\" : \"{ \\\"Version\\\": \\\"2012-10-17\\\", \\\"Statement\\\": [ { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Connect\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:::client\\\/*\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Publish\\\", \\\"iot:Receive\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:::topic\\\/*\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Subscribe\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:::topicfilter\\\/*\\\" ] } ] }\" } } } }", + "roleArn":"" +} +``` +4. Run the following register-ca-certificate command to register the device root CA as a CA certificate in AWS IoT Core. Make sure to set correct region at --region flag +```json +aws iot register-ca-certificate --ca-certificate file://deviceRootCA.pem --verification-cert file://verificationCert.crt --set-as-active --allow-auto-registration --registration-config file://jitp_template.json --region ap-south-1 +``` + +--- +#### Perform JITP +--- +Now we have to create a Device certificate using self-signed root CA and on-board the device using JITP. If you have configured ACM or other PKI use your respective PKI to create a CSR and sign device certificate and skip to step 6. + +1. Download the RootCA1 and save it with the file name AmazonRootCA1.pem. The RootCA1 is used for server-side authentication of publish requests to AWS IoT Core. Also create a device private key. + +```json +wget https://www.amazontrust.com/repository/AmazonRootCA1.pem +openssl genrsa -out deviceCert.key 2048 +``` + +3. Run the following OpenSSL command to create a device CSR. The example JITP template requires the ThingName value to equal the certificate’s CommonName value. The template also requires the CountryName value to equal the Country value in the CA certificate. Refer to the following example: +![ThingName](thingName.png) + +```json +openssl req -new -key deviceCert.key -out deviceCert.csr +``` + +4. Run the following OpenSSL command to create a device certificate: +```json +openssl x509 -req -in deviceCert.csr -CA deviceRootCA.pem -CAkey deviceRootCA.key -CAcreateserial -out deviceCert.crt -days 365 -sha256 +``` +5. Run the following command to combine the root CA certificate and device certificate +```json +cat deviceCert.crt deviceRootCA.pem > deviceCertAndCACert.crt +``` +6. Now connect to your AWS IoT Core endpoint using deviceCertAndCACert.crt (Device certificate), deviceCert.key (Device key) and AmazonRootCA1.pem (root CA). +Download and setup the python sdk +```json +git clone https://github.com/aws/aws-iot-device-sdk-python-v2 +cd aws-iot-device-sdk-python-v2 +sudo python3 setup.py install +``` + +7. Navigate to Samples folder and execute the basicPubSub. +Change the endpoint as per your AWS IoT Core endpoint, also ensure that you state correct paths and names for certificate files. +```json +cd ~/aws-iot-device-sdk-python-v2/samples/ +python3 pubsub.py --endpoint a2weqbsmnrxkaf-ats.iot.ap-south-1.amazonaws.com --root-ca ~/AmazonRootCA1.pem --cert ~/deviceCertAndCACert.crt --key ~/deviceCert.key --client-id "JITP_Demo_Device" --topic "test" --message "Hello World" +``` + +8. The First connection will timeout and fail. But JITP will now create a new Thing named JITP_Demo_Device (as per the Common Name set in CSR), register the device certificate and attach the security policy as per the JITP_template. +The subsequent connections should be successful. + +## Considerations +This implementation covers the basics of device onboarding using Just-in-time Provisioning. It does not cover certain aspects that may arise in production use. + diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/architecture.svg b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/architecture.svg new file mode 100644 index 0000000..8a18854 --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/architecture.svg @@ -0,0 +1,3 @@ + + +
AWS Cloud
AWS Cloud
 SUB: cmd/device1/resp 
 SUB: cmd/device1/resp 
 SUB: cmd/device1/req 
 SUB: cmd/device1/req 
 PUB: cmd/device1/resp 
 PUB: cmd/device1/resp 
Device
Device
AWS IoT Core
MQTT Broker
AWS IoT Core...
 PUB: cmd/device1/req 
 PUB: cmd/device1/req 
Application
1
1
2
2
3
3
4
4
MQTT Connection
(for all publish and subscribe operations)
MQTT Connection...
3
3
4
4
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/checkpoint.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/checkpoint.png new file mode 100644 index 0000000..0814a00 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/checkpoint.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/checkpointARN.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/checkpointARN.png new file mode 100644 index 0000000..4eb26c9 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/checkpointARN.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/iam.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/iam.png new file mode 100644 index 0000000..aaa97f9 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/iam.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/register.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/register.png new file mode 100644 index 0000000..829404d Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/register.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/thingName.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/thingName.png new file mode 100644 index 0000000..219af42 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/thingName.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/things.png b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/things.png new file mode 100644 index 0000000..3e373c2 Binary files /dev/null and b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/Provisioning-JITP/things.png differ diff --git a/src/hugo/content/en-us/implementations/aws/deviceOnboarding/_index.md b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/_index.md new file mode 100644 index 0000000..65f9b0a --- /dev/null +++ b/src/hugo/content/en-us/implementations/aws/deviceOnboarding/_index.md @@ -0,0 +1,8 @@ +--- +hidden: false +# Placeholder to organize patterns - will be hidden when menu navigation becomes untenable +title: "Device Bootstrap" +--- +This is list of AWS implementations for the _Command_ pattern. + +{{% children style="h4" depth="2" description="true" showhidden="true" %}} \ No newline at end of file