第654回 SORACOM公式ブログ

ソラコム公式ブログ

スイッチユーザー機能のセルフサービス化する裏側 ― AWS Lambda Web Functions の利用例

  • この記事をはてなブックマークに追加
  • 本文印刷

 本記事はソラコムが提供する「SORACOM公式ブログ」に掲載された「スイッチユーザー機能のセルフサービス化する裏側 ― AWS Lambda Web Functions の利用例」を再編集したものです。

こんにちは、ソラコムのテックエバの松下(ニックネーム: Max)です。

10/31開催のコミュニティイベント「SORACOM UG Explorer 2026」では、SORACOM LTE-M Button for Enterprise(以下、IoT ボタン)を使うハンズオンを実施しました。IoT ボタンはLTE-M通信を行うSIMを内蔵したデバイスなんですが、アカウント運用上は SIM として扱います(ボタンの形状をした SIM というイメージ)。

ハンズオン用に、運営側で貸し出し用の IoT ボタンを40台用意しました。参加者には、ボタンを押すだけでなく、SORACOM のユーザーコンソールで設定や操作も行ってもらいます。そこで検討したのが、ボタンに内蔵された SIM を参加者自身のアカウントへ移す方法と、SIMが登録されている運営側のアカウントを参加者に使ってもらう方法の二つです。

SORACOM の SIM の譲渡機能があり、疑似的な貸し出し・返却ができそうですが、本機能は SIM や紐づくコスト管理の整頓が主目的であるため、例えば SIM のステータスが「使用中」の SIM は譲渡した月内に再譲渡できなかったりと、機能の趣旨にも運用にも合致しません。

SORACOM の「スイッチユーザー機能」

もう一つの方法である、IoT ボタンの登録先アカウントを使ってもらう方法について考えると、まず手軽に思いつくのが「ID とパスワードを参加者に共有する」というものです。非常に簡単ですが、配布方法や終了後のパスワード変更を忘れると、想定外の継続利用や設定変更につながるおそれがあります。参加者が多かったり、頻繁に行う場合は、対応漏れのリスクが残ります。

そこで使えるのが、スイッチユーザー機能です。
ユーザーコンソールからログアウトすることなく、他のユーザーとしてログインし直す機能です。これで、ハンズオン参加者は自分のアカウントでログインしておきつつ、運営側のアカウントに切り替えて IoT ボタンを操作でき、終わったら元のユーザー(自分のアカウント)に戻れば終了となります。費用もスイッチユーザー先にかかるので、受講者負担を減らせるのもメリットです。

スイッチユーザー機能の利用には、運営側=切り替え先アカウントに「切り替え元のアカウントを信頼する(信頼ポリシー)」の設定が必要です。ユーザーコンソールから設定できますが、今回はスイッチ先が40アカウントあり、事前準備を運営側が一件ずつ Web 画面上で設定するのが手間な上に、やはり即時性に欠ける(受講者に待ってもらう必要がある)という問題がありました。

この事前準備の負担を減らすため、利用者がスイッチ元から申請し、信頼ポリシーを SORACOM の API で自動的に設定する仕組みを、2026年10月に発表された AWS Lambda Web Functions で実現したというお話です。

自動化した範囲

自動化したのは、指定された在庫の割当と信頼ポリシー設定です。信頼ポリシーが未割当の IoT ボタンのアカウント(以下、在庫)を探し、そこに信頼ポリシーを設定した後、スイッチユーザーに必要な情報を表示します。

フォームでは、スイッチ元の Operator ID(以下、OPID)と、スイッチ先 OPID を入力します。スイッチ先 OPID はボタンの裏に管理用としてラベルを貼っていたので、そこの手間は無くて助かりました(先人の作業に感謝!)。

在庫管理には Amazon DynamoDB を使っています。詳細は後ほど解説しますが、割当済みの在庫がどれか?などは外部のDBが必要だったので利用しました。ゆるく管理できればいい程度だったのでトランザクション分離ほどの厳密性は不要で、条件付き更新があるDynamoDBは使いやすかったです。

AWS アーキテクチャーと AWS Lambda Web Functions

  • AWS Lambda Web Functions(以下、Web Functions)はフォームを表示し、申請リクエストを Amazon Simple Queue Service(Amazon SQS)へ送ります。この時点で UX としては受け付け完了となり、結果表示ページへリダイレクトしています。
  • SQS 着信をトリガーに AWS Lambda Event Functions(以下、Event Functions)が起動します。在庫確認や引き当て処理を行い、SORACOM API 経由で信頼ポリシーを設定します。
  • 結果表示ページは、フォームの Web Functions を流用していて、処理は DynamoDB を読み出すだけにし、割り当て処理中もしくは処理結果を表示します。

以上が全体の流れです。1つずつ解説していきます。

まず基本的な考え方として Web Functions から直接在庫処理や SORACOM API を呼び出さず、SQS を経由した非同期処理にしています。
狙いは、在庫の割当と SORACOM API の呼び出しを一件ずつ処理(直列化)するためです。直列化ノウハウは色々と出ているので設定だけ紹介すると、 Event Functions は BatchSize=1、Reserved Concurrency=1、SQS は Visibility Timeout=Event Functions の Timeout の 6 倍としています。

在庫情報は DynamoDB に保存しています。パーティションキーはスイッチ先 OPID です。Event Functions は申請内のスイッチ先 OPID の在庫を取得し、条件付き更新でスイッチ元 OPID(SAM ユーザーの場合はユーザー名も)を記録します。別の申請者がすでに割り当てられている在庫を指定した場合、更新は失敗します。在庫引き当てができたら、SORACOM API で信頼ポリシーを設定します。
ちなみに信頼ポリシーは常に上書きとして実装しています。在庫情報を DynamoDB に一本化しておくことで、トラブル時は DynamoDB のレコード調整だけでリトライできる運用としたためです。

フォーム送信後の結果ページでは DynamoDB の割り当て状態を確認します。同じ申請者(Operator は OPID、SAM ユーザーは OPID とユーザー名)から再度結果を開いた場合は、割り当て状態に応じて処理中、完了、失敗を表示します。結果ページを閉じても、同じ申請内容で開き直して状態を確認できます。これで主要な流れは終わりです。

ちなみに Event Functions から呼び出す SORACOM API の認証情報は AWS Systems Manager の Parameter Store に SecureString 型パラメータに JSON で保存してあります。Event Function が処理時に読み込みます。

Lambda Web Functions の利便性

AWS Lambda “Event” Functions 単体で Web アプリを公開する仕組み自体は、AWS Lambda Web Adapter(LWA)があります。対して Web Functions はコンテナを使わずにコードデプロイだけで Web アプリができるという手軽さです。今回のような突貫・限定型のインタラクティブ Web アプリとの相性が良さそうという背景です。実際、コンテナ化や Amazon Elastic Container Registry (Amazon ECR) を利用せずにできたことは、非常にお手軽でした。

フォームは Node.js 向け Web フレームワークの Hono で実装しています。ここのこだわりは(Rubyでないのなら)正直無く、後述する AI での開発に任せやすいものを使ってます。Ruby が使えるなら Sinatra を選びますけど (^^

Event Functions とのコスト比較は Developers.IO ― Web Functions の課金は従来の Lambda とどれだけ違うのか、同じ待ち時間の負荷をかけて請求データで確認してみました に詳しく分析されていました。今回のユースケース・ワークロードは十分 Web Functions 向きのようです。

ローカル開発の環境と AI コーディングエージェント

開発のお供にしたのは AI コーディングエージェントの OpenCode (v2) です。積極的に Shell を使ってくれること(ワンショットのコードを抑えられる)と、cioffiAI/rem-memory のような長期記憶を組み合わせられるのが私のスタイルに合っていたという背景です。今も基本的に OpenCode 使いです。

OpenCode に SORACOM knowledge MCP サーバーと、AWS MCP サーバーを登録、ローカルに SORACOM CLI と AWS CLI を用意して、プロジェクトの AGENTS.md にこれらを使わせるように指示しています。また開発時は、ローカル開発向け AWS サービスエミュレーターの Nimbus と(定番?の)DynamoDB Local をセットアップしています。この2つは Docker コンテナ化されてるので導入しやすく、特に Nimbus は SQS や Parameter Store もエミュレートできるため、テスト用分岐コードを極力減らせます。

テストに使える「AWS Emulator」

テスト時のリソースに、本文では Nimbus や DynamoDB Local をご紹介しました。11/11に AWS Emulator がリリースされており、これも利用できそうです(未検証ですが、情報まで)

以上の環境上で OpenCode(AI コーディングエージェント)に、開発の方向性や使える環境を伝えて実装をしていきます。例として、以下のような対話をしています(実際のプロンプト履歴を基に再現しています)。

ハンズオン用の在庫ボタンは40件あります。利用者がスイッチ元 OPID と、ボタンに記載されたスイッチ先 OPID を送信できるフォームを作ってください。申請受付と SORACOM API の処理は分離し、結果は後から確認できるようにしてください。

在庫割当では、同じ在庫が別の申請者に割り当てられないようにしてください。同じ申請者からの再送は同じ割当として扱い、指定された在庫が使えない場合に別の在庫へ勝手に置き換えないでください。

Nimbus と DynamoDB Local を使ってローカルで検証できる構成にしてください。Web Function から SQS への送信、Event Function の処理、結果画面をそれぞれテストし、実際の SORACOM API を呼ばずに確認できるようにしてください。

実際に、以下のように Hono のコードが生成され、Web Functions で動きました。(一部を出しています)

 export function createApp({ sqs, dynamodb }) {   const app = new Hono();    app.get('/favicon.ico', (context) => {     // omit   });    app.get('/style.css', async (context) => {     // omit   });    app.get('/form', (context) => context.html(html`     <!-- omit -->   `));    app.get('/form/using_rootuser', (context) => context.html(html`     <!-- omit -->   `));    app.get('/form/with_samuser', (context) => context.html(html`     <!-- omit -->   `));    app.post('/form', async (context) => {     const form = await context.req.formData();     const opid = String(form.get('opid') ?? '');     const stock_opid = String(form.get('stock_opid') ?? '');     const job = {       opid,       stock_opid,       ...(form.has('sam_username') ? { sam_username: String(form.get('sam_username') ?? '') } : {}),     };      // バリデーションは省略      try {       const { QueueUrl } = await sqs.client.send(         new GetQueueUrlCommand({ QueueName: sqs.queueName }),       );       if (!QueueUrl) throw new Error(`SQS queue not found: ${sqs.queueName}`);       await sqs.client.send(new SendMessageCommand({         QueueUrl,         MessageBody: JSON.stringify(job),       }));     } catch {       // omit: エラー画面     }      const resultUrl = `/result?${new URLSearchParams(job)}`;     return context.redirect(resultUrl, 303);   });    app.get('/result', async (context) => {     const opid = context.req.query('opid');     const stock_opid = context.req.query('stock_opid');     const sam_username = context.req.query('sam_username');      try {       const items = [];       let ExclusiveStartKey;       do {         const page = await dynamodb.client.send(new ScanCommand({           TableName: dynamodb.tableName,           ConsistentRead: true,           ExclusiveStartKey,         }));         items.push(...(page.Items ?? []));         ExclusiveStartKey = page.LastEvaluatedKey;       } while (ExclusiveStartKey);        const assigned = items.find((item) => item.STOCK_OPID === stock_opid);        // omit: 在庫対象外の判定        const matchesIdentity = assigned.ASSIGNED_OPID === opid         && (sam_username === undefined           ? assigned.ASSIGNED_SAM_USERNAME === undefined           : assigned.ASSIGNED_SAM_USERNAME === sam_username.trim());        // omit: 割当済み・処理失敗の判定        if (matchesIdentity && assigned.ASSIGNMENT_STATUS === 'COMPLETE') {         return context.html(html`           <!-- omit: 完了画面 -->         `);       }        const query = new URLSearchParams({         opid,         stock_opid,         ...(sam_username !== undefined ? { sam_username } : {}),       });       const refreshUrl = `/result?${query}`;        return context.html(html`         <!-- omit: 2秒ごとに更新する処理中画面 -->       `);     } catch {       // omit: 結果確認エラー画面     }   });    app.notFound(async (context) => context.text('Not Found', 404));   return app; } 

またデプロイについては AWS CDK ではなく AWS CLI を AI に組み立ててもらい、それを実行していました。というのは初期設定と反復部分を手っ取り早く分離、かつ再現性のある仕組みにしたかったというのと、CDK を組むほどの規模でもないというのが理由です。

AWS MCP サーバーと AWS CLI

11月初頭の段階で AWS MCP サーバー参照をする場合、AWS CLI の利用を抑制する指示が盛り込まれるようになりました。CLI/Shell ではなく MCP からの操作が強制されるようですが、今後変化もあるかもしれません。

AI-DLC(v2)と今回の開発

今回の進め方には、AI を用いた開発ライフサイクル「AI-DLC v2」は、特に意識せず作っています。というのも、人間が実装しても数日で終わりそうな仕事を肩代わりさせる程度で、ワークフロー化までする必要が無さそうだった(本当のところはわからん)というのが背景です。

とはいえ、AI-DLC v2 を読みつつ振り返ると、今回の開発と重なる部分がいくつかあったように思えます。要件を対話で具体化し、実装を小さく分け、テストで確認しながら修正する点です。この辺は AI-DLC の有無以前に AI との付き合い方に近いかもしれませんが、ともかく AI-DLC の考え方や実践をある程度学んでおくことで、小規模なシステムなら瞬時に作れる可能性も見えてきました。

あとがき

AWS Lambda Web Functions を用いた API 操作のセルフサービス化システムについて解説しました。小規模な Web アプリなら十分に展開できるでしょう。Amazon CloudFront を使うアーキテクチャーもあるので、規模や要件に応じて構成が考えられます。

また AI コーディングエージェントによる開発も MCP や CLI を用いることで、省力的かつ確実性高く実現できました。SORACOM の API はドキュメント公開もされてますし、文中でもご紹介した SORACOM CLI と Knowledge MCP サーバーを組み合わせれば、AI エージェントで IoT・SORACOM 開発ができることが伝われば幸いです。

― ソラコム松下 (Max)

参考資料

投稿 スイッチユーザー機能のセルフサービス化する裏側 ― AWS Lambda Web Functions の利用例 は SORACOM公式ブログ に最初に表示されました。

本記事はアフィリエイトプログラムによる収益を得ている場合があります

過去記事アーカイブ

2026年
01月
02月
03月
04月
05月
06月
07月
08月
09月
10月
11月
2025年
01月
02月
03月
04月
05月
06月
07月
08月
09月
10月
11月
12月
2024年
01月
02月
03月
04月
05月
06月
07月
08月
09月
10月
11月
12月
2023年
01月
02月
03月
04月
05月
06月
07月
08月
09月
10月
11月
12月
2022年
01月
02月
03月
04月
05月
06月
07月
08月
09月
10月
11月
12月
2021年
01月
02月
03月
04月
05月
06月
07月
08月
09月
10月
11月
12月