はじめに
この記事はほとんど人間が書いてますが、一部Codexに手伝ってもらっています
AWS IoT Coreと聞くと、温湿度センサーやスマート家電、工場の機器などを思い浮かべます。
でも今回使ってみて面白かったのは、
ユーザーのMacを1台のIoTデバイスとして登録し、AWSからMacへ作業指示を届ける
という使い方です。
例えば、AWS側のAIエージェントが
「このMacに開発ツールを入れてほしい」
「モデルをダウンロードしてほしい」
と判断したとします。
ただし、インストールを実行する場所はAWS上のサーバーではなく、ユーザーの手元にあるMacです。
AWSのAIエージェントが、他人のMacの中身を直接操作できるわけではありません。
そこで必要になるのが、
AWS側からMacへ指示を送り、Mac側がそれを受け取って実行する
という橋渡しです。
この橋渡しに何か使えるものはないかと探してしたところ、AWS IoT Coreを探し出したというわけです。
これは前回アプリを実装していて驚いたことで、今回は敢えてこの事実を抜粋して改めてお話しします。
AWS IoT Coreとは
AWS IoT Coreは、インターネットにつながるデバイスとAWSサービスを安全に接続するためのマネージドサービスです。
MQTTという軽量なメッセージングプロトコルを使い、デバイスはメッセージの送受信を行えます。また、AWS IoT CoreではX.509証明書とIoTポリシーを使って、デバイスごとに接続・公開・購読の権限を細かく制御できます。
本来の用途では、例えばこんな「モノ」をつなげます。
- 温度・湿度センサー
- スマートロック
- 工場設備
- 車載デバイス
- 家庭内の家電
今回はセンサーの代わりに、ユーザーのMacを1台の「モノ」として登録します。
AWS上のアプリケーション / AIエージェント
↓
AWS IoT Core
↓
ユーザーのMac上の常駐プログラム
↓
brew install / ファイル取得 / ローカル処理
Macから見ると、AWS IoT Coreは「クラウド側から届く作業指示の窓口」なのです。
なぜ普通のAPIではなくAWS IoT Coreなのか
最初は「APIを作ってMacから定期的に問い合わせればよいのでは」と考えました。
もちろん、それでも実現できます。
ただ、この用途ではIoT Coreの性質がかなり都合よく働きます。
- Macごとに証明書を発行できる
- MQTTで双方向通信できる
- Topic単位で権限を絞れる
- Macが一時的にオフラインでも、クラウド側に「やってほしいこと」を持たせられる
- IoT Ruleを使えば、受け取ったイベントをLambdaやS3など別サービスへ連携できる
特に重要なのが、単なるメッセージ配送ではなく、「望ましい状態」と「現在の状態」を分けて持てるところです。
ここで使うのがDevice Shadowです。
Device Shadowは「デバイス用の共有メモ」
AWS IoT Coreには、Device Shadowという機能があります。
これは、デバイスの状態をクラウド側にJSONドキュメントとして保持する仕組みです。状態は主に次の2つに分かれます。
-
desired: デバイスにしてほしい状態 -
reported: デバイスが実際に報告した状態
例えば、MacにNode.jsをインストールしてほしい場合、AWS側はこのように書き込みます。
{
"state": {
"desired": {
"command": {
"id": "install-node-001",
"type": "brew_install",
"package": "node"
}
}
}
}
この内容は、「MacにNode.jsを入れてほしい」という依頼です。
Mac側の常駐プログラムはShadowを確認し、処理を実行します。
brew install node
処理が終わったら、Macは実行結果をreportedに書き戻します。
{
"state": {
"reported": {
"command": {
"id": "install-node-001",
"status": "succeeded",
"completedAt": "2026-09-16T08:46:05Z"
}
}
}
}
するとAWS IoT Coreは、desiredとreportedの差分が解消された状態として管理できます。
AWS側: 「Node.jsを入れてほしい」
Mac側: 「入れ終わりました」
この「指示」と「実行結果」を同じ状態ドキュメントで追える感覚が、黒板や共有メモに近いです。
Macがスリープ中でも指示を失いにくい
この構成で一番面白いと感じたのはここです。
Macは常にオンラインとは限りません。
- ノートPCを閉じている
- Wi-Fiが切れている
- 常駐プログラムを再起動している
- ユーザーが外出している
通常のリアルタイムメッセージングだけに頼ると、オフライン中の指示を取りこぼす設計になりがちです。
Device Shadowを使うと、AWS側はMacがオンラインかどうかを過度に意識せず、先にdesiredへ「してほしいこと」を書き込めます。
Mac側は起動時・再接続時にShadowの現在値を取得し、未完了の指示を確認して処理します。
ここは少し大事なポイントです。
/update/deltaトピックを購読すると、desiredとreportedの差分が発生したときに通知を受け取れます。一方で、Macがオフライン中に書き込まれた指示を確実に拾うには、再接続時にShadowを取得する処理も入れるのが安全です。
1. Macがオンライン中
→ /update/delta を受信して即時実行
2. Macがオフライン中
→ AWSは desired に指示を保存
→ Macが次回起動・再接続
→ Shadowを取得して未処理の指示を実行
「今つながっている相手にメッセージを送る」というより、クラウドにある望ましい状態へMacを追従させると考えると分かりやすいです。
実装イメージ
Mac側では、次のような責務を持つ小さなエージェントを動かします。
- X.509証明書でAWS IoT Coreへ接続する
- Shadowの
/update/deltaを購読する - 起動時にShadowを取得する
- 指示の形式・許可内容を検証する
- ローカル処理を実行する
- 成功・失敗を
reportedへ返す
MQTTのShadow関連トピックは次のような形です。
$aws/things/{thingName}/shadow/get
$aws/things/{thingName}/shadow/get/accepted
$aws/things/{thingName}/shadow/update
$aws/things/{thingName}/shadow/update/delta
例えばmy-macbookというThingなら、差分通知はこのトピックです。
$aws/things/my-macbook/shadow/update/delta
desiredとreportedに差がある場合、/update/deltaには差分だけが届きます。
{
"version": 42,
"timestamp": 1789548365,
"state": {
"command": {
"id": "install-node-001",
"type": "brew_install",
"package": "node"
}
}
}
「任意のシェルコマンドを実行」は危険
この仕組みは便利ですが、設計を雑にするとかなり危険です。
例えば、AWSから受け取った文字列をそのまま実行する実装は避けるべきです。
# やってはいけない例
subprocess.run(command_from_cloud, shell=True)
これではAWS側の設定ミス、認可ミス、認証情報の漏えいが、そのままMac上の任意コマンド実行につながります。
実際には、クラウドから渡す値を「許可済みの操作」に限定するのがよいです。
{
"type": "brew_install",
"package": "node"
}
Mac側では、許可リストにある処理だけを実行します。
ALLOWED_PACKAGES = {"node", "python", "gh"}
if command["type"] == "brew_install":
package = command["package"]
if package not in ALLOWED_PACKAGES:
raise ValueError("許可されていないパッケージです")
subprocess.run(
["brew", "install", package],
check=True
)
ポイントは、AWS IoT Coreを「Macへのリモートシェル」として扱わないことです。
用途を限定した命令プロトコルとして扱うと、安全性と運用性が上がります。
IoTポリシーもMacごとに絞る
AWS IoT Coreでは、各Macに固有の証明書を割り当て、アクセスできるTopicを限定できます。
例えば、my-macbookは自分自身のShadowだけを操作できるようにします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "arn:aws:iot:ap-northeast-1:123456789012:client/my-macbook"
},
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": "arn:aws:iot:ap-northeast-1:123456789012:topic/$aws/things/my-macbook/shadow/*"
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": "arn:aws:iot:ap-northeast-1:123456789012:topicfilter/$aws/things/my-macbook/shadow/*"
}
]
}
実運用では、証明書をMacごとに分ける、証明書の失効・ローテーションを考える、不要になったThingや証明書を無効化する、といった運用も必要です。
他にも応用できそうな使い方
MacをIoTデバイスとして扱う発想は、インストール処理以外にも使えそうです。
- AIエージェントが作成したファイルをMacで受け取り、指定フォルダへ保存する
- Mac上でローカルLLMやCLIを実行し、結果だけをAWSへ返す
- 社内端末に対して設定変更やキャッシュ削除などの承認済みタスクを配布する
- 展示会用PCやサイネージ端末の状態を監視・制御する
- 個人の開発環境で、クラウドのジョブ完了をトリガーに後処理を動かす
もちろん、端末管理が主目的ならAWS Systems Managerなどの方が適した場面もあります。
一方で、クラウド上のイベントやAIの判断をきっかけに、用途を絞ったローカル処理を非同期に動かしたい場合は、AWS IoT Core + Device Shadowはかなり相性がよいと感じました。
まとめ
AWS IoT Coreは「センサーや家電をつなぐサービス」というイメージが強いですが、実際にはもっと広く使えます。
今回のポイントは、Macを1台のIoTデバイスとして扱い、
AWSが desired に指示を書く
↓
Macが指示を取得して実行する
↓
Macが reported に結果を書く
という状態同期のモデルにしたことです。
リアルタイムで命令を飛ばすだけではなく、Macがオフラインでも「次に起きたときにやってほしいこと」を残せるのがDevice Shadowの面白さでした。
IoT Coreはモノをつなぐサービスですが、見方を変えると、クラウドと手元のコンピュータを安全にゆるくつなぐための仕組みとしても使えます。
最後まで読んでいただきありがとうございました。
