Article summary
If you’ve ever glanced at Azure Functions and F#, you might think they were made for each other. And yet if you want to create a new Azure Function project in Visual Studio, C# is apparently your only option.
Maybe someday, Visual Studio will include support for Azure Functions in F#, but for now it’s possible to get there by adapting the C# Azure Function template. After all, F# is a first-class language on the .NET CLR, and it’s all the same once it’s compiled anyway.
It’s important to note that I’m talking about compiled F# functions—not the fsx
script that you get when creating a function through the Azure web portal.
Project Files
Starting a new Azure Function project in Visual Studio 2017 generates these five files:
- FunctionApp1.csproj
- Function1.cs
- host.json
- local.settings.json
- .gitignore
The C# project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.13" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
We can convert the C# project file into an F# project file just by changing a few lines:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.13" />
</ItemGroup>
<ItemGroup>
<Compile Include="Function1.fs" />
</ItemGroup>
<ItemGroup>
<Content Include="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>
I’ve removed the reference to the Microsoft.CSharp
assembly and added a section that lists F# source files (because in F# order matters), including a new Function1.fs
.
For some reason, the lines in the C# project file that copy host.json
and local.settings.json
don’t work in the F# project (those files are simply missing from the output directory). In order to include them, I had to change the item type from None
to Content
.
Unfortunately, this causes these files to be required, and the build will fail if they’re gone. That’s not a problem for host.json
, but the .gitignore
from the C# template includes local.settings.json
!
The easiest thing to do is just remove that rule from .gitignore
and make sure local.settings.json
doesn’t contain any secrets.
Source Files
Now onto the source files. For reference, here’s what a C# timer-triggered Azure function template looks like:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
}
Porting this to F# is pretty straightforward:
namespace FunctionApp1
open System
open Microsoft.Azure.WebJobs
open Microsoft.Azure.WebJobs.Host
module Function1 =
[<FunctionName("Function1")>]
let Run ([<TimerTrigger("0 */5 * * * *")>] myTimer: TimerInfo) (log: TraceWriter) =
log.Info(sprintf "F# Timer trigger function executed at: %s" (DateTime.Now.ToString()))
The hard part is done! Those other files (host.json
and local.settings.json
) are used by the Azure Function host, so no changes are necessary there.
All that’s left is to build the project and then either run it locally or publish it to Azure.
Publishing
In Visual Studio, a C# Azure Function is endowed with a “Publish…” context menu item. Since our F# project amounts to a plain F# library, we don’t get the wizard. But this is hardly a problem since the command-line tools can handle publishing just fine.
First, install the Azure Functions Core Tools. Then you can either run your function locally, or publish it to Azure and run it there.
To run your function locally:
- Build the project (using Visual Studio, or
dotnet build
from your project directory). - Switch to the build artifacts directory (e.g.
bin\Debug\net461
). - Run
func host start
.
To publish to Azure:
- Create an Azure Function resource using the Azure web portal or
az
command. - From your project directory, run
dotnet publish -c Release
. - Switch to the publish output directory (e.g.
bin\Release\net461\publish
). - Run
func azure functionapp publish <function-app-name>
.
F# Azure Function Template
To get started quickly, here is a simple template for an Azure timer function (zip download) using the files I’ve referenced in this post. But since Microsoft is still actively developing Azure’s Function infrastructure, I look forward to better tooling support soon!
The tooling for F# support in Azure Functions should improve soon, including the templates for Func CLI, Visual Studio and Code.
I wouldn’t recommend removing local.settings.json from .gitignore. It has to exist for pretty much any Function App except HTTP-only anyway, and it’s very likely that people will start putting secrets inside (storage / service bus / cosmos db connection string etc).
Another intro to precompiled F# Functions can be found at my blog https://mikhail.io/2017/12/precompiled-azure-functions-in-fsharp/ with some examples in https://github.com/mikhailshilkov/azure-functions-fsharp-examples