「このデバイス(IMEI)で使っているSIM(IMSI)はどれ?」を調べてみた(AWS Lambdaで)

できるけど、これで十分なのかは要件次第ですね。
2022.01.28

SORACOMには、開発者向けにAPIが提供されています。

今回は、「このデバイス(IMEI)って、どのSIM(IMSI)を使ってるんだっけ?」をあとで調べる方法があるか、SORACOM APIを使って試してみました。

おすすめの方

  • SORACOM APIを使いたい方
  • SORACOM APIをLambdaで使いたい方
  • SORACOM APIでIMEIからSIM(IMSI)を調べたい方
  • AWS SAMでLambdaを作りたい方
  • パラメータストアのSecureStringをLambdaで取得したい方

SORACOM APIを使う準備

  • SAMユーザ作成
  • 認証情報取得
  • SAMユーザのアクセス権限付与
  • System ManagerのパラメータストアにSORACOM SAMユーザの認証情報を格納
    • 認証キーID
    • 認証キーシークレット

上記については、次のブログを参考に行います。

なお、今回のアクセス権限は下記となります。

{
  "statements": [
    {
      "api": [
        "Subscriber:listSubscribers"
      ],
      "effect": "allow"
    }
  ]
}

SORACOM API用のパーミッション設定

Lambdaを作る

sam init

sam init \
    --runtime python3.8 \
    --name Lambda-Soracom-Api-Search-Imsi-Sample \
    --app-template hello-world \
    --package-type Zip

SAMテンプレート

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda-Soracom-Api-Search-Imsi-Sample

Parameters:
  SoracomKeyId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /SORACOM/SAM/KeyId

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.8
      Timeout: 10
      Policies:
        - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
      Environment:
        Variables:
          SORACOM_KEY_ID: !Ref SoracomKeyId
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get

  HelloWorldFunctionLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}

Outputs:
  HelloWorldApi:
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"

requirements.txt

requests

Lambdaコード

お試し実装のため、下記となっています。本番運用時には適宜検討し、実装してください。

  • APIトークンの有効期限はデフォルト設定です。
  • レートリミットの考慮をしていません。

API トークンの有効期間について

制限事項と注意事項

SORACOM API リファレンスを見る限り、「IMEIを指定して、情報取得する」というAPIは無さそうです。 そのため、下記の手順で探しました。

  1. 所持しているSIM情報の一覧を取得する
  2. 「最新のセッション情報内のIMEI」と「探したいIMEI」が一致するSIM情報を探す

なお、IMEIはセッション情報内に存在するため、次の点に注意する必要があります。今回は未考慮です。

  • 該当のSIMが1度もデータ送信してない可能性
  • 該当のSIMと機材(IMEI)の組み合わせが古い可能性
  • 該当のSIMと機材(IMEI)の組み合わせが複数ある可能性

また、SIMがたくさんある場合は、SIM情報取得時に条件マッチを活用すると良いですね。

  • tag_name
  • tag_value
  • status_filter
  • など

app.py

import os
import json
import boto3
import requests
from typing import Tuple, Optional

ssm = boto3.client('ssm')

SORACOM_ENDPOINT = 'https://api.soracom.io/v1'

IMEI = '3xxxxxxxxxxxxx0'


def lambda_handler(event, context):
    soracom_key_id = os.environ['SORACOM_KEY_ID']
    soracom_secret = get_soracom_secret()

    api_key, token = get_token(soracom_key_id, soracom_secret)

    subscribers = get_subscribers(api_key, token)

    imsi = search_imsi_by_imei(subscribers, IMEI)

    return {
        'statusCode': 200,
        'body': json.dumps({
            'imei': IMEI,
            'imsi': imsi,
        }),
    }

def get_soracom_secret() -> str:
    res = ssm.get_parameter(
        Name='/SORACOM/SAM/Secret',
        WithDecryption=True
    )
    return res['Parameter']['Value']

def get_token(soracom_key_id, soracom_secret) -> Tuple[str, str]:
    headers = {'Content-Type': 'application/json'}
    data = {
        'authKeyId': soracom_key_id,
        'authKey': soracom_secret,
    }
    resp = requests.post(f'{SORACOM_ENDPOINT}/auth', headers=headers, data=json.dumps(data))

    d = resp.json()

    return d['apiKey'], d['token']

def get_subscribers(api_key: str, token: str) -> list:
    headers = {
        'Content-Type': 'application/json',
        'X-Soracom-API-Key': api_key,
        'X-Soracom-Token': token,
    }
    resp = requests.get(f'{SORACOM_ENDPOINT}/subscribers', headers=headers)

    return resp.json()

def search_imsi_by_imei(subscribers: list, imei: str) -> Optional[str]:
    for item in subscribers:
        if 'sessionStatus' not in item:
            continue

        if item['sessionStatus']['imei'] == imei:
            return item['imsi']

    return None

デプロイ

sam build

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket cm-fujii.genki-deploy

sam deploy \
    --template-file packaged.yaml \
    --stack-name Lambda-Soracom-Api-Search-Imsi-Sample-Stack \
    --s3-bucket cm-fujii.genki-deploy \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

APIエンドポイントを確認する

aws cloudformation describe-stacks \
    --stack-name Lambda-Soracom-Api-Search-Imsi-Sample-Stack \
    --query 'Stacks[].Outputs'

動作確認

API Gatewayのエンドポイントにアクセスすると、IMSIが返ってきました。

$ curl https://xxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello
{"imei": "3xxxxxxxxxxxxx0", "imsi": "4xxxxxxxxxxxxx8"}

参考