自宅のラズパイにいつでもどこでもAzure Cloud Shellからアクセスする

外出時に自宅のラズパイが気になることありませんか？
ブラウザさえあれば、Azure Cloud Shellからアクセスできますので、紹介します。


# 仕組み
`IoT Hub デバイス ストリーム`の機能を使います。2020/11/1現在プレビュー状態ですが、誰でも試すことが可能です。

> Azure IoT Hub デバイス ストリームによって、さまざまな cloud-to-device 通信シナリオのセキュリティで保護された双方向 TCP トンネルの作成が容易になります。 デバイス ストリームは、デバイスとサービス エンドポイント間のプロキシとして機能する、IoT Hub ストリーミング エンドポイントによって仲介されます。

![device-stream-proxy-diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/fc59c46c-353f-ec4e-ca64-b560166dbd26.png)

（画像は公式サイトのものです）
https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-device-streams-overview

IoT Hubが仲介することで、通常はファイアーウォールに防がれてアクセスできないRaspberry Piに外部からアクセスできるようになります。


# 環境

## デバイス側

Raspberry Pi 3
Raspberry Pi OS (32-bit) Lite 最新版（2020-08-20）

# セットアップ

以下の手順で進めます。

1. Azure IoT Hubの設定
2. デバイス（Raspberry Pi）側の設定
3. サービス（Azure Cloud Shell）側の設定


## 1. Azure IoT Hubの設定

Azure IoT Hubの作成は、Azure Cloud Shellで実行します。

### Azure IoT 拡張機能を追加

```console:AzureCloudShell
az extension add --name azure-iot
```

### Azure IoT Hubを作成

無料のSKUで作成します。
デバイスストリームに対応しているリージョンは限られています。

* 米国中部
* 米国中部 EUAP
* 東南アジア
* 北ヨーロッパ

```console:AzureCloudShell
az iot hub create --name [IoT Hub名] --resource-group [リソースグループ名] --location centralus --sku f1 --partition-count 2
```

### デバイスを作成

Raspberry PiがIoTデバイス扱いとなりますので、IoT Hubにデバイスを作成します。デバイスIDは`raspberrypi`としました。

```console:AzureCloudShell
az iot hub device-identity create --hub-name [IoT Hub名] --device-id raspberrypi
```

### 接続文字列を取得

接続文字列は２種類必要です。

デバイス（Raspberry Pi）側で使用する接続文字列

```console:AzureCloudShell
az iot hub device-identity connection-string show --hub-name [IoT Hub名] --device-id raspberrypi --output tsv
```

サービス（Azure Cloud Shell）側で使用する接続文字列

```console:AzureCloudShell
az iot hub connection-string show --policy-name service --hub-name [IoT Hub名] --output tsv
```

これで、Azure IoT Hubの設定は完了です。

## 2. デバイス（Raspberry Pi）側の設定

つづけてRaspberry Piの設定です。
OSをインストールした直後の状態から進めたいと思います。

### 必要なソフトウェアのインストール

```console:RaspberryPi
sudo apt install git cmake libssh-dev libcurl4-openssl-dev uuid-dev -y
```

### デバイス側ソースの取得

パブリックプレビュー用ブランチを使用します。

```console:RaspberryPi
git clone -b public-preview https://github.com/Azure/azure-iot-sdk-c.git
cd azure-iot-sdk-c
git submodule update --init
```

### ソースの修正

`azure-iot-sdk-c/iothub_client/samples/iothub_client_c2d_streaming_proxy_sample`にある`iothub_client_c2d_streaming_proxy_sample.c`にデバイス側で使用する接続文字列を指定します。（70行目です）

```c:iothub_client_c2d_streaming_proxy_sample.c
static const char* connectionString = "[device connection string]";
```

### makeする

公式ドキュメントでは`make -j`と書いてありますが、メモリ不足で失敗しますので、`-j`は外しました。

```console:RaspberryPi
cd ~/azure-iot-sdk-c/
mkdir cmake
cd cmake
cmake ..
make
```

makeが成功すると`azure-iot-sdk-c/cmake/iothub_client/samples/iothub_client_c2d_streaming_proxy_sample/`に`iothub_client_c2d_streaming_proxy_sample`が生成されます

わかり易い場所に移動します。

```console:RaspberryPi
mkdir ~/bin
cp iothub_client/samples/iothub_client_c2d_streaming_proxy_sample/iothub_client_c2d_streaming_proxy_sample ~/bin/
```

### 自動起動の設定

serviceファイルを作成します。

```text:iothub_client_c2d_streaming_proxy.service
[Unit]
Description=IoT Hub Client C2D Streaming Proxy
After=network.target

[Service]
ExecStart=/home/pi/bin/iothub_client_c2d_streaming_proxy_sample
WorkingDirectory=/home/pi/bin
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi

[Install]
WantedBy=multi-user.target
```

serviceを有効にして起動します。

```console:RaspberryPi
sudo mv ~/iothub_client_c2d_streaming_proxy.service.service /etc/systemd/system/
sudo systemctl enable iothub_client_c2d_streaming_proxy.service
sudo systemctl start iothub_client_c2d_streaming_proxy.service
```

デバイス（Raspberry Pi）側の設定は完了です。

## 3. サービス（Azure Cloud Shell）側の設定

サービス側のサンプルはNode.jsで動作します。Azure Cloud Shellで提供されているNode.jsのバージョンは`v8.16.0`ですが、v10以上が必要なので、まずはNode.jsをセットアップします。

### Node.jsのインストール

```console:AzureCloudShell
wget https://nodejs.org/dist/v10.19.0/node-v10.19.0-linux-x64.tar.gz
mkdir ~/node
tar zxf node-v10.19.0-linux-x64.tar.gz -C ~/node/
cd node
ln -s node-v10.19.0-linux-x64 node
echo 'export PATH=~/node/node/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
```

### サービス側ソースの取得

```console:AzureCloudShell
wget https://github.com/Azure-Samples/azure-iot-samples-node/archive/streams-preview.zip
unzip streams-preview.zip
mv azure-iot-samples-node-streams-preview/iot-hub/Quickstarts ~/
```

### 外部ライブラリーの取得

```console:AzureCloudShell
cd ~/Quickstarts/device-streams-service/
npm install azure-iothub@streams-preview
npm install
```

### 環境変数の設定

Node.jsのプログラムは環境変数を参照するようですので、必要な情報を環境変数にセットします。
`IOTHUB_CONNECTION_STRING`にサービス側で使用する接続文字列をセットします。
※Azure CLIで取得して設定するように工夫してみました

```console:AzureCloudShell
echo 'export IOTHUB_CONNECTION_STRING="`az iot hub connection-string show --policy-name service --hub-name [IoT Hub名] --output tsv`"'>> ~/.bashrc
echo 'export STREAMING_TARGET_DEVICE=raspberrypi' >> ~/.bashrc
echo 'export PROXY_PORT=2222' >> ~/.bashrc
source ~/.bashrc
```

これでサービス（Azure Cloud Shell）側の設定は完了です。

# 接続

準備は整いましたので、実際に接続してみましょう。
デバイス側はもう特に触りませんので、Azure Cloud Shell上で作業します。

```console:AzureCloudShell
cd ~/Quickstarts/device-streams-service/
node proxy.js &
ssh pi@localhost -p 2222
```

Node.jsのProxyプログラムはフォアグラウンドで動作する仕組みなのでバックグラウンドで動作させます。
localhost（=Azure Cloud Shell）の2222ポートにSSHで接続することで、デバイス側に接続ができます。

Azure Cloud Shellのインスタンスが停止したあとでも、`接続`の手順を行うとデバイス側に接続できます。


AzureのAndroidアプリからもこの通り。
これでいつでもどこでもRaspberry Piにアクセスできますね。

![Screenshot_20201101-205702.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/e389cb53-5b25-df8e-12a7-a4e5828fc1ba.png)



# 参考サイト

https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-device-streams-overview
https://docs.microsoft.com/ja-jp/azure/iot-hub/quickstart-device-streams-proxy-c
https://docs.microsoft.com/ja-jp/azure/iot-hub/quickstart-device-streams-proxy-nodejs