Service Bus triggerでキューの情報をDBに非同期で書き込む（SQL Database）


[Azure Storage Tableへの非同期書き込み]([https://qiita.com/Hikaruru_g/items/4f6c956c3ac9549bde08)がうまくいったので、いよいよ本命のSQL Databaseへの非同期書き込みにチャレンジ！

**2020/02/24 追記
KeyVaultシークレット情報(接続文字列)を管理させるようにできました。後半に記載。**

---

いつも通り情報があまりないです…C#の知識があればさくっと書けるのかもしれないですが、私にはないので手探りで進めます。

VisualStudioを利用してDB参照する方法は以下のサイトが非常に参考になります！
参考サイト：[Azure FunctionsとSQLデータベースを連携する](https://qiita.com/nakamurakko/items/51ed8da23926eff43d1d)

# １．Azure Storage Tableへの非同期書き込みと同様にテンプレから作成できるかと思いきや、ない。

ブレードから統合を選択し、出力から新規追加(+新しい出力)しても見当たらない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/f485890b-c5ea-9370-dfe9-86f024db0c2a.png)

# ２．コーディングの前の下準備（DB作成、テーブル作成）
参考サイトを元にDB作成

```sql:クエリエディタを利用

create table zodiac_type(
    id INT IDENTITY(1,1),
    name nvarchar(20)
)

insert into zodiac_type(name) values ('a');
insert into zodiac_type(name) values ('b');
insert into zodiac_type(name) values ('c');
insert into zodiac_type(name) values ('d');
insert into zodiac_type(name) values ('e');
insert into zodiac_type(name) values ('f');
insert into zodiac_type(name) values ('g');
insert into zodiac_type(name) values ('h');
insert into zodiac_type(name) values ('i');
insert into zodiac_type(name) values ('j');
insert into zodiac_type(name) values ('k');
insert into zodiac_type(name) values ('l');
```

# ３．DBへの書き込み処理は手で書く

１．アプリケーション設定にSQL Databaseの接続文字列を追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/545f8f5b-dca7-aee0-c4b1-660f57c5ec86.png)

２．コード内で先ほど設定した接続文字列が参照できることを確認

```C#:run.csx(一部)
var str = Environment.GetEnvironmentVariable("SQLDATABASE_CONNECT");
log.LogInformation($"{str}"); 
```

３．「Service Busトリガーで起動し、メッセージ内容をDBにINSERTする」処理を加える

Tips. Functionsの.Net Core3.1だとSystem.Data.SqlClientの一部の機能がコンパイルエラーになりました。~2.xに変更することで解消。（それでいいのか・・・）

```C#:run.csx(全量)
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;

public static void Run(string myQueueItem, ILogger log)
{
    log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}");

   // Get the connection string from app settings and use it to create a connection.
    var str = Environment.GetEnvironmentVariable("SQLDATABASE_CONNECT");
    using (SqlConnection conn = new SqlConnection(str))
    {        
        try{
            // 接続文字列を利用してSQLDatabaseに接続
            conn.Open();
        }catch (Exception exc)
        { 
            log.LogInformation($"connect Error!");    
        }        
        var text = "select count(*) from [dbo].[zodiac_type];";
        using (SqlCommand cmd = new SqlCommand(text, conn))
        {
            // SQLの実行はココ selectなのでExecuteScalarクラスを利用
            var rows = cmd.ExecuteScalar();
            log.LogInformation($"{rows} rows were updated");
        }

        var queryCommand = "insert into [dbo].[zodiac_type] (name) values ('" + ($"{myQueueItem}") +"')";
        log.LogInformation($"{queryCommand}");   
        using (SqlCommand cmdcmd = new SqlCommand(queryCommand, conn))
        {
            // SQLの実行はココ insertなのでExecuteNonQueryクラスを利用
            var rowsrows = cmdcmd.ExecuteNonQuery();
        }
    }
}
```

４．Service Busに書き込まれた情報が無事SQL Databaseのnameテーブルに追加されたことを確認

ブラウザから打鍵
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/5eaf0794-d01c-2872-7da5-95eef3914a79.png)


```bash:Azureポータル(コンソールログ)
2020-02-24T12:27:32.182 [Information] Executing 'Functions.ServiceBusQueueTrigger1' (Reason='New ServiceBus message detected on 'queue01'.', Id=********-e58a-477e-a9e9-1fc248e8f359)
2020-02-24T12:27:32.183 [Information] Trigger Details: MessageId: 589b9068e2a7440886d03930a2ecd770, DeliveryCount: 1, EnqueuedTime: 2/24/2020 12:27:32 PM, LockedUntil: 2/24/2020 12:28:02 PM, SessionId: (null)
2020-02-24T12:27:32.191 [Information] C# ServiceBus queue trigger function processed message: W
2020-02-24T12:27:32.193 [Information] 28 rows were updated
2020-02-24T12:27:32.194 [Information] insert into [dbo].[zodiac_type] (name) values ('W')
2020-02-24T12:27:32.201 [Information] Executed 'Functions.ServiceBusQueueTrigger1' (Succeeded, Id=********-e58a-477e-a9e9-1fc248e8f359)
```

クエリエディタからセレクトを実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/bfef48a0-3df4-2152-7c41-0f0488207877.png)

---

# 2020/02/24 追記
接続文字列情報をアプリケーション設定として持っておくとセキュリティ管理責任をアプリケーションも負わなければいけないので、極力KeyVaultに集約管理したい。では、KeyVaultに保管したシークレット情報を読み取る実装に変更してみよう！というお話です。

# １．ServiceBusのキューにデータを書き込む関数(HTTPトリガー)の改修

|  できていないこと  |  できていること  |
|:-----------------|:------------------|
| FunctionsがKeyVaultから接続文字列を取得して、|ServiceBusのキューにデータを書き込む

現状は、Functionsが**アプリケーション設定内にある接続文字列**を取得するように統合時に設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/47aa6dac-a34d-45b8-d658-21a7d0f1f1bb.png)

後から確認する場合はアプリケーション設定から確認可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/7ab5bf0c-0ddb-87b1-9162-45d17a3e2119.png)

でもこの変数名はコード内に出てこないし、接続処理はどうやってるんだ？と思っていろいろあさっているとfunction.jsonに登録されていた模様
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/c9bf1c37-4fcd-c2a4-a6c4-920a41c19c44.png)

１．マネージドIDの作成と関連付け

ここまでわかればKeyVaultからデータをとってくるのは容易いです。まずはFunctionsでマネージドIDを作成して、KeyVaultのシークレットに一覧・取得を権限付け。詳細は過去記事参照([KeyVaultのシークレット情報をマネージドIDを利用して取得する)
](https://qiita.com/Hikaruru_g/items/2e006cc685341ec866cf)

２．シークレット識別子の把握

この時のシークレット情報のURL(シークレット識別子)をメモ

例）ttps://KeyVault名.vault.azure.net/secrets/シークレット名/**********************

３．Key Vault 参照形式に書き換えてメモ帳に用意

参考：[App Service と Azure Functions の Key Vault 参照を使用する](
https://docs.microsoft.com/ja-jp/azure/app-service/app-service-key-vault-references)

> 参照構文
Key Vault 参照の形式は @Microsoft.KeyVault({referenceString}) です。{referenceString} は次のいずれかのオプションで置換されます。

４．一番最初のアプリケーション設定の値欄にKey Vault 参照形式で貼り付け

緑チェックでキーコンテナの参照　になったらOK

例）@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/7b56dfc9-e152-87cf-3e59-ee81036e52d0.png)


# ２．ServiceBusのキューからデータを取得する関数(Service Bus trigger)の改修

こちらの関数はServiceBusの接続情報とSQLDatabaseの接続情報を2つ持っているので、変更箇所が2箇所になるだけ。

１．シークレット情報を作成して、シークレット識別子を入手。

２．Key Vault 参照形式に変更して、アプリケーション設定の値を変更。

３．以下のようになればOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/038c9df6-fab0-cd56-a95c-6dfe74af825f.png)


４．最後にもう一回打鍵して、想定した挙動で動くことを確認して終了

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/7d73a46a-537d-cef8-484b-81aa2189a45b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/de174b7c-796a-1922-d299-3768c8827fa9.png)