Visual Studio 2015 上でAzure Functions を作成してGitHubからDeployをできるようにならないか

久しぶりにQiitaへの投稿で、新しい機能が増えてたりと半分遊びながら書いてます。
スライドモードが面白そう・・・。
というわけで、Visual Studio 2015 上でAzure Functions を作成して
GitHubからDeployをいい感じにできないかと模索してみた。

---
# 11月Azure Functions GA
このGAで、プランが大きく変わったなぁというの一番思っていたところですが、それまでのメモリ予約して、その秒数での課金となっていた部分が、Azure側で自動計測するものに変わったため、実装する側としてみればメモリサイズが予測しにくいものを作りやすくなったと思う。
たとえるならサイズのあるリソースとして画像を例にしたとき、内部にメモリストリームを展開したい場合などは、その画像のサイズに制限を加えるか最大値を見るしかなかったんじゃないかなと。
　ただ、GAしたといっても、まだドキュメントが古いものままだったりするので、情報の更新日時を確認して混乱しないようにしたほうがよいかもしれない。
　また、特に根拠があるわけではなく体感として、起動速度が上がっているなぁと感じる。
過去サイズ制限のあった時代に128MBで動かしていたものが、10秒くらいのレスポンスだったものが、最近動かしたところ5秒くらいで返ってくるという感じだった。

---
# Azure Functions の開発

テンプレートによるデプロイの前に、そもそもどうやって作るのか？

### Azure ポータル上
ポータル上で、テンプレートを作成して、それを編集することができるということで、何やら、スクリプトファイルを編集しているような気分で実装していくことができる。
がしかし、これはVisualStudioなどの統合開発環境や高度なエディタに慣れた人にとっては、一字一句間違わないように入力していくというのは、なかなか大変なところ。

### お試し環境
[https://functions.azure.com/try](https://functions.azure.com/try)
上記のリンクにアクセスすると1時間試せるもの。
使い方はポータル上のものと同じ。


### ローカル開発環境 azure-functions-cli
[azure-functions-cli](https://www.npmjs.com/package/azure-functions-cli)
現在ローカル向けの開発環境として、npmからインストール可能なツールセットがある。
これは現在、Windows向けであり、MacやLinux上ではローカルな環境ととして動かすということはできないらしい（試してはいない）。
Windows環境では、Visual Studio Codeを使うことで、デバッグもできた。

### ローカル開発環境 Visual Studio 2015
[Visual Studio Tools for Azure Functions](https://blogs.msdn.microsoft.com/webdev/2016/12/01/visual-studio-tools-for-azure-functions/)
12月には Visual Studio 2015向けの拡張機能が、プレビューとして公開。
ローカル実行するものは、npm経由で入れられる azure-functions-cli がインストールされる。
インテリセンスなどのエディタ機能は、Visual Studio に入っているもの拡張機能のものが使われるようだ。

今回は、Visual Studio 2015 で作ったものを前提として進めていく。


---
# ソースは・・・？
ポータル上で編集した場合、ポータルにすべてのものがあるのでどうしたもこうしたもないが、チームで作ったりローカル開発環境で作ったりしたとき、どうやってデプロイするか。

### 継続的なデプロイ
[Azure Functions の継続的なデプロイ](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-continuous-deployment)
フォルダーの構成があるようで、そこに必要なファイルが書いてあった。
それらをリポジトリに用意しておければよいというところが、
このあたりで、デプロイするには、秘密キーなど非公開にしたい情報というのもあったりする。
これらを通常のリポジトリに含めてしまうと公開は難しい。
また、実際に公開してで動くものと試験的に動かすものでも、同じリソースを使うというのは現実的ではない。
特にFunctionsの制御で使われるストレージアカウントは必須として、function.jsonで定義されたトリガーやインプット、アウトプットを含めてそれぞれの環境用にあるほうがいいだろう。

### ローカル Git
ということでいろいろあるのですが、特殊なのはローカルGitリポジトリ。
これはAppService自体がリポジトリになってくれるので、ここにコミットすることで自動的にデプロイされるという仕組み。
[Azure App Service へのローカル Git デプロイ](https://docs.microsoft.com/ja-jp/azure/app-service-web/app-service-deploy-local-git)
リポジトリ一つに対して、サービス一つなので上記のようなにキー情報をどうするかとか考えなくてもよさそう。
ただし、テンプレート化したい部分もあるので、使わないことにした。

 
---
# ソースを公開リポジトリGitHubへ
何かということで、先日のイベントで利用したDemoアプリケーションを公開することにした。
[Serverless meetup sapporo demo application (SourceCodeOnly) ](https://github.com/darkcrash/FunctionApp201612111/tree/SourceCodeOnly)
公開するに当たってアカウントに紐づいている以下のもの削除した。

* Azure Function が利用するストレージアカウントの接続文字列をappsettings.jsonから削除する
* APIのキー情報などもappsettings.jsonから削除する

当然このままでは全く動かなくなってしまうので
ストレージアカウントの接続文字列は、ローカルで動く開発用ストレージの「UseDevelopmentStorage=true」とした
ただし、APIキーはどうしてもアカウントと紐づいてしまうので、排除したままとした。

またアプリケーションは、FunctionsとAppServiceの２つ組み合わせている。
結果リポジトリに複数のアプリケーションが存在していることになる。これが後で面倒なことになる。

# テンプレート化
[Provision a function app on a dynamic hosting plan](https://github.com/Azure/azure-quickstart-templates/tree/master/101-function-app-create-dynamic)
このARMテンプレートをベースに、CognitiveServiceアカウントやWebアプリケーションを追加していくことにした。
![2016-12-18.png](https://qiita-image-store.s3.amazonaws.com/0/58908/d2871bdb-673d-dda8-ed4b-5d709efd5ec8.png)

* [CognitiveServices のスキーマ](https://github.com/Azure/azure-resource-manager-schemas/blob/master/schemas/2016-02-01-preview/Microsoft.CognitiveServices.json)
 * ※Freeのプランは一つしか作ることができないらしい。
* [Deploy a web app linked to a GitHub repository](https://github.com/Azure/azure-quickstart-templates/tree/master/201-web-app-github-deploy)
 * GitHubからAppServiceにデプロイする

作成済みのものをリソースエクスプローラー、Automationスクリプトから現在の状態として参照し、変更したい内容はスキーマの内容に従って設定していく。

# Azure Resource Manager Template

CognitiveServicesのキー情報をAppConfigに設定したいが・・・
うまいこと取得できなかったので、調べた方法

```ps1:powershell
Get-AzureRmProviderOperation -OperationSearchString *  | where {$_.Operation -like "*listKey*"} | FT Operation

Operation                                                                                
---------                                                                                
Microsoft.ServiceBus/namespaces/authorizationRules/listkeys/action                       
Microsoft.ServiceBus/namespaces/queues/authorizationRules/listkeys/action                
Microsoft.ServiceBus/namespaces/topics/authorizationRules/listkeys/action                
Microsoft.DocumentDB/databaseAccounts/listKeys/action                                    
Microsoft.Storage/storageAccounts/listkeys/action                                        
Microsoft.Relay/namespaces/authorizationRules/listkeys/action                            
Microsoft.Relay/namespaces/HybridConnections/authorizationRules/listkeys/action          
Microsoft.Relay/namespaces/WcfRelays/authorizationRules/listkeys/action                  
Microsoft.Cache/redis/listKeys/action                                                    
Microsoft.ClassicStorage/storageAccounts/listKeys/action                                 
Microsoft.NotificationHubs/Namespaces/authorizationRules/listkeys/action                 
Microsoft.NotificationHubs/Namespaces/NotificationHubs/authorizationRules/listkeys/action
Microsoft.Media/mediaservices/listKeys/action                                            
Microsoft.AppService/gateways/listKeys/Action                                            
Microsoft.AppService/apiapps/listKeys/Action                                             
Microsoft.EventHub/namespaces/authorizationRules/listkeys/action                         
Microsoft.EventHub/namespaces/eventhubs/authorizationRules/listkeys/action               
Microsoft.Batch/batchAccounts/listkeys/action                                            
Microsoft.CognitiveServices/accounts/listKeys/action   
```

Storageと同じようにlistkeys関数を使っていたのだが、何度やってもエラーとなっていた・・。
これは・・・・Kが大文字である必要があった。
Key1は以下のようにして取得した。

```json:azuredeploy.json
"[listKeys(resourceId('Microsoft.CognitiveServices/accounts', variables('cognitiveServicesAccountName')), providers('Microsoft.CognitiveServices', 'accounts').apiVersions[0]).key1]"

```

[Azure リソース マネージャーのテンプレートの関数 - リソース関数](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/resource-group-template-functions#a-nameresource-functionsaリソース関数)

試行錯誤の後・・・ある程度うまくいった・・・

```
19:25:45 - テンプレート 'azuredeploy.json' をリソース グループ 'FunctionApp' に正常に配置しました。
```

# 結果

![2016-12-18 (1).png](https://qiita-image-store.s3.amazonaws.com/0/58908/b5379514-9815-85e8-286c-71be96264145.png)

なるべくなら、すべてをGitHubから自動化したいところだったが、結局Azure Functionsに関してはうまく反映させることはできなかった。
Functionsは作成したファイルを配置するだけでよいのだが、プロジェクトのビルドが動いてしまうために、現在のGitHub上のソース管理では、うまくはいかなかった。
この解決方法として、リポジトリを何らかの方法で分割してしまうという選択肢があった。
ただ、本当に公開するサービスとして継続的なデプロイをするならば、GitHubへのコミットしたリソースを直接デプロイするよりも、自動ビルドなどを組み合わせてテスト実行などを経てから、Gatewayに隠れたアプリケーションとして公開するほうがいいだろうと思う。



# ブランチによる分割
とはいえ、このまま終わるのも寂しいので、あがいてみようと思う。
Gitにはブランチによる分割で同じリポジトリでも全く異なる領域を作ることができる。これを利用して、ソースコントロールとして接続するブランチを切り替えることで、Functionsだけをデプロイするように仕向けてみた。
ただし、これはVS上一つのソリューションで実行できていた環境を破壊することで実現することになるため、デプロイはできても開発環境を選んだ理由を捨てていることにはなる。デプロイされることだけを目的にしている。

# あがいた結果
成功！
![2016-12-18 (4).png](https://qiita-image-store.s3.amazonaws.com/0/58908/66e264c9-a64b-4375-2889-d911899e0763.png)

![2016-12-18 (2).png](https://qiita-image-store.s3.amazonaws.com/0/58908/4f435eba-77d7-7903-d1ea-9aa2be0b28e2.png)

途中、ApiVersionに挫折しそうになりながらも動く状態へ

* ソース
 * [Serverless meetup sapporo demo application (Master) ](https://github.com/darkcrash/FunctionApp201612111/)
 * [Serverless meetup sapporo demo application (functionOnly) ](https://github.com/darkcrash/FunctionApp201612111/tree/functionOnly)

* ボタン

[![deploybutton.png](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fgithub.com%2Fdarkcrash%2FFunctionApp201612111%2Fraw%2Fmaster%2FAzureResourceGroup20161211%2Fazuredeploy.json)

また、サブスクリプションに既存の無料のCognitiveService(ComputerVision)が存在する場合はこのようなエラーが出る
![2016-12-18 (3).png](https://qiita-image-store.s3.amazonaws.com/0/58908/db0cfc56-ba4d-ed01-607a-22e208bab282.png)



# おしまい
当初の目的は望んだ形では達成できずに、直前までバタバタしていたが、なんとか動いたのは良かった。
ApiVersionの違いが思わぬ変更点があったりするので、そのあたりは注意が必要。
特にFunctionsのテンプレート(azuredeploy.json)は、そのまま使うより一度ポータルから作成したものから流用したほうがよさそう。
ビルド・テストのプロセスを入れた状態であれば、ブランチによる分割はしなくても大丈夫なように作れるはず・・・
・・・・(-_-)zzz