Connect Azure Functions to Azure Storage using command line tools
In this article, you integrate an Azure Storage queue with the function and storage account you created in the previous quickstart article. You achieve this integration by using an output binding that writes data from an HTTP request to a message in the queue. Completing this article incurs no additional costs beyond the few USD cents of the previous quickstart. To learn more about bindings, see Azure Functions triggers and bindings concepts.
Configure your local environment
Before you begin, you must complete the article, Quickstart: Create an Azure Functions project from the command line. If you already cleaned up resources at the end of that article, go through the steps again to recreate the function app and related resources in Azure.
Before you begin, you must complete the article, Quickstart: Create an Azure Functions project from the command line. If you already cleaned up resources at the end of that article, go through the steps again to recreate the function app and related resources in Azure.
Before you begin, you must complete the article, Quickstart: Create an Azure Functions project from the command line. If you already cleaned up resources at the end of that article, go through the steps again to recreate the function app and related resources in Azure.
Before you begin, you must complete the article, Quickstart: Create an Azure Functions project from the command line. If you already cleaned up resources at the end of that article, go through the steps again to recreate the function app and related resources in Azure.
Before you begin, you must complete the article, Quickstart: Create an Azure Functions project from the command line. If you already cleaned up resources at the end of that article, go through the steps again to recreate the function app and related resources in Azure.
Before you begin, you must complete the article, Quickstart: Create an Azure Functions project from the command line. If you already cleaned up resources at the end of that article, go through the steps again to recreate the function app and related resources in Azure.
Retrieve the Azure Storage connection string
Earlier, you created an Azure Storage account for function app's use. The connection string for this account is stored securely in app settings in Azure. By downloading the setting into the local.settings.json file, you can use the connection to write to a Storage queue in the same account when running the function locally.
From the root of the project, run the following command, replace
<APP_NAME>
with the name of your function app from the previous step. This command overwrites any existing values in the file.func azure functionapp fetch-app-settings <APP_NAME>
Open local.settings.json file and locate the value named
AzureWebJobsStorage
, which is the Storage account connection string. You use the nameAzureWebJobsStorage
and the connection string in other sections of this article.
Important
Because the local.settings.json file contains secrets downloaded from Azure, always exclude this file from source control. The .gitignore file created with a local functions project excludes the file by default.
Register binding extensions
Except for 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 definition to the function
Although a function can have only one trigger, it can have multiple input and output bindings, which lets you connect to other Azure services and resources without writing custom integration code.
You declare these bindings in the function.json file in your function folder. From the previous quickstart, your function.json file in the HttpExample folder contains two bindings in the bindings
collection:
The way you declare binding attributes depends on your Python programming model.
You declare these bindings in the function.json file in your function folder. From the previous quickstart, your function.json file in the HttpExample folder contains two bindings in the bindings
collection:
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
Each binding has at least a type, a direction, and a name. In the above example, the first binding is of type httpTrigger
with the direction in
. For the in
direction, name
specifies the name of an input parameter that's sent to the function when invoked by the trigger.
The second binding in the collection is of type http
with the direction out
, in which case the special name
of $return
indicates that this binding uses the function's return value rather than providing an input parameter.
To write to an Azure Storage queue from this function, add an out
binding of type queue
with the name msg
, as shown in the code below:
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
},
{
"type": "queue",
"direction": "out",
"name": "msg",
"queueName": "outqueue",
"connection": "AzureWebJobsStorage"
}
]
In this case, msg
is given to the function as an output argument. For a queue
type, you must also specify the name of the queue in queueName
and provide the name of the Azure Storage connection (from local.settings.json file) in connection
.
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "Request",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "Response"
}
]
The second binding in the collection is named res
. This http
binding is an output binding (out
) that is used to write the HTTP response.
To write to an Azure Storage queue from this function, add an out
binding of type queue
with the name msg
, as shown in the code below:
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
},
{
"type": "queue",
"direction": "out",
"name": "msg",
"queueName": "outqueue",
"connection": "AzureWebJobsStorage"
}
]
}
The second binding in the collection is named res
. This http
binding is an output binding (out
) that is used to write the HTTP response.
To write to an Azure Storage queue from this function, add an out
binding of type queue
with the name msg
, as shown in the code below:
{
"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"
}
]
}
In this case, msg
is given to the function as an output argument. For a queue
type, you must also specify the name of the queue in queueName
and provide the name of the Azure Storage connection (from local.settings.json file) in connection
.
In a C# project, the bindings are defined as binding attributes on the function method. Specific definitions depend on whether your app runs in-process (C# class library) or in an isolated process.
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, representing a collection of messages written to an output binding when the function completes. In this case, the output is a storage queue named outqueue
. The StorageAccountAttribute
sets the connection string for the storage account. 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're already using the default storage account.
The Run method definition must now look like the following code:
[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. These strings 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. You pass the application setting that contains the Storage account connection string, rather than passing the connection string itself.
The run
method definition must now look like the following example:
@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) {
...
}
For more information on the details of bindings, see Azure Functions triggers and bindings concepts and queue output configuration.
Add code to use the output binding
With the queue binding defined, you can now update your function to receive the msg
output parameter and write messages to the queue.
Update HttpExample\function_app.py to match the following code, add the msg
parameter to the function definition and msg.set(name)
under the if name:
statement:
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
)
The msg
parameter is an instance of the azure.functions.Out class
. The set
method writes a string message to the queue. In this case, it's the name
passed to the function in the URL query string.
Add code that uses the output binding object on context.extraOutputs
to create a queue message. Add this code before the return statement.
context.extraOutputs.set(sendToQueue, [msg]);
At this point, your function could look as follows:
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' };
}
},
});
Add code that uses the output binding object on context.extraOutputs
to create a queue message. Add this code before the return statement.
context.extraOutputs.set(sendToQueue, [msg]);
At this point, your function could look as follows:
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,
});
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 must 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
})
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(name);
}
At this point, your function must 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(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.
msg.setValue(name);
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 must now look like the following example:
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);
Observe that you don't need to write any code for authentication, getting a queue reference, or writing data. All these integration tasks are conveniently handled in the Azure Functions runtime and queue output binding.
Run the function locally
Run your function by starting the local Azure Functions runtime host from the LocalFunctionProj folder.
func start
Toward the end of the output, the following lines must appear:
Note
If HttpExample doesn't appear as shown above, you likely started the host from outside the root folder of the project. In that case, use Ctrl+C to stop the host, go to the project's root folder, and run the previous command again.
Copy the URL of your HTTP function from this output to a browser and append the query string
?name=<YOUR_NAME>
, making the full URL likehttp://localhost:7071/api/HttpExample?name=Functions
. The browser should display a response message that echoes back your query string value. The terminal in which you started your project also shows log output as you make requests.When you're done, press Ctrl + C and type
y
to stop the functions host.
Tip
During startup, the host downloads and installs the Storage binding extension and other Microsoft binding extensions. This installation happens because binding extensions are enabled by default in the host.json file with the following properties:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}
}
If you encounter any errors related to binding extensions, check that the above properties are present in host.json.
View the message in the Azure Storage queue
You can view the queue in the Azure portal or in the Azure Storage Explorer. You can also view the queue in the Azure CLI, as described in the following steps:
Open the function project's local.setting.json file and copy the connection string value. In a terminal or command window, run the following command to create an environment variable named
AZURE_STORAGE_CONNECTION_STRING
, and paste your specific connection string in place of<MY_CONNECTION_STRING>
. (This environment variable means you don't need to supply the connection string to each subsequent command using the--connection-string
argument.)export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
(Optional) Use the
az storage queue list
command to view the Storage queues in your account. The output from this command must include a queue namedoutqueue
, which was created when the function wrote its first message to that queue.az storage queue list --output tsv
Use the
az storage message get
command to read the message from this queue, which should be the value you supplied when testing the function earlier. The command reads and removes the first message from the queue.echo `echo $(az storage message get --queue-name outqueue -o tsv --query '[].{Message:content}') | base64 --decode`
Because the message body is stored base64 encoded, the message must be decoded before it's displayed. After you execute
az storage message get
, the message is removed from the queue. If there was only one message inoutqueue
, you won't retrieve a message when you run this command a second time and instead get an error.
Redeploy the project to Azure
Now that you've verified locally that the function wrote a message to the Azure Storage queue, you can redeploy your project to update the endpoint running on Azure.
In the LocalFunctionsProj folder, use the func azure functionapp publish
command to redeploy the project, replacing<APP_NAME>
with the name of your app.
func azure functionapp publish <APP_NAME>
In the local project folder, use the following Maven command to republish your project:
mvn azure-functions:deploy
Verify in Azure
As in the previous quickstart, use a browser or CURL to test the redeployed function.
Examine the Storage queue again, as described in the previous section, to verify that it contains the new message written to the queue.
Clean up resources
After you've finished, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.
az group delete --name AzureFunctionsQuickstart-rg
Next steps
You've updated your HTTP triggered function to write data to a Storage queue. Now you can learn more about developing Functions from the command line using Core Tools and Azure CLI: