Visual Studio Code でprecompiled なAzure Functions を作る。


Azure Functions の公式ドキュメントでは、C#スクリプトで開発する方法について多く記述されている。
Macで開発する場合、Windows のVisual StudioのようなAzure Function Toolsのようなツールが無い。Visual Studio for MacやVisual Studio Codeだと、C#スクリプトのシンタックスハイライトやコードジャンプのサポートが乏しいので、Azure Functions の開発が難しい。
幸いなことに、Azure Funtions はビルドしたC#のプロジェクトを実行することも可能。

そこで、ここではそのC# ソリューション、およびプロジェクトの作成手順について解説する。

# 前提条件

この記事は、下記のものが事前にインストール済みであることを前提に書いています。

* Azure Function
	* デプロイ時に`func` コマンドでアプリケーション名を指定するため、予めプロビジョニングしておきます。
	* :exclamation: ランタイムバージョンは2.0 
* [dotnet](https://www.microsoft.com/net/learn/get-started/macos)
* [azure-function-core-tools](https://www.npmjs.com/package/azure-functions-core-tools)
* [Visual Studio Code](https://www.microsoft.com/ja-jp/dev/products/code-vs.aspx)

# ソリューションの作成

まずは、ソリューションを作成する。

```
mkdir myFunctions
cd myFunctions
dotnet new sln
```

ソリューションはここまでで、肝心なFunctionのプロジェクトを作ります。

# プロジェクトの作成

実際のFunctionを開発するためのプロジェクトを作成する。
ここではプロジェクト名をFunctionsとします。

```
dotnet new classlib -o Functions -n Function
dotnet sln ./myFunctions.sln add ./Functions/Function.csproj
```

Nuget `Microsoft.Net.Sdk.Functions` をFunctionsプロジェクトに追加します。

```
dotnet add Functions package Microsoft.Net.Sdk.Functions
```

この段階で使えるトリガーの種類は、[リポジトリ](https://github.com/Azure/azure-webjobs-sdk)を見ると、

* BlobTrigger
* QueueTrigger

の2つが提供されているようです。
更に追加するには、

* [Azure/azure-webjobs-sdk-extensions: Azure WebJobs SDK Extensions](https://github.com/Azure/azure-webjobs-sdk-extensions)

を追加することで出来そうです（未検証）。

## Function の設定

ローカルで実行できるようにするため、`func init` で設定ファイルを生成します。

```
$ cd myFunctions/Function
$ func init
```


# BlobトリガーのFunctionを作る

ここではとても簡単なFunctionを1つ作ります。
Blobストレージへアップロードされたファイル名とサイズをログに表示するだけのFunctionです。

```BlobTrigger.cs
using System;
using System.IO;
using Microsoft.WindowsAzure.Storage;
using Microsoft.Azure.WebJobs;       // (1)
using Microsoft.Azure.WebJobs.Host;  //

namespace Function
{
    public class BlobTriggerFunction
    {
        [StorageAccount("MyBlobStorage")] // (2)
        [FunctionName("BlobTriggerFunction")]  // (3)
        public static void Run(
            [BlobTrigger("updated/{name}")] Stream myBlob,  // (4)
            string name, TraceWriter log)
        {
            log.Info($"Blob Trigger function processed: {name} ({myBlob.Length} KB)");
        }
    }
}
```

1. アトリビュートを使えるようにするために、`Microsoft.Azure.WebJobs`と`Microsoft.Azure.WebJobs.Host`を読み込み、
2. Blobストレージへ接続するための接続文字列を持つ環境変数を指定して、
3. ビルド時に生成されるFunction名を指定します。



# ビルド

ビルドの前に`local.settings.json` の中に必要な環境変数が記述されていることを確認しましょう。
ここでは少なくともBlobトリガーを動かすために

* `MyBlobStorage`
* `AzureWebJobsStorage`
* `AzureWebJobsDashboard`

が必要です。

## ビルド後の修正

今の段階では、ビルドにより作成された`function.json`はそのままでは使えず、2ヶ所修正が必要です。

```Functions/bin/Debug/netstandard2.0/BlobTriggerFunction/function.json
{
  "generatedBy": "Microsoft.NET.Sdk.Functions.Console-1.0.5",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "blobTrigger",
      "connection": "AzureWebJobsStorage",
      "direction": "in",  # (1)
      "path": "uploaded/{name}",
      "name": "myBlob"
    }
  ],
  "disabled": false,
  "scriptFile": "../bin/Functions.dll",   # (2)
  "entryPoint": "Function.BlobTriggerFunction.Run"
}
```

1つめが、`bindings.direction` の追加です。Functionの入力なのか出力なのかを指定します。トリガーの場合は、入力なので`in`を設定します。

2つめが、`scriptFile`。
***ビルド時は、相対パスになっていないので、デプロイの前に相対パスに修正する必要がある。***
修正しないままデプロイすると、Azure Portalの画面では認識されません。

ビルドする度に必要なので、面倒くさい… :sob: 

# ローカルで実行する

`func` コマンドを使って起動できるか確認してみましょう。

```
$ cd bin/Debug/netstandard2.0
$ func host start

                  %%%%%%
                 %%%%%%
            @   %%%%%%    @
          @@   %%%%%%      @@
       @@@    %%%%%%%%%%%    @@@
     @@      %%%%%%%%%%        @@
       @@         %%%%       @@
         @@      %%%       @@
           @@    %%      @@
                %%
                %

[11/4/17 2:40:09 AM] Reading host configuration file '/Users/****/Projects/private/myFunctions/Functions/bin/Debug/netstandard2.0/host.json'
[11/4/17 2:40:09 AM] Host configuration file read:
[11/4/17 2:40:09 AM] { }
[11/4/17 2:40:10 AM] Generating 1 job function(s)
[11/4/17 2:40:10 AM] Starting Host (HostId=p40902-1763669228, Version=2.0.11308.0, ProcessId=55029, Debug=False, Attempt=0)
Listening on http://localhost:7071/
Hit CTRL-C to exit...
[11/4/17 2:40:10 AM] Found the following functions:
[11/4/17 2:40:10 AM] Function.BlobTriggerFunction.Run
[11/4/17 2:40:10 AM]
[11/4/17 2:40:10 AM] Host lock lease acquired by instance ID '000000000000000000000000E0CC05CB'.
[11/4/17 2:40:11 AM] Job host started
```


# デプロイ

デプロイもそのまま`func`コマンドで実行できます。
`bin/Debug/netstandard2.0` に移動し、下記のコマンドを実行します。

```
$ func azure functionapp 
```


ということで、C#スクリプトだとVisual Studio Codeがコード補完とかサポートしてなかったり、パッケージのインストールに癖があったりとするのが、C#のコードとして扱えるようになりました。


# 参考

* [Azure Functions での .NET クラス ライブラリの使用 | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-dotnet-class-library)
* [Azure/azure-functions-vs-build-sdk: MSBuild task for Azure Functions](https://github.com/Azure/azure-functions-vs-build-sdk)
	* `samples` 以下にあるサンプルコードが参考になります。
* [Home · Azure/azure-webjobs-sdk Wiki](https://github.com/Azure/azure-webjobs-sdk/wiki)