コマンド ライン ツールを使用して Azure Functions を Azure Storage に接続する

この記事では、前のクイックスタート記事で作成したストレージ アカウントと関数に Azure Storage キューを統合します。 この統合は、HTTP 要求からキュー内のメッセージにデータを書き込む "出力バインディング" を使用して実現します。 この記事を完了しても、前のクイックスタートの数セントを超えるコストが追加で発生することはありません。 バインドの詳細については、「Azure Functions でのトリガーとバインドの概念」を参照してください。

ローカル環境を構成する

開始する前に、「クイックスタート:コマンド ラインから Azure Functions プロジェクトを作成する」という記事を終える必要があります。 その記事の最後でリソースをクリーンアップした場合は、もう一度手順に従って Azure で関数アプリと関連リソースを再作成してください。

開始する前に、「クイックスタート:コマンド ラインから Azure Functions プロジェクトを作成する」という記事を終える必要があります。 その記事の最後でリソースをクリーンアップした場合は、もう一度手順に従って Azure で関数アプリと関連リソースを再作成してください。

開始する前に、「クイックスタート:コマンド ラインから Azure Functions プロジェクトを作成する」という記事を終える必要があります。 その記事の最後でリソースをクリーンアップした場合は、もう一度手順に従って Azure で関数アプリと関連リソースを再作成してください。

開始する前に、「クイックスタート:コマンド ラインから Azure Functions プロジェクトを作成する」という記事を終える必要があります。 その記事の最後でリソースをクリーンアップした場合は、もう一度手順に従って Azure で関数アプリと関連リソースを再作成してください。

開始する前に、「クイックスタート:コマンド ラインから Azure Functions プロジェクトを作成する」という記事を終える必要があります。 その記事の最後でリソースをクリーンアップした場合は、もう一度手順に従って Azure で関数アプリと関連リソースを再作成してください。

開始する前に、「クイックスタート:コマンド ラインから Azure Functions プロジェクトを作成する」という記事を終える必要があります。 その記事の最後でリソースをクリーンアップした場合は、もう一度手順に従って Azure で関数アプリと関連リソースを再作成してください。

Azure Storage の接続文字列を取得する

先ほど、関数アプリで使用する Azure ストレージ アカウントを作成しました。 このアカウントの接続文字列は、Azure のアプリ設定に安全に格納されています。 設定を local.settings.json ファイルにダウンロードすることにより、関数をローカルで実行するときに接続を使用して同じアカウントのストレージ キューへの書き込みを行うことができます。

  1. プロジェクトのルートから、次のコマンドを実行します。<APP_NAME> は、前の手順の関数アプリ名に置き換えてください。 このコマンドを実行すると、ファイル内の既存の値はすべて上書きされます。

    func azure functionapp fetch-app-settings <APP_NAME>
    
  2. local.settings.json ファイルを開き、AzureWebJobsStorage という名前の値を見つけます。それがストレージ アカウントの接続文字列です。 AzureWebJobsStorage という名前と接続文字列は、この記事の他のセクションで使用します。

重要

local.settings.json ファイルには、Azure からダウンロードされたシークレットが含まれているため、このファイルは必ずソース管理から除外してください。 ローカル関数プロジェクトで作成される .gitignore ファイルからは、このファイルが既定で除外されます。

バインディング拡張機能を登録する

HTTP トリガーとタイマー トリガーを除き、バインドは拡張機能パッケージとして実装されます。 ターミナル ウィンドウで次の dotnet add package コマンドを実行して、Storage 拡張機能パッケージをプロジェクトに追加します。

dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues --prerelease

これで、Storage の出力バインドをプロジェクトに追加できるようになります。

出力バインディングの定義を関数に追加する

関数に割り当てることができるトリガーは 1 つだけですが、入力と出力のバインディングは複数割り当てることができます。これらを使用すると、カスタム統合コードを記述しなくても、他の Azure サービスやリソースに接続できます。

Node.js v4 プログラミング モデルを使用する場合、バインド属性は ./src/functions/HttpExample.js ファイルで直接定義されます。 前のクイックスタートのファイルには、app.http メソッドによって定義された HTTP バインドが既に含まれています。

const { app } = require('@azure/functions');

app.http('httpTrigger', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: async (request, context) => {
    try {
      context.log(`Http function processed request for url "${request.url}"`);

      const name = request.query.get('name') || (await request.text());
      context.log(`Name: ${name}`);

      if (!name) {
        return { status: 404, body: 'Not Found' };
      }

      return { body: `Hello, ${name}!` };
    } catch (error) {
      context.log(`Error: ${error}`);
      return { status: 500, body: 'Internal Server Error' };
    }
  },
});

Node.js v4 プログラミング モデルを使用する場合、バインド属性は ./src/functions/HttpExample.js ファイルで直接定義されます。 前のクイックスタートのファイルには、app.http メソッドによって定義された HTTP バインドが既に含まれています。

import {
  app,
  HttpRequest,
  HttpResponseInit,
  InvocationContext,
} from '@azure/functions';

export async function httpTrigger1(
  request: HttpRequest,
  context: InvocationContext,
): Promise<HttpResponseInit> {
  context.log(`Http function processed request for url "${request.url}"`);

  const name = request.query.get('name') || (await request.text()) || 'world';

  return { body: `Hello, ${name}!` };
}

app.http('httpTrigger1', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: httpTrigger1,
});

関数フォルダーの function.json ファイルでそれらのバインディングを宣言します。 前のクイックスタートの HttpExample フォルダーにある function.json ファイルでは、bindings コレクション内に 2 つのバインディングが含まれています。

Python v2 プログラミング モデルを使用する場合、バインド属性はデコレーターとして function_app.py ファイル内で直接定義されます。 function_app.py ファイルには、前のクイック スタートからデコレーター ベースのバインドが既に 1 つ含まれています。

import azure.functions as func
import logging

app = func.FunctionApp()

@app.function_name(name="HttpTrigger1")
@app.route(route="hello", auth_level=func.AuthLevel.ANONYMOUS)

route デコレーターによって、HttpTrigger と HttpOutput バインドが関数に追加され、http 要求が指定されたルートに達したときに関数をトリガーできます。

この関数から Azure Storage キューに書き込むには、queue_output デコレーターを関数コードに追加します。

@app.queue_output(arg_name="msg", queue_name="outqueue", connection="AzureWebJobsStorage")

デコレーターでは、arg_name は、コードで参照されているバインド パラメーターを示しています。queue_name はバインドからの書き込み先キューの名前で、connection はストレージ アカウントの接続文字列を含むアプリケーション設定の名前です。 クイック スタートでは、(local.settings.json ファイル内の) AzureWebJobsStorage 設定にある関数アプリと同じストレージ アカウントを使用します。 queue_name が存在しない場合は、バインドによって最初に使用されるときに作成されます。

"bindings": [
  {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "Request",
    "methods": [
      "get",
      "post"
    ]
  },
  {
    "type": "http",
    "direction": "out",
    "name": "Response"
  }
]

Azure Storage キューに書き込むには:

  • バインド構成に extraOutputs プロパティを追加する

    {
        methods: ['GET', 'POST'],
        extraOutputs: [sendToQueue], // add output binding to HTTP trigger
        authLevel: 'anonymous',
        handler: () => {}
    }
    
  • app.http 呼び出しの上に output.storageQueue 関数を追加する

    const sendToQueue: StorageQueueOutput = output.storageQueue({
      queueName: 'outqueue',
      connection: 'AzureWebJobsStorage',
    });
    

コレクションの 2 つ目のバインディングの名前は res です。 この http バインディングは、HTTP 応答の書き込みに使用される出力バインディング (out) です。

この関数から Azure Storage キューに書き込みを行うには、次のコードで示すように、queue 型の out バインディングを msg という名前で追加します。

    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "msg",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

queue では、queueName にキューの名前を指定し、(local.settings.json ファイルから得られる) Azure Storage 接続の "名前" を connection に指定する必要があります。

C# プロジェクトでは、バインドは関数メソッドのバインド属性として定義されます。 具体的な定義は、お使いのアプリがインプロセス (C# クラス ライブラリ) で実行されるのか、分離ワーカー プロセスで実行されるのかによって異なります。

HttpExample.cs プロジェクト ファイルを開き、次の MultiResponse クラスを追加します。

public class MultiResponse
{
    [QueueOutput("outqueue",Connection = "AzureWebJobsStorage")]
    public string[] Messages { get; set; }
    public HttpResponseData HttpResponse { get; set; }
}

MultiResponse クラスを使用すると、outqueue という名前のストレージ キューと HTTP 成功メッセージに書き込むことができます。 QueueOutput 属性は文字列配列に適用されるので、複数のメッセージをキューに送信できます。

ストレージ アカウントの接続文字列は Connection プロパティによって設定されます。 この例では、既定のストレージ アカウントを既に使用しているため、Connection を省略してもかまいません。

Java プロジェクトでは、バインドは関数メソッドのバインド注釈として定義されます。 その後、これらの注釈に基づいて function.json ファイルが自動的に生成されます。

src/main/java の下の対象の関数コードの場所を参照し、Function.java プロジェクト ファイルを開きます。run メソッド定義に、次のパラメーターを追加します。

@QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") OutputBinding<String> msg

msg パラメーターの型は OutputBinding<T> であり、文字列のコレクションを表します。 これらの文字列は、関数の完了時にメッセージとして出力バインドに書き込まれます。 この場合、出力は outqueue という名前のストレージ キューです。 このストレージ アカウントの接続文字列は、connection メソッドによって設定されます。 接続文字列自体を渡すのではなく、ストレージ アカウントの接続文字列を含むアプリケーション設定を渡します。

run メソッドの定義は次の例のようになります。

@FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION)  
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") 
        OutputBinding<String> msg, final ExecutionContext context) {
    ...
}

バインディングの詳細については、「Azure Functions でのトリガーとバインドの概念」とキューの出力の構成に関する記事を参照してください。

出力バインディングを使用するコードを追加する

キュー バインディングが定義されたら、msg 出力パラメーターを受け取ってメッセージをキューに書き込むように関数を更新することができます。

次のコードに合わせて HttpExample\function_app.py を更新し、関数の定義に msg パラメーターを、if name: ステートメントの下に msg.set(name) を追加してください。

import azure.functions as func
import logging

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

@app.route(route="HttpExample")
@app.queue_output(arg_name="msg", queue_name="outqueue", connection="AzureWebJobsStorage")
def HttpExample(req: func.HttpRequest, msg: func.Out [func.QueueMessage]) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        msg.set(name)
        return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
             status_code=200
        )

msg パラメーターは、azure.functions.Out class のインスタンスです。 set メソッドはキューに文字列メッセージを書き込みます。 この場合は、URL クエリ文字列の中で関数に渡される name です。

context.extraOutputs の出力バインド オブジェクトを使用してキュー メッセージを作成するコードを追加します。 このコードを return ステートメントの前に追加します。

context.extraOutputs.set(sendToQueue, [msg]);

この時点で、関数は次のようになります。

const { app, output } = require('@azure/functions');

const sendToQueue = output.storageQueue({
  queueName: 'outqueue',
  connection: 'AzureWebJobsStorage',
});

app.http('HttpExample', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  extraOutputs: [sendToQueue],
  handler: async (request, context) => {
    try {
      context.log(`Http function processed request for url "${request.url}"`);

      const name = request.query.get('name') || (await request.text());
      context.log(`Name: ${name}`);

      if (name) {
        const msg = `Name passed to the function ${name}`;
        context.extraOutputs.set(sendToQueue, [msg]);
        return { body: msg };
      } else {
        context.log('Missing required data');
        return { status: 404, body: 'Missing required data' };
      }
    } catch (error) {
      context.log(`Error: ${error}`);
      return { status: 500, body: 'Internal Server Error' };
    }
  },
});

context.extraOutputs の出力バインド オブジェクトを使用してキュー メッセージを作成するコードを追加します。 このコードを return ステートメントの前に追加します。

context.extraOutputs.set(sendToQueue, [msg]);

この時点で、関数は次のようになります。

import {
  app,
  output,
  HttpRequest,
  HttpResponseInit,
  InvocationContext,
  StorageQueueOutput,
} from '@azure/functions';

const sendToQueue: StorageQueueOutput = output.storageQueue({
  queueName: 'outqueue',
  connection: 'AzureWebJobsStorage',
});

export async function HttpExample(
  request: HttpRequest,
  context: InvocationContext,
): Promise<HttpResponseInit> {
  try {
    context.log(`Http function processed request for url "${request.url}"`);

    const name = request.query.get('name') || (await request.text());
    context.log(`Name: ${name}`);

    if (name) {
      const msg = `Name passed to the function ${name}`;
      context.extraOutputs.set(sendToQueue, [msg]);
      return { body: msg };
    } else {
      context.log('Missing required data');
      return { status: 404, body: 'Missing required data' };
    }
  } catch (error) {
    context.log(`Error: ${error}`);
    return { status: 500, body: 'Internal Server Error' };
  }
}

app.http('HttpExample', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: HttpExample,
});

Push-OutputBinding コマンドレットと msg 出力バインディングを使用してキューにテキストを書き込むコードを追加します。 if ステートメントで OK ステータスを設定する前に、このコードを追加してください。

$outputMsg = $name
Push-OutputBinding -name msg -Value $outputMsg

この時点で、関数は次のようになるはずです。

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$name = $Request.Query.Name
if (-not $name) {
    $name = $Request.Body.Name
}

if ($name) {
    # Write the $name value to the queue, 
    # which is the name passed to the function.
    $outputMsg = $name
    Push-OutputBinding -name msg -Value $outputMsg

    $status = [HttpStatusCode]::OK
    $body = "Hello $name"
}
else {
    $status = [HttpStatusCode]::BadRequest
    $body = "Please pass a name on the query string or in the request body."
}

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = $status
    Body = $body
})

既存の HttpExample クラスを次のコードに置き換えます。

    [Function("HttpExample")]
    public static MultiResponse Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req,
        FunctionContext executionContext)
    {
        var logger = executionContext.GetLogger("HttpExample");
        logger.LogInformation("C# HTTP trigger function processed a request.");

        var message = "Welcome to Azure Functions!";

        var response = req.CreateResponse(HttpStatusCode.OK);
        response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
        response.WriteString(message);

        // Return a response to both HTTP trigger and storage output binding.
        return new MultiResponse()
        {
            // Write a single message.
            Messages = new string[] { message },
            HttpResponse = response
        };
    }
}

これで、新しい msg パラメーターを使用して、関数コードから出力バインドに書き込むことができます。 成功応答の前に次のコード行を追加して、name の値を msg 出力バインドに追加します。

msg.setValue(name);

出力バインドを使用すると、認証、キュー参照の取得、またはデータの書き込みに、Azure Storage SDK のコードを使用する必要がなくなります。 Functions ランタイムおよびキューの出力バインドが、ユーザーに代わってこれらのタスクを処理します。

run メソッドは次の例のようになります。

public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) 
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", 
        connection = "AzureWebJobsStorage") OutputBinding<String> msg, 
        final ExecutionContext context) {
    context.getLogger().info("Java HTTP trigger processed a request.");

    // Parse query parameter
    String query = request.getQueryParameters().get("name");
    String name = request.getBody().orElse(query);

    if (name == null) {
        return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
        .body("Please pass a name on the query string or in the request body").build();
    } else {
        // Write the name to the message queue. 
        msg.setValue(name);

        return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
    }
}

テストを更新する

アーキタイプはテストのセットも作成するため、run メソッド シグネチャ内の新しい msg パラメーターを処理するためにこれらのテストを更新する必要があります。

src/main/java の下のテスト コードの場所を参照し、Function.java プロジェクト ファイルを開きます。//Invoke の下のコード行を次のコードに置き換えます。

@SuppressWarnings("unchecked")
final OutputBinding<String> msg = (OutputBinding<String>)mock(OutputBinding.class);
final HttpResponseMessage ret = new Function().run(req, msg, context);

認証、キューの参照の取得、データの書き込みのためのコードを記述する必要が "ない" 点に注目してください。 これらの統合タスクはすべて、Azure Functions ランタイムとキュー出力バインディングで処理され、手間がかかりません。

関数をローカルで実行する

  1. LocalFunctionProj フォルダーから、ローカルの Azure Functions ランタイム ホストを起動して関数を実行します。

    func start
    

    出力の最後の方に、次の行があります。

    関数をローカルで実行するときのターミナル ウィンドウの出力のスクリーンショット。

    Note

    HttpExample が上記のように表示されない場合、プロジェクトのルート フォルダー以外からホストを起動したと考えられます。 その場合は Ctrl+C キーを使用してホストを停止し、プロジェクトのルート フォルダーに移動して、前出のコマンドを再度実行してください。

  2. この出力から HTTP 関数の URL をブラウザーにコピーし、クエリ文字列 ?name=<YOUR_NAME> を付加して、http://localhost:7071/api/HttpExample?name=Functions のような完全な URL にします。 ブラウザーには、クエリ文字列値をエコー バックする応答メッセージが表示されるはずです。 要求を行うと、プロジェクトを起動したターミナルにもログ出力が表示されます。

  3. 完了したら、Ctrl + C キーを押し、「y」を入力して関数ホストを停止してください。

ヒント

起動中、Storage バインディング拡張機能など、Microsoft のバインディング拡張機能がホストによってダウンロードされてインストールされます。 このインストールが実行される理由は、host.json ファイルでバインディング拡張機能が次のプロパティによって既定で有効になっているためです。

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[1.*, 2.0.0)"
    }
}

バインディング拡張機能に関連したエラーが発生した場合は、host.json に上記のプロパティが存在することを確認してください。

Azure Storage キューのメッセージを確認する

キューは、Azure portal または Microsoft Azure Storage Explorer で確認できます。 次の手順に従って、Azure CLI でキューを確認することもできます。

  1. 関数プロジェクトの local.setting.json ファイルを開き、接続文字列の値をコピーします。 ターミナルまたはコマンド ウィンドウで、次のコマンドを実行して、AZURE_STORAGE_CONNECTION_STRING という名前の環境変数を作成し、<MY_CONNECTION_STRING> の代わりに実際の接続文字列を貼り付けます。 (この環境変数を作成すれば、--connection-string 引数を使用して接続文字列を後続の各コマンドに指定する必要はありません。)

    export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
    
  2. (省略可) az storage queue list コマンドを使用して、ご利用のアカウント内のストレージ キューを表示します。 このコマンドからの出力には、outqueue という名前のキューが含まれています。これはこのキューに対する最初のメッセージを関数が書き込んだときに作成されたものです。

    az storage queue list --output tsv
    
  3. az storage message get コマンドを使用して、このキューからメッセージ (先ほど関数をテストするときに指定した値) を読み取ります。 このコマンドは、キューから最初のメッセージを読み取って削除します。

    echo `echo $(az storage message get --queue-name outqueue -o tsv --query '[].{Message:content}') | base64 --decode`
    

    メッセージ本文は base64 でエンコードされた状態で保存されるため、表示するメッセージをあらかじめデコードしておく必要があります。 az storage message get を実行すると、メッセージがキューから削除されます。 outqueue にメッセージが 1 つしかない場合、このコマンドを 2 回目に実行したときにメッセージは取得されず、代わりにエラーが返されます。

Azure にプロジェクトを再デプロイする

関数から Azure Storage キューにメッセージが書き込まれたことをローカルで確認したので、プロジェクトを再デプロイして、Azure 上で実行するようにエンドポイントを更新することができます。

LocalFunctionsProj フォルダーで func azure functionapp publish コマンドを使用してプロジェクトを再デプロイし、<APP_NAME> を自分のアプリの名前に置き換えます。

func azure functionapp publish <APP_NAME>

ローカル プロジェクト フォルダーで、次の Maven コマンドを使用してプロジェクトを再発行します。

mvn azure-functions:deploy

Azure で確認する

  1. 前のクイックスタートと同様、ブラウザーまたは CURL を使用して、再デプロイした関数をテストします。

    publish コマンドの出力に表示されている完全な呼び出し URL にクエリ パラメーター &name=Functions を追加して、ブラウザーのアドレス バーにコピーします。 関数をローカルで実行したときと同じ出力がブラウザーに表示されるはずです。

  2. 前セクションの説明に従って、もう一度ストレージ キューを確認します。キューに新しいメッセージが書き込まれていることを確かめてください。

リソースをクリーンアップする

終了後は、追加コストの発生を避けるために、次のコマンドを使用して、リソース グループとそこに含まれるすべてのリソースを削除してください。

az group delete --name AzureFunctionsQuickstart-rg

次のステップ

HTTP によってトリガーされる関数を、ストレージ キューにデータを書き込むように更新しました。 この後は、Core Tools と Azure CLI を使用してコマンド ラインから行う Functions の開発について理解を深めましょう。