Azure Functions F# Developer Reference
F# for Azure Functions is a solution for easily running small pieces of code, or "functions," in the cloud. Data flows into your F# function via function arguments. Argument names are specified in function.json
, and there are predefined names for accessing things like the function logger and cancellation tokens.
Important
F# script (.fsx) is only supported by version 1.x of the Azure Functions runtime. If you want to use F# with version 2.x and later versions of the runtime, you must use a precompiled F# class library project (.fs). You create, manage, and publish an F# class library project using Visual Studio as you would a C# class library project. For more information about Functions versions, see Azure Functions runtime versions overview.
This article assumes that you've already read the Azure Functions developer reference.
How .fsx works
An .fsx
file is an F# script. It can be thought of as an F# project that's contained in a single file. The file contains both the code for your program (in this case, your Azure Function) and directives for managing dependencies.
When you use an .fsx
for an Azure Function, commonly required assemblies are automatically included for you, allowing you to focus on the function rather than "boilerplate" code.
Folder structure
The folder structure for an F# script project looks like the following:
FunctionsProject
| - MyFirstFunction
| | - run.fsx
| | - function.json
| | - function.proj
| - MySecondFunction
| | - run.fsx
| | - function.json
| | - function.proj
| - host.json
| - extensions.csproj
| - bin
There's a shared host.json file that can be used to configure the function app. Each function has its own code file (.fsx) and binding configuration file (function.json).
The binding extensions required in version 2.x and later versions of the Functions runtime are defined in the extensions.csproj
file, with the actual library files in the bin
folder. When developing locally, you must register binding extensions. When developing functions in the Azure portal, this registration is done for you.
Binding to arguments
Each binding supports some set of arguments, as detailed in the Azure Functions triggers and bindings developer reference. For example, one of the argument bindings a blob trigger supports is a POCO, which can be expressed using an F# record. For example:
type Item = { Id: string }
let Run(blob: string, output: byref<Item>) =
let item = { Id = "Some ID" }
output <- item
Your F# Azure Function will take one or more arguments. When we talk about Azure Functions arguments, we refer to input arguments and output arguments. An input argument is exactly what it sounds like: input to your F# Azure Function. An output argument is mutable data or a byref<>
argument that serves as a way to pass data back out of your function.
In the example above, blob
is an input argument, and output
is an output argument. Notice that we used byref<>
for output
(there's no need to add the [<Out>]
annotation). Using a byref<>
type allows your function to change which record or object the argument refers to.
When an F# record is used as an input type, the record definition must be marked with [<CLIMutable>]
in order to allow the Azure Functions framework to set the fields appropriately before passing the record to your function. Under the hood, [<CLIMutable>]
generates setters for the record properties. For example:
[<CLIMutable>]
type TestObject =
{ SenderName : string
Greeting : string }
let Run(req: TestObject, log: ILogger) =
{ req with Greeting = sprintf "Hello, %s" req.SenderName }
An F# class can also be used for both in and out arguments. For a class, properties will usually need getters and setters. For example:
type Item() =
member val Id = "" with get,set
member val Text = "" with get,set
let Run(input: string, item: byref<Item>) =
let result = Item(Id = input, Text = "Hello from F#!")
item <- result
Logging
To log output to your streaming logs in F#, your function should take an argument of type ILogger. For consistency, we recommend this argument is named log
. For example:
let Run(blob: string, output: byref<string>, log: ILogger) =
log.LogInformation(sprintf "F# Azure Function processed a blob: %s" blob)
output <- input
Async
The async
workflow can be used, but the result needs to return a Task
. This can be done with Async.StartAsTask
, for example:
let Run(req: HttpRequestMessage) =
async {
return new HttpResponseMessage(HttpStatusCode.OK)
} |> Async.StartAsTask
Cancellation Token
If your function needs to handle shutdown gracefully, you can give it a CancellationToken
argument. This can be combined with async
, for example:
let Run(req: HttpRequestMessage, token: CancellationToken)
let f = async {
do! Async.Sleep(10)
return new HttpResponseMessage(HttpStatusCode.OK)
}
Async.StartAsTask(f, token)
Importing namespaces
Namespaces can be opened in the usual way:
open System.Net
open System.Threading.Tasks
open Microsoft.Extensions.Logging
let Run(req: HttpRequestMessage, log: ILogger) =
...
The following namespaces are automatically opened:
System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading.Tasks
Microsoft.Azure.WebJobs
Microsoft.Azure.WebJobs.Host
.
Referencing External Assemblies
Similarly, framework assembly references can be added with the #r "AssemblyName"
directive.
#r "System.Web.Http"
open System.Net
open System.Net.Http
open System.Threading.Tasks
open Microsoft.Extensions.Logging
let Run(req: HttpRequestMessage, log: ILogger) =
...
The following assemblies are automatically added by the Azure Functions hosting environment:
mscorlib
,System
System.Core
System.Xml
System.Net.Http
Microsoft.Azure.WebJobs
Microsoft.Azure.WebJobs.Host
Microsoft.Azure.WebJobs.Extensions
System.Web.Http
System.Net.Http.Formatting
.
In addition, the following assemblies are special cased and may be referenced by simplename (e.g. #r "AssemblyName"
):
Newtonsoft.Json
Microsoft.WindowsAzure.Storage
Microsoft.ServiceBus
Microsoft.AspNet.WebHooks.Receivers
Microsoft.AspNEt.WebHooks.Common
.
If you need to reference a private assembly, you can upload the assembly file into a bin
folder relative to your function and reference it by using the file name (e.g. #r "MyAssembly.dll"
). For information on how to upload files to your function folder, see the following section on package management.
Editor Prelude
An editor that supports F# Compiler Services will not be aware of the namespaces and assemblies that Azure Functions automatically includes. As such, it can be useful to include a prelude that helps the editor find the assemblies you are using, and to explicitly open namespaces. For example:
#if !COMPILED
#I "../../bin/Binaries/WebJobs.Script.Host"
#r "Microsoft.Azure.WebJobs.Host.dll"
#endif
open System
open Microsoft.Azure.WebJobs.Host
open Microsoft.Extensions.Logging
let Run(blob: string, output: byref<string>, log: ILogger) =
...
When Azure Functions executes your code, it processes the source with COMPILED
defined, so the editor prelude will be ignored.
Package management
To use NuGet packages in an F# function, add a project.json
file to the function's folder in the function app's file system. Here is an example project.json
file that adds a NuGet package reference to Microsoft.ProjectOxford.Face
version 1.1.0:
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.ProjectOxford.Face": "1.1.0"
}
}
}
}
Only the .NET Framework 4.6 is supported, so make sure that your project.json
file specifies net46
as shown here.
When you upload a project.json
file, the runtime gets the packages and automatically adds references to the package assemblies. You don't need to add #r "AssemblyName"
directives. Just add the required open
statements to your .fsx
file.
You may wish to put automatically references assemblies in your editor prelude, to improve your editor's interaction with F# Compile Services.
How to add a project.json
file to your Azure Function
- Begin by making sure your function app is running, which you can do by opening your function in the Azure portal. This also gives access to the streaming logs where package installation output will be displayed.
- To upload a
project.json
file, use one of the methods described in how to update function app files. If you are using Continuous Deployment for Azure Functions, you can add aproject.json
file to your staging branch in order to experiment with it before adding it to your deployment branch. - After the
project.json
file is added, you will see output similar to the following example in your function's streaming log:
2016-04-04T19:02:48.745 Restoring packages.
2016-04-04T19:02:48.745 Starting NuGet restore
2016-04-04T19:02:50.183 MSBuild auto-detection: using msbuild version '14.0' from 'D:\Program Files (x86)\MSBuild\14.0\bin'.
2016-04-04T19:02:50.261 Feeds used:
2016-04-04T19:02:50.261 C:\DWASFiles\Sites\facavalfunctest\LocalAppData\NuGet\Cache
2016-04-04T19:02:50.261 https://api.nuget.org/v3/index.json
2016-04-04T19:02:50.261
2016-04-04T19:02:50.511 Restoring packages for D:\home\site\wwwroot\HttpTriggerCSharp1\Project.json...
2016-04-04T19:02:52.800 Installing Newtonsoft.Json 6.0.8.
2016-04-04T19:02:52.800 Installing Microsoft.ProjectOxford.Face 1.1.0.
2016-04-04T19:02:57.095 All packages are compatible with .NETFramework,Version=v4.6.
2016-04-04T19:02:57.189
2016-04-04T19:02:57.189
2016-04-04T19:02:57.455 Packages restored.
Environment variables
To get an environment variable or an app setting value, use System.Environment.GetEnvironmentVariable
, for example:
open System.Environment
open Microsoft.Extensions.Logging
let Run(timer: TimerInfo, log: ILogger) =
log.LogInformation("Storage = " + GetEnvironmentVariable("AzureWebJobsStorage"))
log.LogInformation("Site = " + GetEnvironmentVariable("WEBSITE_SITE_NAME"))
Reusing .fsx code
You can use code from other .fsx
files by using a #load
directive. For example:
run.fsx
#load "logger.fsx"
let Run(timer: TimerInfo, log: ILogger) =
mylog log (sprintf "Timer: %s" DateTime.Now.ToString())
logger.fsx
let mylog(log: ILogger, text: string) =
log.LogInformation(text);
Paths provides to the #load
directive are relative to the location of your .fsx
file.
#load "logger.fsx"
loads a file located in the function folder.#load "package\logger.fsx"
loads a file located in thepackage
folder in the function folder.#load "..\shared\mylogger.fsx"
loads a file located in theshared
folder at the same level as the function folder, that is, directly underwwwroot
.
The #load
directive only works with .fsx
(F# script) files, and not with .fs
files.
Next steps
For more information, see the following resources:
Feedback
Submit and view feedback for