Connect Azure Functions to Azure Storage using Visual Studio Code

Azure Functions lets you connect Azure services and other resources to functions without having to write your own integration code. These bindings, which represent both input and output, are declared within the function definition. Data from bindings is provided to the function as parameters. A trigger is a special type of input binding. Although a function has only one trigger, it can have multiple input and output bindings. To learn more, see Azure Functions triggers and bindings concepts.

This article shows you how to use Visual Studio Code to connect the function you created in the previous quickstart article to Azure Storage. The output binding that you add to this function writes data from the HTTP request to a message in an Azure Queue storage queue.

Most bindings require a stored connection string that Functions uses to access the bound service. To make it easier, you use the Storage account that you created with your function app. The connection to this account is already stored in an app setting named AzureWebJobsStorage.

Configure your local environment

Before you start this article, you must meet the following requirements:

This article assumes that you're already signed in to your Azure subscription from Visual Studio Code. You can sign in by running Azure: Sign In from the command palette.

Download the function app settings

In the previous quickstart article, you created a function app in Azure along with the required Storage account. The connection string for this account is stored securely in app settings in Azure. In this article, you write messages to a Storage queue in the same account. To connect to your Storage account when running the function locally, you must download app settings to the local.settings.json file.

  1. Press the F1 key to open the command palette, then search for and run the command Azure Functions: Download Remote Settings.....

  2. Choose the function app you created in the previous article. Select Yes to all to overwrite the existing local settings.

    Important

    Because it contains secrets, the local.settings.json file never gets published, and is excluded from source control.

  3. Copy the value AzureWebJobsStorage, which is the key for the Storage account connection string value. You use this connection to verify that the output binding works as expected.

Register binding extensions

Because you're using a Queue storage output binding, you must have the Storage bindings extension installed before you run the project.

Your project has been configured to use extension bundles, which automatically installs a predefined set of extension packages.

Extension bundles usage is enabled in the host.json file at the root of the project, which appears as follows:

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

With the exception of HTTP and timer triggers, bindings are implemented as extension packages. Run the following dotnet add package command in the Terminal window to add the Storage extension package to your project.

dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage

Now, you can add the storage output binding to your project.

Add an output binding

In Functions, each type of binding requires a direction, type, and a unique name to be defined in the function.json file. The way you define these attributes depends on the language of your function app.

Binding attributes are defined directly in the function.json file. Depending on the binding type, additional properties may be required. The queue output configuration describes the fields required for an Azure Storage queue binding. The extension makes it easy to add bindings to the function.json file.

To create a binding, right-click (Ctrl+click on macOS) the function.json file in your HttpTrigger folder and choose Add binding.... Follow the prompts to define the following binding properties for the new binding:

Prompt Value Description
Select binding direction out The binding is an output binding.
Select binding with direction... Azure Queue Storage The binding is an Azure Storage queue binding.
The name used to identify this binding in your code msg Name that identifies the binding parameter referenced in your code.
The queue to which the message will be sent outqueue The name of the queue that the binding writes to. When the queueName doesn't exist, the binding creates it on first use.
Select setting from "local.setting.json" AzureWebJobsStorage The name of an application setting that contains the connection string for the Storage account. The AzureWebJobsStorage setting contains the connection string for the Storage account you created with the function app.

A binding is added to the bindings array in your function.json, which should look like the following:

{
  "type": "queue",
  "direction": "out",
  "name": "msg",
  "queueName": "outqueue",
  "connection": "AzureWebJobsStorage"
}

In a C# class library project, the bindings are defined as binding attributes on the function method. The function.json file required by Functions is then auto-generated based on these attributes.

Open the HttpExample.cs project file and add the following parameter to the Run method definition:

[Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg,

The msg parameter is an ICollector<T> type, which represents a collection of messages that are written to an output binding when the function completes. In this case, the output is a storage queue named outqueue. The connection string for the Storage account is set by the StorageAccountAttribute. This attribute indicates the setting that contains the Storage account connection string and can be applied at the class, method, or parameter level. In this case, you could omit StorageAccountAttribute because you are already using the default storage account.

The Run method definition should now look like the following:

[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, 
    [Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg, 
    ILogger log)

In a Java project, the bindings are defined as binding annotations on the function method. The function.json file is then autogenerated based on these annotations.

Browse to the location of your function code under src/main/java, open the Function.java project file, and add the following parameter to the run method definition:

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

The msg parameter is an OutputBinding<T> type, which represents a collection of strings that are written as messages to an output binding when the function completes. In this case, the output is a storage queue named outqueue. The connection string for the Storage account is set by the connection method. Rather than the connection string itself, you pass the application setting that contains the Storage account connection string.

The run method definition should now look like the following example:

@FunctionName("HttpExample")
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) {

Add code that uses the output binding

After the binding is defined, you can use the name of the binding to access it as an attribute in the function signature. By using an output binding, you don't have to use the Azure Storage SDK code for authentication, getting a queue reference, or writing data. The Functions runtime and queue output binding do those tasks for you.

Add code that uses the msg output binding object on context.bindings to create a queue message. Add this code before the context.res statement.

context.bindings.msg = (req.query.name || req.body.name);

At this point, your function should look as follows:

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    if (req.query.name || (req.body && req.body.name)) {
        // Add a message to the Storage queue,
        // which is the name passed to the function.
        context.bindings.msg = (req.query.name || req.body.name);
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

Add code that uses the msg output binding object on context.bindings to create a queue message. Add this code before the context.res statement.

context.bindings.msg = name;

At this point, your function should look as follows:

import { AzureFunction, Context, HttpRequest } from "@azure/functions"

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    context.log('HTTP trigger function processed a request.');
    const name = (req.query.name || (req.body && req.body.name));

    if (name) {
        // Add a message to the storage queue, 
        // which is the name passed to the function.
        context.bindings.msg = name; 
        // Send a "hello" response.
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

export default httpTrigger;

Add code that uses the Push-OutputBinding cmdlet to write text to the queue using the msg output binding. Add this code before you set the OK status in the if statement.

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

At this point, your function should look as follows:

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
})

Update HttpExample\__init__.py to match the following code, adding the msg parameter to the function definition and msg.set(name) under the if name: statement.

import logging

import azure.functions as func


def main(req: func.HttpRequest, msg: func.Out[func.QueueMessage]) -> str:

    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}!")
    else:
        return func.HttpResponse(
            "Please pass a name on the query string or in the request body",
            status_code=400
        )

The msg parameter is an instance of the azure.functions.InputStream class. Its set method writes a string message to the queue, in this case the name passed to the function in the URL query string.

Add code that uses the msg output binding object to create a queue message. Add this code before the method returns.

if (!string.IsNullOrEmpty(name))
{
    // Add a message to the output collection.
    msg.Add(string.Format("Name passed to the function: {0}", name));
}

At this point, your function should look as follows:

[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, 
    [Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg, 
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    string name = req.Query["name"];

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    dynamic data = JsonConvert.DeserializeObject(requestBody);
    name = name ?? data?.name;

    if (!string.IsNullOrEmpty(name))
    {
        // Add a message to the output collection.
        msg.Add(string.Format("Name passed to the function: {0}", name));
    }
    return name != null
        ? (ActionResult)new OkObjectResult($"Hello, {name}")
        : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}

Now, you can use the new msg parameter to write to the output binding from your function code. Add the following line of code before the success response to add the value of name to the msg output binding.

// Write the name to the message queue.

When you use an output binding, you don't have to use the Azure Storage SDK code for authentication, getting a queue reference, or writing data. The Functions runtime and queue output binding do those tasks for you.

Your run method should now look like the following example:

@FunctionName("HttpExample")
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();
    }

Update the tests

Because the archetype also creates a set of tests, you need to update these tests to handle the new msg parameter in the run method signature.

Browse to the location of your test code under src/test/java, open the Function.java project file, and replace the line of code under //Invoke with the following code.

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

Run the function locally

Visual Studio Code integrates with Azure Functions Core Tools to let you run this project on your local development computer before you publish to Azure.

  1. To call your function, press F5 to start the function app project. Output from Core Tools is displayed in the Terminal panel.

  2. If you haven't already installed Azure Functions Core Tools, select Install at the prompt. When the Core Tools are installed, your app starts in the Terminal panel. You can see the URL endpoint of your HTTP-triggered function running locally.

    Azure local output

  3. With Core Tools running, navigate to the following URL to execute a GET request, which includes ?name=Functions query string.

    http://localhost:7071/api/HttpExample?name=Functions

  4. A response is returned, which looks like the following in a browser:

    Function localhost response in the browser

  5. Information about the request is shown in Terminal panel.

    Function execution in Terminal panel

  6. Press Ctrl + C to stop Core Tools and disconnect the debugger.

Run the function locally

Azure Functions Core Tools integrates with Visual Studio Code to let you run and debug an Azure Functions project locally. For details on how to debug in Visual Studio Code, see Debug PowerShell Azure Functions locally.

  1. Press F5 to start the function app project. Output from Core Tools is displayed in the Terminal panel.

  2. In the Terminal panel, copy the URL endpoint of your HTTP-triggered function.

    Azure local output

  3. Append the query string ?name=<yourname> to this URL, and then use Invoke-RestMethod in a second PowerShell command prompt to execute the request, as follows:

    PS > Invoke-RestMethod -Method Get -Uri http://localhost:7071/api/HttpTrigger?name=PowerShell
    Hello PowerShell
    

    You can also execute the GET request from a browser from the following URL:

    http://localhost:7071/api/HttpExample?name=PowerShell

    When you call the HttpTrigger endpoint without passing a name parameter either as a query parameter or in the body, the function returns a BadRequest error. When you review the code in run.ps1, you see that this error occurs by design.

  4. Information about the request is shown in Terminal panel.

    Function execution in Terminal panel

  5. When done, press Ctrl + C to stop Core Tools.

After you've verified that the function runs correctly on your local computer, it's time to publish the project to Azure.

A new queue named outqueue is created in your storage account by the Functions runtime when the output binding is first used. You'll use Storage Explorer to verify that the queue was created along with the new message.

Update the tests

Because the archetype also creates a set of tests, you need to update these tests to handle the new msg parameter in the run method signature.

Browse to the location of your test code under src/test/java, open the Function.java project file, and replace the line of code under //Invoke with the following code.

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

Connect Storage Explorer to your account

Skip this section if you have already installed Azure Storage Explorer and connected it to your Azure account.

  1. Run the [Azure Storage Explorer] tool, select the connect icon on the left, and select Add an account.

    Add an Azure account to Microsoft Azure Storage Explorer

  2. In the Connect dialog, choose Add an Azure account, choose your Azure environment, and select Sign in....

    Sign in to your Azure account

After you successfully sign in to your account, you see all of the Azure subscriptions associated with your account.

Examine the output queue

  1. In Visual Studio Code, press the F1 key to open the command palette, then search for and run the command Azure Storage: Open in Storage Explorer and choose your Storage account name. Your storage account opens in Azure Storage Explorer.

  2. Expand the Queues node, and then select the queue named outqueue.

    The queue contains the message that the queue output binding created when you ran the HTTP-triggered function. If you invoked the function with the default name value of Azure, the queue message is Name passed to the function: Azure.

    Queue message shown in Azure Storage Explorer

  3. Run the function again, send another request, and you'll see a new message appear in the queue.

Now, it's time to republish the updated function app to Azure.

Redeploy and verify the updated app

  1. In Visual Studio Code, press F1 to open the command palette. In the command palette, search for and select Azure Functions: Deploy to function app....

  2. Choose the function app that you created in the first article. Because you're redeploying your project to the same app, select Deploy to dismiss the warning about overwriting files.

  3. After deployment completes, you can again use cURL or a browser to test the redeployed function. As before, append the query string &name=<yourname> to the URL, as in the following example:

    curl https://myfunctionapp.azurewebsites.net/api/httptrigger?code=cCr8sAxfBiow548FBDLS1....&name=<yourname>
    
  4. Again view the message in the storage queue to verify that the output binding again generates a new message in the queue.

Clean up resources

In Azure, resources refer to function apps, functions, storage accounts, and so forth. They're grouped into resource groups, and you can delete everything in a group by deleting the group.

You created resources to complete these quickstarts. You may be billed for these resources, depending on your account status and service pricing. If you don't need the resources anymore, here's how to delete them:

  1. In Visual Studio Code, press F1 to open the command palette. In the command palette, search for and select Azure Functions: Open in portal.

  2. Choose your function app, and press Enter. The function app page opens in the Azure portal.

  3. In the Overview tab, select the named link next to Resource group.

    Select the resource group to delete from the function app page.

  4. In the Resource group page, review the list of included resources, and verify that they are the ones you want to delete.

  5. Select Delete resource group, and follow the instructions.

    Deletion may take a couple of minutes. When it's done, a notification appears for a few seconds. You can also select the bell icon at the top of the page to view the notification.

Next steps

You've updated your HTTP triggered function to write data to a Storage queue. Now you can learn more about developing Functions using Visual Studio Code: