【AWS IoT Core】 JITP(Just-In-Time Provisioning)機能をJITRと比較しながら実装してみた

【AWS IoT Core】 JITP(Just-In-Time Provisioning)機能をJITRと比較しながら実装してみた

Clock Icon2025.05.20

はじめに

こんにちは、コンサルティング部の神野です。

前回は AWS IoT Core の「JITR(Just-In-Time Registration)」機能を活用してデバイス証明書のホワイトリスティングの実装方法について解説しました。今回は似た機能にあたる「JITP(Just-In-Time Provisioning)」を使った、より簡潔な自動登録の実装方法をご紹介します!

https://dev.classmethod.jp/articles/aws-iot-core-cdk-jitr-just-in-time-registration-whitelisting/

JITPを使うと、デバイスが初めて接続した時に自動的に証明書の登録処理を行うだけでなく、プロビジョニングテンプレートに従って「モノ」の作成やポリシーの適用まで一連の流れを自動化できます。JITRと比較して、Lambdaを必須としないシンプルな構成で実装できるのが大きな特徴です。

弊社の記事でもJITPをやってみた記事がありますが、改めてJITRと比較しながらやってみた記事を書いてみました。

https://dev.classmethod.jp/articles/aws-iot-just-in-time-provisioning/

JITRとJITPの比較 - どちらを使うべきか?

まずはJITRとJITPの違いを整理し、どのような場合にどちらを選択すべきかを考えてみましょう。

JITR(Just-In-Time Registration)の特徴

Lambda関数を実行して証明書の有効化やモノの登録ができることがポイントかと思います。特にホワイトリスティングなど独自のチェックなどを入れたい場合は有用な可能性が高いです。

特徴一覧

  • 証明書登録時にLambda関数を起動して複雑なカスタムロジックを実行可能
  • DynamoDBなど他のサービスと連携したホワイトリスト検証が実装できる
  • 証明書情報に基づいた詳細な検証と制御が可能
  • 柔軟なカスタマイズが可能(例:ビジネスロジックに基づく複雑な判断)

JITP(Just-In-Time Provisioning)の特徴

シンプルに独自CAを使って、自動でプロビジョニングする際に有効かと思います。テンプレートの用意さえできれば、JITRのようにLambda関数は不要です。
プロビジョニングがテンプレートの範囲内、また独自のチェックロジックなど不要ならこちらで十分な気もしますね。

特徴一覧

  • プロビジョニングテンプレートを使って証明書登録から「モノ」の作成まで自動化
  • Lambda関数の実装が不要
  • テンプレートベースで簡潔に実装可能

選択の指針

  • JITPの場合

    • シンプルな自動登録が必要な場合
    • デバイス接続時の処理を定型化できる場合
    • 管理のオーバーヘッドを最小限にしたい場合
  • JITRの場合

    • 複雑なカスタム検証ロジックが必要な場合
    • 外部データベースとの連携が必要な場合
    • より詳細な制御とカスタマイズが必要な場合

つまり、「シンプルさと管理のしやすさを求めるならJITP」、「カスタム検証が必要な複雑なケースではJITR」という選択肢になってくるかと思います。

実現したいこと

今回実装するシステムでは、以下のことを実現する想定です。

JITP構成図

絵としてはすごくシンプルです。

  1. 独自のCA証明書を元に、デバイス証明書を作成し、証明書にはデバイスIDを書き込む
  2. デバイスが初めてIoT Coreに接続すると、CA証明書に関連付けられたプロビジョニングテンプレートに基づいて自動的に処理を実行
  3. テンプレートに基づき、証明書の登録、「モノ」の作成、ポリシーの適用を自動的に実行
  4. デバイスは追加のセットアップなしに、AWS IoT Coreとの通信を開始できる

JITRのアーキテクチャも載せますが、比較するとよりシンプルに見えます。
ただ要件によって、ホワイトリスティングなどカスタムの検証が必要な場合は下記方法も考慮する必要があるかと思います。

JITR構成図

前提

本記事では環境構築はCDKを作成して、検証を行います。CDKのインストールおよび基本的なセットアップやプロジェクト作成は完了しているものとします。

構築手順

それでは、実際にシステムを構築していきましょう。手順は大きく分けて以下のステップに分けて進めていきます。

  1. CDKによるAWSリソースの定義 (必要最小限のリソース)
  2. プロビジョニングテンプレートの作成
  3. CA証明書作成とAWS IoT Core JITPのための設定
  4. デバイス証明書の作成
  5. デバイスのシミュレーションと動作確認

CDKによるAWSリソースの定義

JITPの場合、JITRと比較してCDKで作成するリソースはかなり少なくなります。今回は、デバイスが使用するIoTポリシーのみをCDKで定義しておきます。
リソース量がかなり少ないのでCDKを特段使う必要もないのですが、前回との対比も兼ねてCDKで実装してみました。

lib/aws-iot-jitp-cdk-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as iot from 'aws-cdk-lib/aws-iot';
import * as iam from 'aws-cdk-lib/aws-iam'; // iamモジュールをインポート

export class JitpSampleCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // IoTポリシーを定義(デバイス接続用の基本ポリシー)
    new iot.CfnPolicy(this, 'DeviceBasePolicy', {
      policyName: 'DeviceBasePolicy',
      policyDocument: {
        Version: '2012-10-17',
        Statement: [
          {
            Effect: 'Allow',
            Action: ['iot:Connect'],
            Resource: [`arn:aws:iot:${this.region}:${this.account}:client/\${iot:ClientId}`]
          },
          {
            Effect: 'Allow',
            Action: ['iot:Publish', 'iot:Receive'],
            Resource: [`arn:aws:iot:${this.region}:${this.account}:topic/devices/*`]
          },
          {
            Effect: 'Allow',
            Action: ['iot:Subscribe'],
            Resource: [`arn:aws:iot:${this.region}:${this.account}:topicfilter/devices/*`]
          }
        ]
      }
    });

    // JITPプロビジョニング用IAMロールを作成
    const jitpProvisioningRole = new iam.Role(this, 'JitpProvisioningRole', {
      roleName: 'JitpProvisioningRole', // 必要に応じてロール名を変更してください
      assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'),
      inlinePolicies: {
        JitpProvisioningPermissions: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              actions: [
                "iot:CreateThing",
                "iot:AttachPolicy", // テンプレートで既存ポリシーをアタッチするため
                "iot:AttachThingPrincipal",
                "iot:UpdateCertificate",
                "iot:AddThingToThingGroup",
                "iot:RegisterThing",
                "iot:DescribeThing",
                "iot:ListThingGroupsForThing",
                "iot:DescribeCertificate",
                "iot:DescribeThingType",
                "iot:DescribeThingGroup",
                "iot:GetPolicy",
                "iot:AttachPrincipalPolicy"
                // "iot:CreatePolicy" // テンプレートが新しいポリシーを作成する場合に必要
              ],
              resources: ["*"], // 本番環境ではリソースをより具体的に指定することを推奨します
            }),
          ],
        }),
      },
    });

    // JITPプロビジョニング用IAMロールのARNを出力
    new cdk.CfnOutput(this, 'JitpProvisioningRoleArn', {
      value: jitpProvisioningRole.roleArn,
      description: 'ARN of the IAM role for JITP provisioning',
      exportName: 'JitpProvisioningRoleArnOutput',
    });
  }
}

JITPではシンプルにプロビジョニングするため、JITRで必要だったDynamoDBテーブルやLambda関数、IoTルールが不要となります。
またJITPプロビジョニング用IAMロールは後続で使用するため、ARNを出力しています。

CDKをデプロイします。

実行コマンド
cdk deploy

プロビジョニングテンプレートの作成

続いて、JITPの中心となるプロビジョニングテンプレートを作成します。このテンプレートは、デバイスが初めて接続した際に実行される処理を定義します。

プロビジョニングテンプレートは、JSON形式で作成します。ローカルにファイルとして保存しておきましょう。

provisioning-template.json
{
    "Parameters": {
      "AWS::IoT::Certificate::CommonName": {
        "Type": "String"
      },
      "AWS::IoT::Certificate::SerialNumber": {
        "Type": "String"
      },
      "AWS::IoT::Certificate::Country": {
        "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": "DeviceBasePolicy"
        },
        "Type": "AWS::IoT::Policy"
      },
      "thing": {
        "Properties": {
          "ThingName": {
            "Ref": "AWS::IoT::Certificate::CommonName"
          },
          "AttributePayload": {
            "version": "v1",
            "serialNumber": {
              "Ref": "AWS::IoT::Certificate::SerialNumber"
            }
          },
          "ThingTypeName": "lightBulb-versionA",
          "ThingGroups": [
            "v1-lightbulbs",
            {
              "Ref": "AWS::IoT::Certificate::Country"
            }
          ]
        },
        "Type": "AWS::IoT::Thing",
        "OverrideSettings": {
          "AttributePayload": "MERGE",
          "ThingTypeName": "REPLACE",
          "ThingGroups": "DO_NOTHING"
        }
      }
    }
  }

まず Parameters セクションでは、テンプレート内で利用する変数を定義します。これらはデバイス証明書のサブジェクトDN(Distinguished Name)や、AWS IoT Coreが提供する情報から自動的に取得されます。

  • AWS::IoT::Certificate::CommonName: デバイス証明書のサブジェクトDNから取得されるコモンネームです。今回のテンプレートでは、これを「モノ」の名前に使用します。
  • AWS::IoT::Certificate::SerialNumber: デバイス証明書のサブジェクトDNから取得されるシリアル番号です。今回のテンプレートでは、これを「モノ」の属性として設定します。
  • AWS::IoT::Certificate::Country: デバイス証明書のサブジェクトDNから取得される国コードです。今回のテンプレートでは、これを「モノ」が所属するグループ名の一部として使用します。
  • AWS::IoT::Certificate::Id: 接続に使われたデバイス証明書のIDです。AWS IoT Coreが自動でこのパラメータに値を渡してくれます。

次に Resources セクションで、実際にプロビジョニングされるAWS IoTリソースを定義していきます。

  1. "certificate" リソース
    • Parameters で受け取った AWS::IoT::Certificate::Id を持つ証明書の Status を ACTIVE に更新します。これにより、この証明書がAWS IoT Coreで有効なものとして扱われるようになります。
  2. "policy" リソース
    • CDKで事前に作成した DeviceBasePolicy という名前のIoTポリシーを、この証明書にアタッチします。これにより、デバイスは定義された権限でAWS IoT Coreと通信できるようになります。
  3. "thing" リソース
    • ThingName を、Parameters で受け取った AWS::IoT::Certificate::CommonName の値(例: デバイス証明書作成時にCNとして JitpDevice を指定した場合、モノの名前は JitpDevice となります)で作成します。
    • AttributePayload で、モノに属性情報を設定します。具体的には、version という属性に静的な値 "v1" を、serialNumber という属性に Parameters で受け取った AWS::IoT::Certificate::SerialNumber の値を設定します。
    • ThingTypeName で lightBulb-versionA を指定しています。これは事前にAWS IoT Coreに登録されているモノのタイプ名を指します。もし lightBulb-versionA というモノのタイプが存在しなければ、プロビジョニングは失敗するので注意しましょう。
    • ThingGroups で、このモノが所属するグループを指定します。具体的には、静的な名前のグループ "v1-lightbulbs" と、Parameters で受け取った AWS::IoT::Certificate::Country の値(例: デバイス証明書作成時にCとして JP を指定した場合、グループ名は JP となります)を名前とするグループに追加します。こちらも、対応するグループが存在しなければプロビジョニングは失敗します。

このテンプレートによって、デバイスの初回接続時に以下の処理が自動で実行されます。

  1. デバイス証明書の情報から AWS::IoT::Certificate::CommonNameAWS::IoT::Certificate::SerialNumberAWS::IoT::Certificate::Country、そしてAWS IoT Coreから提供される AWS::IoT::Certificate::Id パラメータを取得
  2. 取得した AWS::IoT::Certificate::Id に基づいて、デバイス証明書をアクティブ状態に設定
  3. あらかじめCDKで作成した DeviceBasePolicy をデバイス証明書に適用
  4. 証明書の CommonName を名前として「モノ」を作成し、version や serialNumber などの属性を設定。さらに指定されたグループに「モノ」を追加

JITRのようにLambdaを作成する必要がなく、このテンプレートでデバイス登録からモノの作成、ポリシー適用まで完結するのがJITPの魅力ですね!

JITPのテンプレートについては補足セクションにも書いてあるので必要に応じてご参照ください。

AWS IoT CA証明書の設定とJITPの有効化

JITPのためのCA証明書の作成と登録は、JITRとほぼ同じ手順で行います。追加でプロビジョニングテンプレートを関連付ける必要があります。

CA証明書の作成 (手動)

まずはCA証明書を作成します。

CA証明書の作成コマンド
# CA証明書用のディレクトリを作成
mkdir -p ~/aws-iot-jitp/ca
cd ~/aws-iot-jitp/ca

# CAプライベートキーの生成
openssl genrsa -out rootCA.key 2048

# CA証明書の作成 (例: Common Name を MyDeviceRootCA とする場合)
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem -subj "/CN=MyDeviceRootCA"

CA証明書の登録とJITP有効化 (手動)

作成したCA証明書 (rootCA.pem) をAWS IoT Coreに登録し、プロビジョニングテンプレートを関連付けます。

  1. get-registration-codeコマンドを実行して、登録コードを取得します。
登録コード取得
aws iot get-registration-code
{
    "registrationCode": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
  1. 検証証明書のキーペアを作成します。
検証証明書のキーペア作成
openssl genrsa -out verification_cert.key 2048
  1. 検証証明書のCSRを作成します。その際に、Common Nameは先ほどの登録コードを指定します。
検証証明書のCSR作成
openssl req -new \
-key verification_cert.key \
-out verification_cert.csr
入力例
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []: <1.で取得した登録コード>
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
  1. 検証証明書を作成します。
検証証明書作成
openssl x509 -req \
-in verification_cert.csr \
-CA rootCA.pem \
-CAkey rootCA.key \
-CAcreateserial \
-out verification_cert.pem \
-days 500 -sha256

実行結果の例

Signature ok
subject=C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = <1.で取得した登録コード>
Getting CA Private Key
  1. プロビジョニングテンプレートをJSONファイルからロードし、CA証明書登録時にテンプレートを指定します。roleArnはCDKで出力されたARNを指定します。
    また、templateBodyのJSONは先ほど作成したprovisioning-template.jsonをインラインで記載するようにしています。
CA証明書を登録(JITPテンプレート付き)
aws iot register-ca-certificate  \
--ca-certificate file://rootCA.pem \
--verification-cert file://verification_cert.pem \
--registration-config '{
    "templateBody": "{\"Parameters\":{\"AWS::IoT::Certificate::CommonName\":{\"Type\":\"String\"},\"AWS::IoT::Certificate::SerialNumber\":{\"Type\":\"String\"},\"AWS::IoT::Certificate::Country\":{\"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\":\"DeviceBasePolicy\"},\"Type\":\"AWS::IoT::Policy\"},\"thing\":{\"Properties\":{\"ThingName\":{\"Ref\":\"AWS::IoT::Certificate::CommonName\"},\"AttributePayload\":{\"version\":\"v1\",\"serialNumber\":{\"Ref\":\"AWS::IoT::Certificate::SerialNumber\"}},\"ThingTypeName\":\"lightBulb-versionA\",\"ThingGroups\":[\"v1-lightbulbs\",{\"Ref\":\"AWS::IoT::Certificate::Country\"}]},\"Type\":\"AWS::IoT::Thing\",\"OverrideSettings\":{\"AttributePayload\":\"MERGE\",\"ThingTypeName\":\"REPLACE\",\"ThingGroups\":\"DO_NOTHING\"}}}}",
    "roleArn": "<CDKで出力されたARN>"
  }'

実行結果
certificatedIdは6.で有効化する時に必要なので、メモしておきます。

実行結果
{
    "certificateArn": "arn:aws:iot:ap-northeast-1:xxx:cacert/yyy",
    "certificateId": "zzz"
}
  1. 登録したCA証明書をActiveにし、JITP機能を有効化します。
CA証明書を有効化しJITPを有効にする
aws iot update-ca-certificate --profile private \
  --certificate-id <取得したcertificateId> \
  --new-status ACTIVE \
  --new-auto-registration-status ENABLE \
  --registration-config '{
    "templateBody": "{\"Parameters\":{\"AWS::IoT::Certificate::CommonName\":{\"Type\":\"String\"},\"AWS::IoT::Certificate::SerialNumber\":{\"Type\":\"String\"},\"AWS::IoT::Certificate::Country\":{\"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\":\"DeviceBasePolicy\"},\"Type\":\"AWS::IoT::Policy\"},\"thing\":{\"Properties\":{\"ThingName\":{\"Ref\":\"AWS::IoT::Certificate::CommonName\"},\"AttributePayload\":{\"version\":\"v1\",\"serialNumber\":{\"Ref\":\"AWS::IoT::Certificate::SerialNumber\"}},\"ThingTypeName\":\"lightBulb-versionA\",\"ThingGroups\":[\"v1-lightbulbs\",{\"Ref\":\"AWS::IoT::Certificate::Country\"}]},\"Type\":\"AWS::IoT::Thing\",\"OverrideSettings\":{\"AttributePayload\":\"MERGE\",\"ThingTypeName\":\"REPLACE\",\"ThingGroups\":\"DO_NOTHING\"}}}}",
    "roleArn": "<CDKで出力されたARN>"
  }'

コンソール上でも登録されているか確認してみましょう。証明書管理画面でCA証明書が「アクティブ」状態になっていることを確認します。

CleanShot 2025-05-20 at 17.30.56@2x

デバイス証明書の作成

次に、デバイス証明書を作成します。デバイス証明書には、テンプレートで使用するデバイスIDをシリアル番号として埋め込みます。

デバイス証明書作成
# デバイス証明書用のディレクトリを作成
mkdir -p ~/aws-iot-jitp/device
cd ~/aws-iot-jitp/device

# デバイスプライベートキーの生成
openssl genrsa -out device.key 2048

# 証明書署名要求(CSR)の作成(デバイスIDを埋め込む)
openssl req -new -key device.key -out device.csr -subj "/C=JP/CN=JitpDevice/serialNumber=DEVICE123456"

# CSRをCA証明書で署名
openssl x509 -req -in device.csr -CA ~/aws-iot-jitp/ca/rootCA.pem -CAkey ~/aws-iot-jitp/ca/rootCA.key -CAcreateserial -out device.crt -days 365 -sha256

# デバイスの証明書とCA証明書を結合
cat device.crt ~/aws-iot-jitp/ca/rootCA.pem > deviceAndCA.crt

証明書のserialNumberフィールドに、プロビジョニングテンプレートで参照するSerialNumberパラメータの値を設定してしています。またC=JPもテンプレートで参照するため指定します。

動作確認

最後に、作成したシステムが正しく動作するか確認してみましょう。

AWS IoT Core接続情報の取得

まず、AWS IoT Coreのエンドポイント情報を取得します。

エンドポイント取得
aws iot describe-endpoint --endpoint-type iot:Data-ATS

実行結果の例

取得結果の例
{
    "endpointAddress": "xxxxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
}

出力された endpointAddress をメモしておきます。

AWS のルートCA証明書をダウンロード

AWSのルートCA証明書をダウンロードします。接続時にはこのルートCA証明書を使用します。

curl -o ~/aws-iot-jitp/AmazonRootCA1.pem https://www.amazontrust.com/repository/AmazonRootCA1.pem

MQTTクライアントを使った接続テスト

Python用のAWS IoT SDKを使って、作成したデバイス証明書で接続してみます。 以下のPythonスクリプト (device_test.py) を作成します。

device_test.py
import time
import json
import argparse
import AWSIoTPythonSDK.MQTTLib as AWSIoTPyMQTT

# 引数の解析
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", required=True, help="AWS IoT Core endpoint")
parser.add_argument("--cert", required=True, help="Path to certificate file")
parser.add_argument("--key", required=True, help="Path to private key file")
parser.add_argument("--ca", required=True, help="Path to CA certificate file (AmazonRootCA1.pem)")
parser.add_argument("--device-id", required=True, help="Device ID (used as client ID)")
args = parser.parse_args()

# MQTT クライアントの設定
client_id = args.device_id
mqtt_client = AWSIoTPyMQTT.AWSIoTMQTTClient(client_id)
mqtt_client.configureEndpoint(args.endpoint, 8883)
mqtt_client.configureCredentials(args.ca, args.key, args.cert)

# MQTT 接続パラメータの設定
mqtt_client.configureAutoReconnectBackoffTime(1, 32, 20)
mqtt_client.configureOfflinePublishQueueing(-1)
mqtt_client.configureDrainingFrequency(2)
mqtt_client.configureConnectDisconnectTimeout(10)
mqtt_client.configureMQTTOperationTimeout(5)

# 接続コールバック関数
def simple_on_online_callback():
    print("Client is online.")

# onOnlineイベントに関数を割り当て(接続成功時に呼び出される)
mqtt_client.onOnline = simple_on_online_callback

# 接続試行
print(f"Attempting to connect to AWS IoT Core as device {args.device_id}...")
try:
    if mqtt_client.connect(10): # 接続タイムアウト10秒
        print("Connection successful.")
        # メッセージ発行
        topic = f"devices/{args.device_id}/status"
        message = {
            "message": f"Hello from device {args.device_id}",
            "timestamp": time.time()
        }
        print(f"Publishing message to topic: {topic}")
        mqtt_client.publish(topic, json.dumps(message), 1) # QoS 1

        # メッセージが送信され、コールバックが処理されるのを少し待つ
        time.sleep(5)
        print("Disconnecting...")
        mqtt_client.disconnect()
        print("Disconnected.")
    else:
        print("Connect attempt failed or timed out.")
except Exception as e:
    print(f"An error occurred: {str(e)}")

AWS IoT Python SDK をインストールします。

pip install AWSIoTPythonSDK

スクリプトを実行して、デバイスで接続を試みます。

# JITP接続テスト
python device_test.py \
  --endpoint xxx-ats.iot.ap-northeast-1.amazonaws.com \
  --cert ~/aws-iot-jitp/device/deviceAndCA.crt \
  --key ~/aws-iot-jitp/device/device.key \
  --ca ~/aws-iot-jitp/AmazonRootCA1.pem \
  --device-id DEVICE123456

JITPの場合も、初回接続時はタイムアウトで返ってくる可能性が高いです。これは、JITPのプロビジョニングプロセスが実行されるためです。2回目以降はプロビジョニングが完了しているため、接続が成功するはずです。ここは実際の処理では検討が必要ですね。

1回目実行
Attempting to connect to AWS IoT Core as device DEVICE123456...
Connect timed out
An error occurred: 

2回目を実行してみます!

Attempting to connect to AWS IoT Core as device DEVICE123456...
Client is online.
Connection successful.
Publishing message to topic: devices/DEVICE123456/status
Disconnecting...
Disconnected.

おお、問題なく繋がりましたね!!

コンソール上で確認すると、自動的に「モノ」が作成され、証明書が有効化されていることが確認できます。また、テンプレートで指定した属性も設定されているはずです。

証明書

アクティブで無事登録されているかつ、モノとも紐付けされていますね。

CleanShot 2025-05-20 at 17.32.07@2x 2

モノ

モノも作成されているかつ、テンプレートでマッピング指定したタイプや属性、グループも問題なく反映されていますね!!

CleanShot 2025-05-20 at 17.32.39@2x

CleanShot 2025-05-20 at 17.33.55@2x

コンソール上でも問題なく証明書の登録および有効化、テンプレートに基づいてモノの作成も完了していることを確認できました!

最後に改めてJITRとJITPを比較してみます。

JITR と JITP の実装比較

実際に両方の方式を実装してみた経験から、それぞれのメリットとデメリットを比較してみます。

比較項目 JITR (Just-In-Time Registration) JITP (Just-In-Time Provisioning)
実装の複雑さ より複雑(Lambda、DynamoDB等の追加リソースが必要) シンプル(テンプレートのみで実装可能)
必要リソース Lambda関数、DynamoDB、IoTルールなど プロビジョニングテンプレートとIAMロールのみ
カスタマイズ性 高い(Lambda内でカスタムロジックを実装可能) 限定的(テンプレート内で定義された範囲内)
検証機能 外部DBと連携した柔軟なホワイトリスト検証可能 テンプレートベースの基本的な検証のみ
管理オーバーヘッド 中(複数リソースの管理が必要) 低い(テンプレート管理のみ)
スケーラビリティ 対応可能 対応可能
適したユースケース 複雑な検証ロジックが必要な場合
外部DBとの連携が必要な場合
柔軟な証明書管理が必要な場合
シンプルな自動登録が必要な場合
定型化された処理で十分な場合
管理の簡素化が求められる場合
運用コスト Lambda実行コストなど発生 テンプレート部分のみ

結論としては、「シンプルさと管理のしやすさを求めるならJITP」、「カスタム検証が必要な複雑なケースではJITR」という選択になるでしょう。要件に応じて適切な方式を選択することが重要です。

おわりに

今回は改めてJITPの実装をやってみました。JITRよりできることが少ない分、シンプルに実装できて良いですね。JITPで要件を満たせるなら採用してみたいなと思いました!

JITRとJITPの両方を理解しておくことで、要件に応じて適切なIoTデバイス登録・認証の仕組みを選択できるようになるかと思います!

この記事が、皆さんのIoTプロジェクトでの参考になれば幸いです! 最後までご覧いただきありがとうございましたー!!!

補足:AWS公式ドキュメントにおけるJITPの解説

本記事ではJITPの実装方法をご紹介しましたが、AWSの公式ドキュメントにもJITPに関する詳細な情報が記載されています。
JITRよりも柔軟性が低い分、公式ドキュメントを見ながら何ができるかを把握することは大事かと思い、補足セクションを記載しました。

https://docs.aws.amazon.com/iot/latest/developerguide/jit-provisioning.html

特に重要な箇所だけピックアップしております。

  • JITPの動作概要
    デバイスが初めてAWS IoT Coreへの接続を試みた際、そのデバイス証明書を発行したCA証明書に紐付けられたプロビジョニングテンプレートに基づき、自動的にモノの登録やポリシーの適用が実行される仕組みです
  • 証明書ステータスの遷移
    JITPのプロビジョニングプロセス中、対象となるデバイス証明書のステータスは一時的に PENDING_ACTIVATION となり、全ての処理が正常に完了すると ACTIVE へと更新されます。初回接続時に若干の遅延が生じるのは、このバックグラウンド処理によるものです。
  • テンプレートで使用可能なパラメータ
    プロビジョニングテンプレート内で参照できる定義済みのパラメータ群がリストアップされています。本記事では AWS::IoT::Certificate::Id およびデバイス証明書の serialNumber 属性を利用しましたが、その他にも CommonNameCountryOrganization といった証明書のSubjectフィールドに含まれる情報を活用できます。これにより、より詳細なデバイス情報に基づいたモノの属性設定や、モノのグループ化といった高度な制御が可能になります。
    • AWS::IoT::Certificate::Id は、AWS IoTが内部的に生成する証明書の一意な識別子であり、証明書自体に格納されている値ではない点が明記されています。
  • プロビジョニングテンプレートの例
    OverrideSettings ディレクティブで、これにより既存のモノと競合した場合の属性のマージ方法(MERGE)、置き換え(REPLACE)、あるいは何もしない(DO_NOTHING)といった挙動を細かく制御できる点が示されています。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.