Azure Functions Premium プランのリージョン仮想ネットワークの統合を試してみた

Azure Functions Puremium プランでリージョン仮想ネットワークの統合というのが一般提供されたみたいですね。

[Azure Functions Premium プランのリージョン仮想ネットワークの統合](https://azure.microsoft.com/ja-jp/updates/functions-premium-vnet/)

仮想ネットワークまわりはめんどくさい印象しかなくて、なるべく遠ざけてきたのですが、この機会に触ってみたいと思います。

## 確認方法

仮想ネットワークのサービス エンドポイントっていうのを使って Azure Functions Premium から SQL Database にアクセスしてみようと思います。
なんとなく、Database がインターネットにつながっているのを嫌がる人も多いし自分も、なんとなく理解できるので。

ということで Azure ポータルでポチポチリソースを作っていきます。全部同じリージョンに作りましょう。

- Azure Functions Premium plan
- SQL Database (安いサーバーレスモデル)
- 仮想ネットワーク
    - サブネット 2 つ (default: 10.0.0.0/24, functions: 10.0.1.0/24)

サブネットを作るときにサービス エンドポイントを構成できて、そこで Microsoft.SQL を 10.0.1.0/24 のほうに追加します。（作ったあとからでも設定できます）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/c7a4544e-4857-8c5c-090d-209de8051410.png)

SQL Database の方を完全にふさいでしまう前に DB 作ってしまいます。自分の IP アドレスを SQL Database のファイアウォールに追加して、SQL Database のクエリエディター（プレビュー）から以下の SQL を実行してテーブルとデータを作っておきました。

```sql
CREATE TABLE MESSAGES (
  ID INTEGER PRIMARY KEY,
  MESSAGE NVARCHAR(256)
);

INSERT INTO MESSAGES(ID, MESSAGE) VALUES(1, 'Hello from SQL Database');
```

## Azure Functions の構成

とりあえず Azure Functions で VNet 統合をします。Azure Functions のネットワークの設定を開いて NNet 統合の「構成するにはここをクリック」を選択しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/27812b82-d09c-dce2-ad27-31fdb9157c2d.png)

そして「VNet の追加」を選択して、先ほど作っておいた functions という名前のサブネットを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/b7adba8c-20d7-caf1-fbd2-067c93584696.png)


適当に Azure Functions のコードを作成します。とりあえず SQL Database につながればいいので手抜きな感じで、こんなコードを書きました。

```csharp
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Data.SqlClient;
using Microsoft.Extensions.Configuration;

namespace PrivateNetFunc
{
    public class Function1
    {
        public Function1(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        private IConfiguration Configuration { get; }

        [FunctionName("Function1")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
            ILogger log)
        {
            string result = null;
            using (var conn = new SqlConnection(Configuration.GetConnectionString("DB_CON")))
            {
                await conn.OpenAsync();
                log.LogInformation("Opened");
                using (var command = conn.CreateCommand())
                {
                    command.CommandText = "SELECT TOP 1 MESSAGE FROM MESSAGES";
                    log.LogInformation($"Execugint Query: {command.CommandText}");
                    using (var r = await command.ExecuteReaderAsync())
                    {
                        log.LogInformation($"Executed ExecuteReaderAsync");
                        await r.ReadAsync();
                        log.LogInformation($"Executed ReadAsync");
                        result = r.GetString(0);
                        log.LogInformation($"Get data: {result}");
                    }
                }

                await conn.CloseAsync();
            }

            return new OkObjectResult($"{DateTime.UtcNow}: {result}");
        }
    }
}
```

SQL Database の接続文字列をポータルからゲットして Azure Functions の接続文字列に DB_CON という名前でついかしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/a8448973-c41b-068c-e4a9-08331e3844ce.png)

そして、Azure Functions にコードをデプロイします。

## 動作確認（動かないことを確認）

まだ、ちゃんと設定が終わっていないのでエラーになるはずです。試してみましょう。
叩くとこんな感じのエラーが出ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/755cc35c-7a58-9109-9107-00cb8326f77f.png)

## SQL Server の仮想ネットワークからの接続受け入れ設定

仮想ネットワークのサービスエンドポイントで Microsoft.Sql を追加しましたが、まだ SQL Server 側で仮想ネットワークからの通信を行け入れるようにしていなかったので SQL Server のファイアウォールと仮想ネットワークで、既存の仮想ネットワークを追加を選択して、 functions の仮想ネットワークを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/6f19ff63-0da9-6713-20de-7a8e2fc4ded9.png)

## 再度動作確認（動くことを確認）

この設定まで完了すると、関数のエンドポイントを叩くことで、ちゃんと動くようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/dc2ce2aa-f643-e769-fe1a-f3374bdcbe63.png)

## 余談

Azure Functions は、仮想ネットワーク上にあるわけじゃないので、その点は注意です。
あと、Azure Functions の全ての送信側の通信を仮想ネットワーク側に回して、ネットワークセキュリティグループとかで制御したい場合には、アプリケーション設定に以下の項目を追加することで、[RFC 1918](https://www.nic.ad.jp/ja/translation/rfc/1918.html) 以外の通信も仮想ネットワークにルーティングするようになります。

- WEBSITE_VNET_ROUTE_ALL という名前で値に 1 を設定

## まとめ

こんだけ、簡単に仮想ネットワークに統合できて、仮想ネットワークからのアクセスのみに SQL Db やらなんやら対応サービスを構成するだけで接続元を絞れるなら仮想ネットワーク使うのもいいですね。