Azure Functions Runtime

Build 2017
の中でひそかに更新されたBlogがあった。

Introducing Azure Functions Runtime preview

なんというか、Runtime?ってどういうことだろうと思って開いてみたところ。

Azure Functionをオンプレミスでも動かせるようにできるものらしい。

 

最低要件とか

細かいドキュメントは、「Azure Functions Runtime Overview」あたりから

Windows Server 2016 or Windows 10 Creators Update with access to a SQL Server instance.

Windows Server 2016 または、Windows 10 Creators Updateとなっている。
このあたり、Windows Containerに依存している部分があるためと考えられそうだ。また、SQL Serverへの接続も必要と。

機能

このRuntimeが持つ機能は大きく二つ

「Azure Functions Management Role」
ポータルのような管理する機能を提供する

「Azure Functions Worker Role」
FunctionをWindows Containerに展開して実行する

ドキュメントでは、VisualStudio 2017からの直接デプロイもできるとはなっている。

ダウンロードは、Blog記事より下部にある

How do I get started?

Please download the Azure Functions Runtime installer.

からできた。

ちょっとだけ

以下、Windows Server 2016 へのインストールと、インストール後の画面
SQL Serverへの接続を構成しない限り動作しないようだ。
インストール後は再起動が必要となった。

スライドショーには JavaScript が必要です。

Microsoft TranslatorのAPIを使うに当たって・・

Azureの認証が必要となるために、単純な呼び出しでは利用できない様子。

https://github.com/MicrosoftTranslator/GetAzureToken

ここのソースが参考になった。

Functionsで利用する場合は

https://github.com/MicrosoftTranslator/GetAzureToken/blob/master/AzureAuthToken.cs

Tokenの取得が必要となるため、上記を参考にするとよさそうだ。
リクエストがTokenの取得と、取得したTokenを渡すAPI呼び出しになっていることに注意が必要。

 

 

Visual Studio Tools for Azure Functions の修正版

Azure Functions 向けの拡張機能を入れたところで、.NET Coreのプロジェクトがだめになるっていうものに対する修正版が出てたようです。

https://blogs.msdn.microsoft.com/webdev/2016/12/01/visual-studio-tools-for-azure-functions/

Update 12-6-16 @5:00 PM: Updated version of the tools are available that fix the ability to open .NET Core projects with Azure Functions tools installed. Install the updated version over your old version to fix the issue, there is no need to uninstall the previous copy.

というわけで、入れてみたところ・・・

2016-12-10

インストール完了。
プロジェクトの新規作成で、ASP.NET Coreのプロジェクトテンプレートから作成したみところ、無事にできました。

2016-12-10-2

良かった良かったー

風邪っぽい・・・?VS2015の拡張機能

Microsoft Azure2 Advent Calendar 2016 の6日目の記事です。
.NET Core Advent Calendarの6日目の記事です。


2016-12-10追記
Visual Studio Tools for Azure Functions の修正版が出たようで、解消しました。

両方空いていたのと、両方を好きな人はこれから私と悲しい気持ちを味わう前に楽しくなってもらえればという願いを込めて書きました。
相変わらず、しょうもないネタなのですが・・・お時間あればお付き合いください。
Visual Studio 2015の話になります。

季節の変わり目で、咳が止まらず目が覚めて何気なく.NET Coreプロジェクトを作ろうとしたら・・・

2016-12-05-1

2016-12-05-2

あ、、、あれれ?コンソールだめなら、Webは・・・?

2016-12-05-3

2016-12-05-4

 

うーむ、VS2017 RCと共存してるからかなぁ・・・とか思いつつ、既存の.NET Coreアプリを開いてみたところ・・・

=====================
2016/12/04 5:47:00
Recoverable
System.Exception: ディスク上のプロジェクトファイルの検出中に次のエラーが発生しました。Could not load file or assembly 'Microsoft.VisualStudio.ProjectSystem.DotNet, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. 指定されたファイルが見つかりません。。
   at Microsoft.VisualStudio.ProjectSystem.DotNet.Common.FileMirroring.SourceItemsInMemoryProject.<InitializeFromDiskAsync>d__54.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.ProjectSystem.DotNet.Common.FileMirroring.SourceItemsInMemoryProject.<Creator>d__52.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.ProjectSystem.UnconfiguredProjectImpl.AutoLoadMethodStateMachine.<<StartExecution>b__6_0>d.MoveNext()
===================

どうやら風邪がうつったようです

いやいやそんなわけはなく・・・いろいろ調べてみたところ・・・
どうやら、Visual Studio Tools for Azure Funtionsの問題として認識されてるよというところまでは見つけました。

https://blogs.msdn.microsoft.com/webdev/2016/12/01/visual-studio-tools-for-azure-functions/

Warning: We have discovered that installing this Preview breaks the ability to create or open .NET Core projects in Visual Studio 2015.  We are currently investigating, and will make a fix available as soon as it is ready.  In the meantime, do not install this on any machines where you plan to work with .NET Core projects.

.NET Coreプロジェクトを扱うことができなくなってしまうということで、対策をがんばってるそうです。

そう・・・思えばFunctionsへの浮気心が、.NET Coreとの距離を近づけたのかもしれない・・・

そうか、浮気といえば・・・電話番号は二つ・・・つまり、マシンをもう一つAzureで作ればいいじゃない!
この発想には賛否両論あると思いますが…

というわけで、デプロイ!!
で、さりげなく機能紹介。

2016-12-05-5

というわけで、デプロイできた仮想マシンのブレードを開くと、
Automation スクリプトというのがあるのでそこを選択すると出てくるものです。
これは、ご存知の方多いとは思いますが、ARMというテンプレートで、この仮想マシンと同じものを再現するために使える逆起しの設計書みたいな状態です。
ただ、エラーが出てる通り、この仮想マシンに設定した自動シャットダウンと、仮想マシン拡張の診断機能は含まれていません。なので100%ではないですが、このJSONを1から作るよりも、一度作ってみたものをもとにJSON取得して、修正。
ARMとしてテンプレートデプロイをするというシナリオが近道のような気がします。

 

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "virtualMachines_vm20161206_adminPassword": {
            "defaultValue": null,
            "type": "SecureString"
        },
        "virtualMachines_vm20161206_name": {
            "defaultValue": "vm20161206",
            "type": "String"
        },
        "networkInterfaces_vm20161206128_name": {
            "defaultValue": "vm20161206128",
            "type": "String"
        },
        "networkSecurityGroups_vm20161206_nsg_name": {
            "defaultValue": "vm20161206-nsg",
            "type": "String"
        },
        "publicIPAddresses_vm20161206_ip_name": {
            "defaultValue": "vm20161206-ip",
            "type": "String"
        },
        "virtualNetworks_vm20161206_vnet_name": {
            "defaultValue": "vm20161206-vnet",
            "type": "String"
        },
        "storageAccounts_vm20161206diag122_name": {
            "defaultValue": "vm20161206diag122",
            "type": "String"
        },
        "storageAccounts_vm20161206disks246_name": {
            "defaultValue": "vm20161206disks246",
            "type": "String"
        }
    },
    "variables": {},
    "resources": [
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Compute/virtualMachines/vm20161206' から一般化されました。",
            "type": "Microsoft.Compute/virtualMachines",
            "name": "[parameters('virtualMachines_vm20161206_name')]",
            "apiVersion": "2015-06-15",
            "location": "japaneast",
            "properties": {
                "hardwareProfile": {
                    "vmSize": "Standard_F8s"
                },
                "storageProfile": {
                    "imageReference": {
                        "publisher": "MicrosoftVisualStudio",
                        "offer": "VisualStudio",
                        "sku": "VS-2015-Ent-VSU3-AzureSDK-29-Win10-N",
                        "version": "latest"
                    },
                    "osDisk": {
                        "name": "[parameters('virtualMachines_vm20161206_name')]",
                        "createOption": "FromImage",
                        "vhd": {
                            "uri": "[concat('https', '://', parameters('storageAccounts_vm20161206disks246_name'), '.blob.core.windows.net', concat('/vhds/', parameters('virtualMachines_vm20161206_name'),'20161204060340.vhd'))]"
                        },
                        "caching": "ReadWrite"
                    },
                    "dataDisks": []
                },
                "osProfile": {
                    "computerName": "[parameters('virtualMachines_vm20161206_name')]",
                    "adminUsername": "watashi.kanrisha",
                    "windowsConfiguration": {
                        "provisionVMAgent": true,
                        "enableAutomaticUpdates": true
                    },
                    "secrets": [],
                    "adminPassword": "[parameters('virtualMachines_vm20161206_adminPassword')]"
                },
                "networkProfile": {
                    "networkInterfaces": [
                        {
                            "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaces_vm20161206128_name'))]"
                        }
                    ]
                }
            },
            "resources": [],
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccounts_vm20161206disks246_name'))]",
                "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaces_vm20161206128_name'))]"
            ]
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/networkInterfaces/vm20161206128' から一般化されました。",
            "type": "Microsoft.Network/networkInterfaces",
            "name": "[parameters('networkInterfaces_vm20161206128_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "ipConfigurations": [
                    {
                        "name": "ipconfig1",
                        "properties": {
                            "privateIPAddress": "10.0.0.4",
                            "privateIPAllocationMethod": "Dynamic",
                            "publicIPAddress": {
                                "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_vm20161206_ip_name'))]"
                            },
                            "subnet": {
                                "id": "[concat(resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworks_vm20161206_vnet_name')), '/subnets/default')]"
                            }
                        }
                    }
                ],
                "dnsSettings": {
                    "dnsServers": []
                },
                "enableIPForwarding": false,
                "networkSecurityGroup": {
                    "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroups_vm20161206_nsg_name'))]"
                }
            },
            "resources": [],
            "dependsOn": [
                "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_vm20161206_ip_name'))]",
                "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworks_vm20161206_vnet_name'))]",
                "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroups_vm20161206_nsg_name'))]"
            ]
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/networkSecurityGroups/vm20161206-nsg' から一般化されました。",
            "type": "Microsoft.Network/networkSecurityGroups",
            "name": "[parameters('networkSecurityGroups_vm20161206_nsg_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "securityRules": [
                    {
                        "name": "default-allow-rdp",
                        "properties": {
                            "protocol": "TCP",
                            "sourcePortRange": "*",
                            "destinationPortRange": "3389",
                            "sourceAddressPrefix": "*",
                            "destinationAddressPrefix": "*",
                            "access": "Allow",
                            "priority": 1000,
                            "direction": "Inbound"
                        }
                    }
                ]
            },
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/publicIPAddresses/vm20161206-ip' から一般化されました。",
            "type": "Microsoft.Network/publicIPAddresses",
            "name": "[parameters('publicIPAddresses_vm20161206_ip_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "publicIPAllocationMethod": "Dynamic",
                "idleTimeoutInMinutes": 4
            },
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/virtualNetworks/vm20161206-vnet' から一般化されました。",
            "type": "Microsoft.Network/virtualNetworks",
            "name": "[parameters('virtualNetworks_vm20161206_vnet_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "10.0.0.0/24"
                    ]
                },
                "subnets": [
                    {
                        "name": "default",
                        "properties": {
                            "addressPrefix": "10.0.0.0/24"
                        }
                    }
                ]
            },
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Storage/storageAccounts/vm20161206diag122' から一般化されました。",
            "type": "Microsoft.Storage/storageAccounts",
            "sku": {
                "name": "Standard_LRS",
                "tier": "Standard"
            },
            "kind": "Storage",
            "name": "[parameters('storageAccounts_vm20161206diag122_name')]",
            "apiVersion": "2016-01-01",
            "location": "japaneast",
            "tags": {},
            "properties": {},
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Storage/storageAccounts/vm20161206disks246' から一般化されました。",
            "type": "Microsoft.Storage/storageAccounts",
            "sku": {
                "name": "Premium_LRS",
                "tier": "Premium"
            },
            "kind": "Storage",
            "name": "[parameters('storageAccounts_vm20161206disks246_name')]",
            "apiVersion": "2016-01-01",
            "location": "japaneast",
            "tags": {},
            "properties": {},
            "resources": [],
            "dependsOn": []
        }
    ]
}

このテンプレートを見て、何をデプロイしたか分かった人はすごい!

で、話は戻りまして・・・こうしてどちらかの環境用のマシンを分けておくと、何かと便利なので、あまり使ってないサブスクリプションを持ってる人とかはどうでしょうか?

まとめ

こんな状態でもツールをリリースして、たたかれる覚悟でも一歩でも進もうとしていることに私はすごいことなんじゃないかと思っています。

enjoy azure !
enjoy .NET Core !

Visual Studio 2015 のAzure FunctionsでStartしたときのUAC

Microsoft Azure2 Advent Calendar 2016 の3日目の隙間に向けて・・・隙間なネタを・・・!

開始するたびにダイアログがぽこぽこ上がってくるこれ・・・

UACを切ってしまえば・・・でもいいのだけど。それはそれで不安があるしとか思いつつ、もう一ついけそうだと思ってたのが、Visual Studioを管理者モードで起動する

2016-12-04-1

こうすると、親プロセスであるVisual Studioが管理者状態で動いていているので、そこから開始されるFunctionsのローカル実行のプロセスも自動的に管理者権限で動いてくれるということで、UACのダイアログは出てこなくなる。

ただ、Visual Studioに特権が与えられるので、それはそれで不安になることもあるかもしれない。

クリスマス近いんだねー

(メモ)Visual Studio 2015 の Azure Functions プロジェクトに 参照サービスを追加する

Functions単体では、ただコードを書いて、日付を返すとかはできますが、それだけでは、日付を返すとか文字列を変換するとかのことはできても、それは普通にローカルで動くプログラムでも得られるわけで、あまり意味がありません。
Functionsのプロジェクトを他のサービスに接続してみます。

 

プロジェクトのコンテキストメニュー(失敗)

ソリューションエクスプローラー>Functionsプロジェクト>コンテキストメニュー>追加>Connected Service

2016-12-03-1

追加用の画面が表示される
2016-12-03

Azure Storageを選ぶ
2016-12-03-2

既存を選ぶか、Create a New Storage Accountで新規に追加

2016-12-03-3

  1. Azure サブスクリプション を関連付けられているアカウントを選択
  2. サブスクリプションを選択
  3. Storage Account のURLになるものを入力
  4. お好みで料金体系・サービス体系を選択
  5. リソースグループを選択(ここで新規で作ることもできる)
  6. Locationを選択

Azure Storageの画面でAddとしてみると・・・

2016-12-03-4

NuGetのパッケージを追加できずにエラー。
というわけで、この方法ではだめな様子。
ひとまずフィードバックを送信。

functions の場合、プロジェクトの参照というよりは、bindingの定義があってという感じなのもあるかもしれないのでひとまずこの方法ではなさそう。

Functionsの追加から

試しにFunctionの追加から、
テンプレートは、FaceLocatorという、Blobストレージにある画像から顔がある四角形領域を返してくれるもの。

2016-12-03-5

ストレージアカウントの接続文字列を入力する欄が赤くなってますので、ポータルから引っ張ってくる。

2016-12-03-6

この接続文字列は絶対に公開しないように・・・
このように公開してしまった場合は、アカウントを消すか、接続文字列を再生成する必要が出てくる。その場合既存の接続文字列は使えなくなるので、いろいろ面倒。

追加した「FaceLocatorCSharp」の
function.json

{
  "bindings": [
    {
      "type": "blobTrigger",
      "name": "image",
      "path": "images/{name}.jpg",
      "connection": "DefaultEndpointsProtocol=https;AccountName=functionapp;AccountKey=xxxxxx;",
      "direction": "in"
    },
    {
      "type": "table",
      "name": "outTable",
      "tableName": "faceRectangle",
      "connection": "DefaultEndpointsProtocol=https;AccountName=functionapp;AccountKey=xxxxxx;",
      "direction": "out"
    }
  ],
  "disabled": false
}

このファイルにあるbindingsが、作ったfunctionsの入出力の定義そのものになっている。nameにある文字列がfunctionsのメソッドにある引数として以下のように

public static async Task Run(Stream image, string name, IAsyncCollector<FaceRectangle> outTable, TraceWriter log)
{
    ....
}

しかし・・・

F5デバッグ開始をしてみるもののエラーが発生してしまう。
かなり手詰まりではあるが、実際に動いているものがあるということはそこから得られるヒントがある・・・と信じて

ポータルから・・・

ポータルから生成したもののソースコードを見てみると

  {
  "bindings": [
    {
      "type": "blobTrigger",
      "name": "image",
      "path": "images/{name}.jpg",
      "connection": "functionappXXXX",
      "direction": "in"
    },
    {
      "type": "table",
      "name": "outTable",
      "tableName": "faceRectangle",
      "connection": "functionappXXXX",
      "direction": "out"
    }
  ],
  "disabled": false
}

といった形で、appSettings.jsonに設定されたものを指定しているようだ。
なので置き換えた。

それでもエラーはまだ残るため次に、テンプレート作成時には空となっていた
AzureWebJobsStorageおよびAzureWebJobsDashboardにも接続文字列を追加した

これは、ポータルで生成した場合は自動的に追加されているものだった。

FaceLocatorのキー

さらにわかったことがあって、テンプレート「FaceLocatorCSharp」では、VisionAPIを使うことになっているが、このための接続情報をappSettingsに追加してやる必要があった。

それがわかるコードはここで

        client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", Environment.GetEnvironmentVariable("Vision_API_Subscription_Key"));

「Vision_API_Subscription_Key」という設定値を必要としていたので、appSettings.jsonに追加した。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=functionapp;AccountKey=xxxx",
    "AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=functionapp;AccountKey=xxxx",
    "Vision_API_Subscription_Key": "xxxxxxxxxxxxxxxxx",
    "functionapp": "DefaultEndpointsProtocol=https;AccountName=functionapp;AccountKey=xxxx"
  }
}

もちろんこれは使えるものではなく、
https://www.microsoft.com/cognitive-services/en-us/subscriptions
より取得する必要がある

 

 

Visual Studio の拡張機能として

まだ、このFunctionsの拡張機能はPreviewであり今後変わる可能性があると思うが、
現状動くポータルを正解としていけば動かす道は掴んでいけそうであることがわかった。

.funprojを覗いてみて・・・メモ

Visual Studioで読み込まれるプロジェクトファイルはXML形式で、ビルドするために何らかのtargetsファイルを読み込んでいたり、プロジェクトに設定されたファイルを記述していたりするので、のぞいてみた。

今後変わる可能性は十分にあるので、完全に自己満足or趣味

なんと!
ファイルはどれも関連づいていない。
プロジェクトのディレクトリにあるものはすべて一部となる様子。

ビルドの定義は、Micorosoft.AzureFunctions.targetsファイルにあるようで。
Debug|Any CPU
Release|Any CPU

内部の初期値では、.NET Framework 「v4.5.1」ただ、ビルドのログを見てる限りコンパイラが動いてなさそうなので、ダミーに近い。

MSBuildとしてのメインは、Publishなのかなぁという感じがした。

Publish

その中で
\bin
\obj
*.funproj
*.pubxml
*.user
に該当するファイル、フォルダーは対象外としている様子

デプロイするためのリソースは一時的
obj\PublishTemp\
に格納される
現状これは、パターンに該当しないファイル以外をコピーしている。

 

ソース管理

pubxmlファイルはデプロイ先の情報を持っているため、GitHubなどの公開されるソース管理では対象外としたほうがよさそうだ。

Visual Studio 用の Azure Functions拡張機能(Preview)

昨日、まだ出ないだろうと思ってとりあえずな記事書いたところで、
正式なFunctionsの拡張機能が出たようです。

https://blogs.msdn.microsoft.com/webdev/2016/12/01/visual-studio-tools-for-azure-functions/

 

注意点として

  • プレビューであること
  • Visual Studio 2015 Update 3用であること
  • Azure 2.9.6 .NET SDKがインストールされていること

プロジェクトテンプレート

FunctionApp1という名前で作ってみたところ、以下のようなファイルが

Properties
appsettings.json
FunctionApp1.funproj
host.json
Project_Readme.html

これだけだと、ファンクションそのものがないからの状態なんですが、
デバッグ開始(F5)してみると
Azure-Functions-Cliをダウンロードしてくるぞ?と聞かれました。
いいよとすると、コマンドプロンプトのように、ローカルホスト用であるfunc.exeが起動

Listening on http://localhost:7071/
Hit CTRL-C to exit...
Reading host configuration file 'c:\Projects\FunctionApp1\FunctionApp1\host.json'
Generating 0 job function(s)
Starting Host (HostId=sb-v2-functionapp1, Version=1.0.0.0, ProcessId=9404, Debug=False)
No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. config.UseServiceBus(), config.UseTimers(), etc.).
Job host started
File change of type 'Created' detected for 'c:\Projects\FunctionApp1\FunctionApp1\data'
Host configuration has changed. Signaling restart.
Stopping Host
Job host stopped
Reading host configuration file 'c:\Projects\FunctionApp1\FunctionApp1\host.json'
Generating 0 job function(s)
Starting Host (HostId=sb-v2-functionapp1, Version=1.0.0.0, ProcessId=9404, Debug=True)
No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. config.UseServiceBus(), config.UseTimers(), etc.).
Job host started
Debugger listening on [::]:5858

特に何もファンクションがないので、Jobは0となってます。

Functionsを追加

Blogに書かれているように
ソリューションエクスプローラー>プロジェクト>コンテキストメニュー>追加>New Azure Function

とすると、テンプレートがずらずらーっと並んでるので追加。
再びデバッグ開始で、

Listening on http://localhost:7071/
Hit CTRL-C to exit...
Reading host configuration file 'c:\Projects\FunctionApp1\FunctionApp1\host.json'
Generating 1 job function(s)
Starting Host (HostId=sb-v2-functionapp1, Version=1.0.0.0, ProcessId=7960, Debug=True)
Found the following functions:
Host.Functions.HttpTriggerCSharp

Job host started
Http Function HttpTriggerCSharp: http://localhost:7071/api/HttpTriggerCSharp

というわけで、ブレークポイントを設定してブラウザから
http://localhost:7071/api/HttpTriggerCSharp
にアクセスしてみると・・・

2016-12-02-1

おおー、変数展開、呼び出し履歴、タスク、診断ツール
と普通にいけてるじゃないですか!!
ローカル環境での実行なので、AzureFunctionsをデプロイしてなくてもいいし。
極端な話、ネットワークにつながってなくても書ける・・・かもしれない?

デプロイ

Blogに書かれているように
ソリューションエクスプローラー>プロジェクト>公開

AppServiceとしてを選ぶと、既存のFunctionsAppのみアイコン付きで出てきました。

2016-12-02-2

そのまま、AppServiceの感覚でデプロイ。

ここからは、せっかく書いた昨日の記事が生きてくるかもしれません。

 

 

Azure Functions ローカル Git リポジトリ

Azure Functionsのソースを、ローカルGit管理にしてしまう方法

少し試してポータルで編集している分には、それはそれでいいのですが
慣れた馴染んだエディタで編集したいとき、ローカルマシン上に持っていきたいというときにいいのかな?ということで。

1.ポータルから ローカル Git リポジトリ 設定

ポータルから対象のFunction Appを開き、
Function App の設定>継続的インテグレーションの構成

2016-11-26-4

 

デプロイのブレードが表示されるのでセットアップ

2016-11-26-5

 

ローカル Git リポジトリを選択

2016-11-26-6

ユーザー名パスワードを求められたら、
入力して忘れないようにしておきます。
(2回目以降入力が省略された!

2.ローカルマシンでclone

gitが入っている前提で

[FunctionAppName]には、作成時に入力したFunctionAppの名前を入れます

>git clone https://[FunctionAppName].scm.azurewebsites.net/

2016-11-26-7

忘れないようにしていたユーザー名とパスワードを入力

Cloning into '[FunctionAppName].scm.azurewebsites.net'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Checking connectivity... done.

 

[FunctionAppName].scm.azurewebsites.net
というディレクトリが作成されていたらできあがり!

vscodeで開いた状態
ちゃんとコミットできる状態になってた

2016-11-26-8

注意点

ローカルGitリポジトリを設定すると、ポータルから編集できなくなります。

その場合、新しい関数を追加したいとき地道にファイルを追加するか。
一度ローカルGitリポジトリを無効にするか。
Azure-Functions-Cliを使う必要がありそうです。

※ローカルGitの切断・セットアップを繰り返しているうちに何かおかしくなった
※Visual Studio拡張機能が待ち遠しい

 

azure-functions-cli installと実行を試してみた

azure-functions-cliとはなんぞや?となるかもしれませんが、
Azure FunctionsがGAされたアナウンスの中でツールについての話がありました。

announcing-general-availability-of-azure-functions

We now have support for creating, running, and debugging Functions locally on Windows, with the beta Azure Functions CLI. For JavaScript Functions on NodeJS, the CLI integrates with Visual Studio Code and sets up debug targets automatically. While the CLI currently only works on Windows, we’re working on support on Mac and Linux.

ということで、NodeJSで動くものですよと。
ひとまずWindowsは動くからと。
私自身NodeJSをほとんど扱ったことがありませんので、何か変なところがあれば教えてください。
というわけで、まずは入れてみました。

1.NodeJSのインストール

nodejs.org からダウンロード。ひとまずv6.9.1 LTSを入れた。
特に何かあるわけでもなく。

2.npmでインストール

npm i -g azure-functions-cli

しばらくまって、完了。

 

3.CLI funcコマンド

まずは、公式に通り「func」「azfun」「azure-function」
のうち短いのでfunc

>func
Azure Functions Cli (1.0.0-beta.5)
Function Runtime Version: 1.0.0.0
Usage: func [context] [context] <action> [-/--options]

Contexts:
azure        For Azure login and working with Function Apps on Azure
function     For local function settings and actions
functionapp  For local function app settings and actions
host         For local Functions host settings and actions
settings     For local settings for your Functions host

Actions:
init    Create a new Function App in the current folder. Initializes git repo. Aliases: init, create
run     Run a function directly

1.0.0-beta.5というバージョン情報、それからこの先に渡すコンテキストの情報が出てきました。

4.作る

最初にフォルダーを作り、カレントディレクトリにして

>func init
Writing .gitignore
Writing host.json
Writing appsettings.json
Initialized empty Git repository in C:/******/Source/samples/.git/

という感じで、gitリポジトリとして初期化され、
host.jsonとappsettings.jsonができてます。

次にfunctionを作ります。

>func function create
     _-----_
    |       |    ╭──────────────────────────╮
    |--(o)--|    │   Welcome to the Azure   │
   `---------´   │   Functions generator!   │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? Select an option...
  1) List all templates
  2) List templates by language
  3) List templates by type
  Answer:

という形でyeomanが起動しました。

1)ですべてのテンプレート
2)で開発言語別のテンプレート
3)で種別のテンプレート
のようです。

2を選ぶと

? Select an option... List templates by language
There are 8 languages available
? Select a language...
  1) Batch
  2) C#
  3) F#
  4) JavaScript
  5) Python
  6) Php
  7) PowerShell
  8) Bash
  Answer:

こんな感じに、4) JavaScriptを選びました。

? Select from one of the available templates...
  FaceLocator-JavaScript
  GenericWebHook-JavaScript
  GitHubCommenter-JavaScript
> GitHubWebHook-JavaScript
  HttpGET(CRUD)-JavaScript
  HttpPOST(CRUD)-JavaScript
  HttpTrigger-JavaScript
(Move up and down to reveal more choices)

ひとまず GitHubWebHookを選ぶ

? Enter a name for your function... (MyAzureFunction)

名前を聞かれるので、とりあえず「GitHubWebHookSample」と入れた
そうすると「GitHubWebHookSample」というディレクトリができてました。
中はこんな感じ

function.json
index.js
sample.dat

5.動かす

>func host start

とすると、権限確認のダイアログが出て続けると以下のような状態に

Listening on http://localhost:7071/
Hit CTRL-C to exit...
Reading host configuration file '***\Source\samples\host.json'
Generating 1 job function(s)
Starting Host (HostId=44d3feb53b1b49439bf13fdc57c64832, Version=1.0.0.0, ProcessId=29564, Debug=True)
Found the following functions:
Host.Functions.GitHubWebHookSample

Job host started
Http Function GitHubWebHookSample: http://localhost:7071/api/GitHubWebHookSample
File change of type 'Changed' detected for '***\Source\samples\data\functions\sampledata'
Host configuration has changed. Signaling restart.
Stopping Host
Job host stopped
Reading host configuration file '***\Source\samples\host.json'
Generating 1 job function(s)
Starting Host (HostId=44d3feb53b1b49439bf13fdc57c64832, Version=1.0.0.0, ProcessId=29564, Debug=True)
Found the following functions:
Host.Functions.GitHubWebHookSample

Job host started
Debugger listening on [::]:5858

一度ホストが上がって、ファイルの変更を検出して再び動いているようです。
この後、いろいろいじってみるとそのたびに起動しなおしたりしてました。

vscodeを起動して、デバッグを構成

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Attach to Process",
            "type": "node",
            "request": "attach",
            "port": 5858
        }
    ]
}

デバッグにアタッチで成功。
ブレークポイントを設定して、
「http://localhost:7071/api/GitHubWebHookSample」にアクセスしてみます

すると・・・

2016-11-26-2

 

 

無事に動きましたとさー
ほっ・・・