scale-tone

teaser

How to deploy your Azure Functions from a NuGet package.

Yes, it is absolutely possible:

The above Azure CLI script creates a new Function App instance and deploys some .Net code into it from a local my-local-nuget-package.nupkg file.

As we all know, a NuGet package is in fact nothing but a ZIP-file containing whatever you put in it, plus some NuGet-related metadata. Change the package file extension from .NUPKG to .ZIP - and you’ll be able to look inside of it with Windows File Explorer.

On the other hand, Azure Functions do support so called Zip Deployment, aka copying compiled binaries and config files from a provided ZIP-file into Function Instance’s wwwroot folder. The same is also very true for ‘classic’ Azure App Services.

Combine the above two knowledges together - and voila, it just works.

Not every NuGet package can be deployed in that way, of course, but only a specially formatted one. E.g. for .Net-based Azure Functions it should contain the output of dotnet publish command, and the way I managed to achieve that (there can be other ways as well) is by having a NUSPEC-file like this:

Call that file e.g. nuspec.nuspec, put it next to your CSPROJ-file and include it into your project by adding the following item to that CSPROJ-file:

<None Update="nuspec.nuspec">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

Then in your project root folder run this:

dotnet publish
nuget pack bin\Debug\netcoreapp2.1\publish\nuspec.nuspec

and a newly created my-nuget-packed-function.1.0.0.nupkg file should appear.

For a Function written in e.g. JavaScript/TypeScript the NUSPEC-file will look a bit different:

(the main difference is that, since this file now resides in the project’s root folder, we’ll need to exclude some stuff that should never be packed).

And then you run:

npm install
npm build
nuget pack nuspec.nuspec

to get your JavaScript/TypeScript Function App packed.

NOTE: if you don’t have NuGet.exe installed, you can get it from here.

Now, ways to trigger the Zip Deployment of your package are actually numerous. Including even via an HTTP POST. But for me the most reasonable thing to do was to use the very similar App Service’s Run from Package feature and combine it with ARM templates, so that my users can avail from that quick and nice Deploy to Azure button to instantly deploy my Function App into their Azure subscriptions. The entire working ARM template would be a bit too long and irrelevant here, so I’ll just show a fragment of it, that sets WEBSITE_RUN_FROM_PACKAGE app setting to the publicly available package download link:

"properties": {
    "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('functionAppName'))]",
    "siteConfig": {
        "appSettings": [
            {
                "name": "WEBSITE_RUN_FROM_PACKAGE",
                "value": "https://www.nuget.org/api/v2/package/my-nuget-packed-function"
            }
            
            ... more settings go here
        ]
}

Just to eliminate (or bring more?) confusion here: Zip Deployment and Run from Package features are very similar yet a bit different.

Indeed, as of today, there’re lots of different ways to deploy your Function App to Azure. It is even possible to have an ARM template, that deploys your custom Docker image or raw source code from your repo. Still, the approach with ARM templates + NuGet feeds might be beneficial in certain cases. E.g. if you want to distribute your Functions in a packed, versioned way, yet still allow them to stay under Consumption Plan (since the custom Docker container+Consumption Plan combination is so far not yet supported).