So this morning I found my self browsing through the images Microsoft has published in the Docker Hub and then I saw this one: microsoft/azure-functions-runtime and decided to Run a Precompiled .NET Core Azure Function in a Container.


Create a .NET Core lib project

Create a .NET Core lib project.

1dotnet new lib --name dni
2cd dni
3rm .\Class1.cs

Add the required references to be able to use Azure Functions API

Be sure to add the following packages to the project. I couldn’t make the sample work with the latest versions of some of the Microsoft.AspNetCore.* packages, so pay special attention to the version parameter.

1dotnet add package Microsoft.AspNetCore.Http -v 2.0.0
2dotnet add package Microsoft.AspNetCore.Mvc -v 2.0.0
3dotnet add package Microsoft.Azure.WebJobs -v 3.0.0-beta4
4dotnet add package Microsoft.Extensions.Primitives -v 2.0.0
5dotnet add package Newtonsoft.Json -v 10.0.3

Modify the Output Path of the project

Edit dni.csproj and the OutputPath property with the following value: wwwroot\bin. The project contents should now look like the following text:

 1<Project Sdk="Microsoft.NET.Sdk">
 2  <PropertyGroup>
 3    <TargetFramework>netstandard2.0</TargetFramework>
 4    <OutputPath>wwwroot\bin</OutputPath>
 5  </PropertyGroup>
 6  <ItemGroup>
 7    <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.0.0" />
 8    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
 9    <PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.0-beta4" />
10    <PackageReference Include="Microsoft.Extensions.Primitives" Version="2.0.0" />
11    <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
12  </ItemGroup>

Create the wwwroot and validate folders

Create two folders. The first one wwwroot will hold all the functions and global configuration. The second one: validate will hold the configuration of the validate function we’ll be deploying.

1md wwwroot
2md wwwroot/validate

Create host.json file in the wwwroot folder

Create a host.json file in the wwwroot folder with the following contents:

1{ }

Create function.json and run.csx files

In the wwwroot/validate folder create a functions.json file with the following contents:

 2  "bindings": [
 3    {
 4      "authLevel": "anonymous",
 5      "name": "req",
 6      "type": "httpTrigger",
 7      "direction": "in"
 8    }
 9  ],
10  "scriptFile": "..//bin//netstandard2.0//dni.dll",
11  "entryPoint": "Dni.HttpTrigger.Run",
12  "disabled": false

Note that in functions.json we are specifying the function trigger type as httpTrigger and also specifying the scriptFile and entryPoint.

Now create a run.csx file with the following contents:

1//leave this file empty - all the logic is placed within our precompiled function

Create an HttpTrigger.cs file to hold your function

Create a HttpTrigger.cs file with the following contents:

 1namespace Dni
 3    using System.Collections.Generic;
 4    using System.Linq;
 5    using Microsoft.AspNetCore.Http;
 6    using Microsoft.AspNetCore.Mvc;
 7    using Microsoft.Azure.WebJobs.Host;
 9    public class HttpTrigger
10    {
11        public static IActionResult Run(HttpRequest req, TraceWriter log)
12        {
13            log.Info("DNI Validation function is processing a request.");
15            string dni = req.Query["dni"];
17            return dni != null
18                ? (ActionResult)new OkObjectResult(ValidateDNI(dni))
19                : new BadRequestObjectResult("Please pass a dni on the query string");
20        }
22        public static bool ValidateDNI(string dni)
23        {
24            var table = "TRWAGMYFPDXBNJZSQVHLCKE";
25            var foreignerDigits = new Dictionary<char, char>()
26        {
27            { 'X', '0'},
28            { 'Y', '1'},
29            { 'Z', '2'}
30        };
31            var numbers = "1234567890";
32            var parsedDNI = dni.ToUpper();
33            if (parsedDNI.Length == 9)
34            {
35                var checkDigit = parsedDNI[8];
36                parsedDNI = parsedDNI.Remove(8);
37                if (foreignerDigits.ContainsKey(parsedDNI[0]))
38                {
39                    parsedDNI = parsedDNI.Replace(parsedDNI[0], foreignerDigits[parsedDNI[0]]);
40                }
42                return parsedDNI.Length == parsedDNI.Where(n => numbers.Contains(n)).Count() &&
43                    table[int.Parse(parsedDNI) % 23] == checkDigit;
44            }
46            return false;
47        }
48    }

Cause I didn’t want to create a simple Hello World sample, this Azure Function Code takes the dni parameter from the query string and validates if the given value is a valid Spanish National Identity Document number.

Create a dockerfile

Create a dockerfile with the following contents:

1FROM microsoft/azure-functions-runtime:v2.0.0-beta1
2ENV AzureWebJobsScriptRoot=/home/site/wwwroot
3ADD wwwroot /home/site/wwwroot

Note that once you execute a docker build on the file all the contents of the wwwroot folder will be copied to the /home/site/wwwroot inside the image.

Build the project and the docker image

Build the .NET Core project so you get a precompiled funtion and then build the docker image:

1dotnet build
2docker build -t dni .

Create a container and test the function

Run the following to run the Azure Function in a container:

1docker run -p 5000:80 dni

Now navigate to: http://localhost:5000/api/validate?dni=54495436H and the page should return true.

You can download all code and files here.

Hope it helps!