[Azure] WebApp、FunctionAppのVNet統合を使ってみる

Azure App ServiceのVNet統合(VNet Integration)は、App Serviceアプリケーションがインターネット経由のルーティングを使用せずに仮想ネットワーク内のリソースにアクセス可能になる機能です。

同様の機能に[App Service Environment](https://docs.microsoft.com/en-us/azure/app-service/environment/intro)がありますが、そちらはより大規模に展開する場合のオプションとなります。

今回は図のように WebApp、FunctionAppをVNet統合し、そこから 仮想マシンにプライベートIPでアクセスする構成を試してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/7cf58a88-4054-74fe-68da-186bec7cb5e3.png)

# VNetの設定

構成図にあるように、App ServiceをVNet統合するためには、統合用に専用サブネットが必要となります。といっても空のサブネットを作っておけばいいだけでそれ以外の注意点は特にない(必要な設定は統合設定時におこなわれる)ため、今回は VM、WebApp、FunctionApp用のサブネットをそれぞれ作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/4c088e20-58de-3e06-db83-1a6ee26ebbec.png)

# VMの設定

動作確認用にVM(Ubuntu)を1台作成しました。プライベートIPでアクセスできることの確認のため、nginxをインストールして確認用のHTMLファイルを配置しました。

```
azureuser@vm-nginx-test:~$ sudo apt install -y nginx
※ログは省略※
azureuser@vm-nginx-test:~$ sudo vi /var/www/html/vnet-test.html

azureuser@vm-nginx-test:~$ ls -l /var/www/html/
total 8
-rw-r--r-- 1 root root 612 Jan  5 08:16 index.nginx-debian.html
-rw-r--r-- 1 root root 142 Jan  5 08:23 vnet-test.html

azureuser@vm-nginx-test:~$ cat /var/www/html/vnet-test.html
<!DOCTYPE html>
<html>
<head>
<title>app service vnet integ demo</title>
</head>
<body>
<h1>app service vnet integ demo</h1>
</body>
</html>

azureuser@vm-nginx-test:~$ curl http://10.3.0.4/vnet-test.html
<!DOCTYPE html>
<html>
<head>
<title>app service vnet integ demo</title>
</head>
<body>
<h1>app service vnet integ demo</h1>
</body>
</html>
```

セキュリティグループ設定：Port 80は仮想ネットワーク内からのみアクセス可能としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/dc690c17-12d4-a28a-6ac8-8c10bb42cd83.png)

後はこのファイルにapp serviceからアクセスできるようになることの確認になります。

# WebAppの設定

WebAppをVNet統合するためには、App Serviceプランが Standardまたは Premiumプランである必要があります。設定はネットワーク→VNet統合の「構成するにはここをクリック」から実施します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/02143912-5438-211b-4ce9-71c1d59c8338.png)

未対応プランの場合はここでメッセージが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/3146167d-e018-a802-4682-dbee9c7ae037.png)

統合作業は統合先のネットワーク、サブネットを選択するだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/cff69ca2-16a7-5500-e508-a5af6542d8ee.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/6697e284-b162-64c8-eb64-e10b78a56afe.png)

統合できたら、コンソールからアクセス確認してみます。

```
Last login: Tue Jan  5 08:54:32 2021 from 169.254.129.4
  _____
  /  _  \ __________ _________   ____
 /  /_\  \___   /  |  \_  __ \_/ __ \
/    |    \/    /|  |  /|  | \/\  ___/
\____|__  /_____ \____/ |__|    \___  >
        \/      \/                  \/
A P P   S E R V I C E   O N   L I N U X

Documentation: http://aka.ms/webapp-linux
PHP quickstart: https://aka.ms/php-qs
PHP version : 7.4.9
Note: Any data outside '/home' is not persisted
root@9ea5xxxxxxxx:/home# curl http://10.3.0.4/vnet-test.html
<!DOCTYPE html>
<html>
<head>
<title>app service vnet integ demo</title>
</head>
<body>
<h1>app service vnet integ demo</h1>
</body>
</html>

```

テスト用に配置したHTMLが取得できることが確認できました。

WebAppで表示できることも確認してみます。今回は簡単なphpでの表示を確認しました。

```php:vnet-test.php
<?php
    $output=null;
    $retval=null;
    exec('curl http://10.3.0.4/vnet-test.html', $output, $retval);
    echo "Returned with status $retval and output:";
    foreach ($output as $line) {
        print $line;
    }
?>
```

デプロイしてWebAppにアクセスしてみると、HTMLの内容が出力されることが確認できました（そのまま出力しているのだけなので titleタグが bodyに入っておかしなことになっていますが.....内容は取得できています）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/cc07e806-5c82-128d-fbd7-6bccd0c79fde.png)

# FunctionAppの設定

同様にFunctionAppのVnet統合を試してみます。手順はほぼ同様ですが、FunctionAppではApp Serviceプランが Premium必須となります。設定は同様にネットワーク→VNet統合の「構成するにはここをクリック」から実施します。WebAppとの細かな違いとして、FunctionAppでは対応プランでない場合は「Vnet統合」のメニュー自体が表示されていませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/6b5a0639-9113-15e6-f32d-5f6e3a468a1c.png)

統合作業は統合先のネットワーク、サブネットを選択するだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/08d55d0b-ebed-f013-a092-fdbfb4df7aa2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/51c30a74-9457-a599-61d5-5033599a8e1b.png)

統合できたら、コンソールからアクセス確認してみます。

```
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@7160xxxxxxxxx:~# curl http://10.3.0.4/vnet-test.html
<!DOCTYPE html>
<html>
<head>
<title>app service vnet integ demo</title>
</head>
<body>
<h1>app service vnet integ demo</h1>
</body>
</html>
```

FunctionAppで取得できることの確認のため、nodejsで簡単な関数を作成して確認します。

```javascript:index.js
module.exports = function (context, myTimer) {
    var timeStamp = new Date().toISOString();
    context.log('JavaScript timer trigger function ran!:', timeStamp);   
    
    const { exec } = require('child_process')
    exec('curl http://10.3.0.4/vnet-test.html', (err, stdout, stderr) => {
        if (err) {
            context.log(`stderr: ${stderr}`)
            return
        }
        context.log(`stdout: ${stdout}`)
    })
};

```

実行してログを確認してみると、HTMLが取得できていることが確認できました。

```
2021-01-05T09:27:55  Welcome, you are now connected to log-streaming service.Starting Log Tail -n 10 of existing logs -
※途中のログは省略※
2021-01-05T09:27:55.214986884Z: [INFO]        stdout: <!DOCTYPE html>
2021-01-05T09:27:55.214991684Z: [INFO]        <html>
2021-01-05T09:27:55.214994984Z: [INFO]        <head>
2021-01-05T09:27:55.214998184Z: [INFO]        <title>app service vnet integ demo</title>
2021-01-05T09:27:55.215001784Z: [INFO]        </head>
2021-01-05T09:27:55.215004884Z: [INFO]        <body>
2021-01-05T09:27:55.215007985Z: [INFO]        <h1>app service vnet integ demo</h1>
2021-01-05T09:27:55.215157286Z: [INFO]        </body>
2021-01-05T09:27:55.217799118Z: [INFO]        </html>
```

# アクセス拒否の確認

以上でプライベートIPでのアクセスは確認できました。最後にテストとして、セキュリティグループ設定を変えてアクセス拒否されることを確認してみます。

VMのセキュリティグループに、WebApp、FunctionAppサブネットからのPort 80拒否設定を追加しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/dc1286bf-c338-f04a-8090-aa25aa37ca3a.png)

設定変更後WebAppで確認すると、接続エラーとなりました。

```
Last login: Tue Jan  5 09:10:37 2021 from 169.254.129.4
  _____
  /  _  \ __________ _________   ____
 /  /_\  \___   /  |  \_  __ \_/ __ \
/    |    \/    /|  |  /|  | \/\  ___/
\____|__  /_____ \____/ |__|    \___  >
        \/      \/                  \/
A P P   S E R V I C E   O N   L I N U X

Documentation: http://aka.ms/webapp-linux
PHP quickstart: https://aka.ms/php-qs
PHP version : 7.4.9
Note: Any data outside '/home' is not persisted
root@d87exxxxxxxxx:/home# curl http://10.3.0.4/vnet-test.html
curl: (7) Failed to connect to 10.3.0.4 port 80: Connection timed out

```

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/6179a754-da3e-96f2-80e0-f0a3889c82a5.png)|
|:-:|


同様にFunctionAppでも接続エラーになることが確認できました。

```
root@312cxxxxxxxx:~# curl http://10.3.0.4/vnet-test.html
curl: (7) Failed to connect to 10.3.0.4 port 80: Connection timed out

```

```
2021-01-05T09:33:45  Welcome, you are now connected to log-streaming service.Starting Log Tail -n 10 of existing logs ----/appsvctmp/volatile/logs/runtime/10135e79f2864959dec634c480504dfe7961768345c680515fe60342b25b31da.log
※途中のログは省略※
2021-01-05T09:37:21.346997183Z: [INFO]        stderr:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
2021-01-05T09:37:21.347076284Z: [INFO]                                         Dload  Upload   Total   Spent    Left  Speed
2021-01-05T09:37:21.349585315Z: [INFO]        
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0

  0     0    0     0    0     0      0      0 --:--:--  0:02:07 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:02:08 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:02:09 --:--:--     0
curl: (7) Failed to connect to 10.3.0.4 port 80: Connection timed out

```

以上で WebApp、FunctionAppの VNet統合が確認できました。

# 参考資料

- [Integrate your app with an Azure virtual network](https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet#new-vnet-integration)
- [チュートリアル: Functions を Azure 仮想ネットワークに統合する](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-create-vnet)
- [AppService環境の概要](https://docs.microsoft.com/en-us/azure/app-service/environment/intro)