Home

Getting started with Azure functions

azure dotnet

Back in 2017 or so I seem to remember doing some “Azure Developer” type courses, one of which included writing and deploying an Azure Function. In the last week I have been wrestling with some complex Azure Functions written in C#, targetting dotnet 9.0.

Here are some notes from a Saturday experiment on writing an Azure function, getting it working locally, deploying to Azure Functions using Zip deploy, and configuring for Managed Identity and a ServiceBus trigger.

Notes on my setup

I am working on a Mac Mini, with an M1 (Arm) chip, using Visual Studio. Currently I’m looking to build it using locally-installed binaries, but I will also detail using a Dev Container, and using containers for deployment.

From Develop Azure Functions locally using Core Tools we see:

brew tap azure/functions
brew install azure-functions-core-tools@4
# if upgrading on a machine that has 2.x or 3.x installed:
brew link --overwrite azure-functions-core-tools@4

For me that meant I could run

which func

And see

/opt/homebrew/bin/func

And run:

func --version

Create your local project

The article then suggests

func init MyProjFolder --worker-runtime dotnet-isolated

Note - this is not what I ran - see after the troubleshooting note

No templates or subcommands found matching: ‘func’

I did run into this particular error, as detailed in Issue 4399, and followed the workaround from badsyntax:

dotnet new globaljson

Edit global.json, change sdk version to eg 8.0.203 or 9.0.102

Then I was able to:

func init myproject --worker-runtime dotnet-isolated --docker --target-framework net9.0

Execution model comparison

It is worth referring to Differences between the isolated worker model and the in-process model for .NET on Azure Functions

Support ends for the in-process model on November 10, 2026, so I’m choosing to learn the newer way.

Create new function with HTTP Trigger

cd ~/Developer/personal
mkdir -p funcexperiments
cd funcexperiments
dotnet new globaljson
cat global.json

We see that global.json has

{
  "sdk": {
    "version": "9.0.300"
  }
}

Edit it to be

{
  "sdk": {
    "version": "9.0.102"
  }
}

Run

func new --template "Http Trigger" --name MyHttpTrigger  --worker-runtime dotnet-isolated --docker --target-framework net9.0

tree gives us:

.
├── Dockerfile
├── funcexperiments.csproj
├── global.json
├── host.json
├── local.settings.json
├── MyHttpTrigger.cs
├── obj
│   ├── funcexperiments.csproj.nuget.dgspec.json
│   ├── funcexperiments.csproj.nuget.g.props
│   ├── funcexperiments.csproj.nuget.g.targets
│   ├── project.assets.json
│   └── project.nuget.cache
├── Program.cs
└── Properties
    └── launchSettings.json

If we use code2prompt with an exclude glob pattern code2prompt ./ --exclude="**/obj/*", it honours .gitignore and gives us:

Project Path: funcexperiments

Source Tree:

funcexperiments
├── Dockerfile
├── MyHttpTrigger.cs
├── Program.cs
├── Properties
│   └── launchSettings.json
├── funcexperiments.csproj
├── global.json
├── host.json
├── local.settings.json
└── obj

funcexperiments/Dockerfile:

FROM mcr.microsoft.com/dotnet/sdk:9.0-preview AS installer-env

COPY . /src/dotnet-function-app
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwroot

# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0-appservice
FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]

funcexperiments/MyHttpTrigger.cs:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace funcexperiments;

public class MyHttpTrigger
{
    private readonly ILogger<MyHttpTrigger> _logger;

    public MyHttpTrigger(ILogger<MyHttpTrigger> logger)
    {
        _logger = logger;
    }

    [Function("MyHttpTrigger")]
    public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
    {
        _logger.LogInformation("C# HTTP trigger function processed a request.");
        return new OkObjectResult("Welcome to Azure Functions!");
    }
}

funcexperiments/Program.cs:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Services
    .AddApplicationInsightsTelemetryWorkerService()
    .ConfigureFunctionsApplicationInsights();

builder.Build().Run();

funcexperiments/Properties/launchSettings.json:

{
  "profiles": {
    "funcexperiments": {
      "commandName": "Project",
      "commandLineArgs": "--port 7106",
      "launchBrowser": false
    }
  }
}

funcexperiments/funcexperiments.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <OutputType>Exe</OutputType>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.2" />
  </ItemGroup>
</Project>

funcexperiments/global.json:

{
  "sdk": {
    "version": "9.0.102"
  }
}

funcexperiments/host.json:

{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      },
      "enableLiveMetricsFilters": true
    }
  }
}

funcexperiments/local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
  }
}

Running func start looks to do several things, including a dotnet build, seen by the presence of the ./bin directory

  Determining projects to restore...
  All projects are up-to-date for restore.
  Determining projects to restore...
  Restored /Users/nickromney/Developer/personal/funcexperiments/obj/Debug/net9.0/WorkerExtensions/WorkerExtensions.csproj (in 2.14 sec).
  WorkerExtensions -> /Users/nickromney/Developer/personal/funcexperiments/obj/Debug/net9.0/WorkerExtensions/buildout/Microsoft.Azure.Functions.Worker.Extensions.dll
  funcexperiments -> /Users/nickromney/Developer/personal/funcexperiments/bin/output/funcexperiments.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:07.25



Azure Functions Core Tools
Core Tools Version:       4.0.7317 Commit hash: N/A +5ca56d37938824531b691f094d0a77fd6f51af20 (64-bit)
Function Runtime Version: 4.1038.300.25164

[2025-05-31T15:04:54.667Z] Found /Users/nickromney/Developer/personal/funcexperiments/funcexperiments.csproj. Using for user secrets file configuration.
[2025-05-31T15:04:55.919Z] Worker process started and initialized.

Functions:

        MyHttpTrigger: [GET,POST] http://localhost:7071/api/MyHttpTrigger

For detailed output, run func with --verbose flag.

If we go to the URL in a browser, we see “Welcome to Azure Functions!”, and in the terminal, evidence that the function has been triggered by an HTTP call:

[2025-05-31T15:06:24.375Z] Executing 'Functions.MyHttpTrigger' (Reason='This function was programmatically called via the host APIs.', Id=696ed6e9-5598-4b4c-adf9-1c1c77282a5c)
[2025-05-31T15:06:24.516Z] C# HTTP trigger function processed a request.
[2025-05-31T15:06:24.519Z] Executing OkObjectResult, writing value of type 'System.String'.
[2025-05-31T15:06:24.549Z] Executed 'Functions.MyHttpTrigger' (Succeeded, Id=696ed6e9-5598-4b4c-adf9-1c1c77282a5c, Duration=189ms)

As suggested, let’s run with func start --verbose

 Determining projects to restore...
  All projects are up-to-date for restore.
  Determining projects to restore...
  All projects are up-to-date for restore.
  WorkerExtensions -> /Users/nickromney/Developer/personal/funcexperiments/obj/Debug/net9.0/WorkerExtensions/buildout/Microsoft.Azure.Functions.Worker.Extensions.dll
  funcexperiments -> /Users/nickromney/Developer/personal/funcexperiments/bin/output/funcexperiments.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.31


Selected out-of-process host.

                  %%%%%%
                 %%%%%%
            @   %%%%%%    @
          @@   %%%%%%      @@
       @@@    %%%%%%%%%%%    @@@
     @@      %%%%%%%%%%        @@
       @@         %%%%       @@
         @@      %%%       @@
           @@    %%      @@
                %%
                %


Azure Functions Core Tools
Core Tools Version:       4.0.7317 Commit hash: N/A +5ca56d37938824531b691f094d0a77fd6f51af20 (64-bit)
Function Runtime Version: 4.1038.300.25164

[2025-05-31T15:10:56.328Z] Found /Users/nickromney/Developer/personal/funcexperiments/funcexperiments.csproj. Using for user secrets file configuration.
[2025-05-31T15:10:56.594Z] Building host: version spec: , startup suppressed: 'False', configuration suppressed: 'False', startup operation id: '3a78566e-c2fc-4686-bb2f-1f7ed72ef11a'
[2025-05-31T15:10:56.620Z] Reading host configuration file '/Users/nickromney/Developer/personal/funcexperiments/bin/output/host.json'
[2025-05-31T15:10:56.621Z] Host configuration file read:
[2025-05-31T15:10:56.621Z] {
[2025-05-31T15:10:56.621Z]   "version": "2.0",
[2025-05-31T15:10:56.621Z]   "logging": {
[2025-05-31T15:10:56.621Z]     "applicationInsights": {
[2025-05-31T15:10:56.621Z]       "samplingSettings": {
[2025-05-31T15:10:56.621Z]         "isEnabled": true,
[2025-05-31T15:10:56.621Z]         "excludedTypes": "Request"
[2025-05-31T15:10:56.621Z]       },
[2025-05-31T15:10:56.621Z]       "enableLiveMetricsFilters": true
[2025-05-31T15:10:56.621Z]     }
[2025-05-31T15:10:56.621Z]   }
[2025-05-31T15:10:56.621Z] }
[2025-05-31T15:10:56.637Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: python
[2025-05-31T15:10:56.646Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: java
[2025-05-31T15:10:56.646Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: powershell
[2025-05-31T15:10:56.647Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: node
[2025-05-31T15:10:56.652Z] Extension Bundle not loaded. Loading extensions from /Users/nickromney/Developer/personal/funcexperiments/bin/output. BundleConfigured: False, PrecompiledFunctionApp: False, LegacyBundle: False, DotnetIsolatedApp: True, isLogicApp: False
[2025-05-31T15:10:56.652Z] Script Startup resetting load context with base path: '/Users/nickromney/Developer/personal/funcexperiments/bin/output/.azurefunctions'.
[2025-05-31T15:10:56.654Z] Loading startup extension 'Startup'
[2025-05-31T15:10:56.678Z] Loaded extension 'Startup' (1.0.0.0)
[2025-05-31T15:10:56.687Z] Reading host configuration file '/Users/nickromney/Developer/personal/funcexperiments/bin/output/host.json'
[2025-05-31T15:10:56.687Z] Host configuration file read:
[2025-05-31T15:10:56.687Z] {
[2025-05-31T15:10:56.687Z]   "version": "2.0",
[2025-05-31T15:10:56.687Z]   "logging": {
[2025-05-31T15:10:56.687Z]     "applicationInsights": {
[2025-05-31T15:10:56.687Z]       "samplingSettings": {
[2025-05-31T15:10:56.687Z]         "isEnabled": true,
[2025-05-31T15:10:56.687Z]         "excludedTypes": "Request"
[2025-05-31T15:10:56.687Z]       },
[2025-05-31T15:10:56.687Z]       "enableLiveMetricsFilters": true
[2025-05-31T15:10:56.687Z]     }
[2025-05-31T15:10:56.687Z]   }
[2025-05-31T15:10:56.687Z] }
[2025-05-31T15:10:56.711Z] Starting host metrics provider.
[2025-05-31T15:10:56.774Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: python
[2025-05-31T15:10:56.774Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: java
[2025-05-31T15:10:56.775Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: powershell
[2025-05-31T15:10:56.775Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: node
[2025-05-31T15:10:56.776Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: python
[2025-05-31T15:10:56.776Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: java
[2025-05-31T15:10:56.776Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: powershell
[2025-05-31T15:10:56.776Z] FUNCTIONS_WORKER_RUNTIME set to dotnet-isolated. Skipping WorkerConfig for language: node
[2025-05-31T15:10:56.802Z] Initializing Warmup Extension.
[2025-05-31T15:10:56.825Z] Initializing Host. OperationId: '3a78566e-c2fc-4686-bb2f-1f7ed72ef11a'.
[2025-05-31T15:10:56.829Z] Host initialization: ConsecutiveErrors=0, StartupCount=1, OperationId=3a78566e-c2fc-4686-bb2f-1f7ed72ef11a
[2025-05-31T15:10:56.844Z] Loading functions metadata
[2025-05-31T15:10:56.845Z] Worker indexing is enabled
[2025-05-31T15:10:56.846Z] Fetching metadata for workerRuntime: dotnet-isolated
[2025-05-31T15:10:56.846Z] Reading functions metadata (Worker)
[2025-05-31T15:10:57.213Z] {
[2025-05-31T15:10:57.213Z]   "ProcessId": 57263,
[2025-05-31T15:10:57.213Z]   "RuntimeIdentifier": "osx-arm64",
[2025-05-31T15:10:57.213Z]   "WorkerVersion": "2.0.0.0",
[2025-05-31T15:10:57.213Z]   "ProductVersion": "2.0.0\u002Bd8b5fe998a8c92819b8ee41d2569d2525413e9c5",
[2025-05-31T15:10:57.213Z]   "FrameworkDescription": ".NET 9.0.5",
[2025-05-31T15:10:57.213Z]   "OSDescription": "Darwin 24.5.0 Darwin Kernel Version 24.5.0: Tue Apr 22 19:48:46 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T8103",
[2025-05-31T15:10:57.213Z]   "OSArchitecture": "Arm64",
[2025-05-31T15:10:57.213Z]   "CommandLine": "/Users/nickromney/Developer/personal/funcexperiments/bin/output/funcexperiments.dll --host 127.0.0.1 --port 52378 --workerId a07e9554-9a4f-4638-9936-b0e6ec4d8ff0 --requestId f1987092-4b4c-4b11-9bf7-6bb04db04ca1 --grpcMaxMessageLength 2147483647 --functions-uri http://127.0.0.1:52378/ --functions-worker-id a07e9554-9a4f-4638-9936-b0e6ec4d8ff0 --functions-request-id f1987092-4b4c-4b11-9bf7-6bb04db04ca1 --functions-grpc-max-message-length 2147483647"
[2025-05-31T15:10:57.213Z] }
[2025-05-31T15:10:57.312Z] 1 functions found (Worker)
[2025-05-31T15:10:57.317Z] Reading functions metadata (Custom)
[2025-05-31T15:10:57.321Z] 0 functions found (Custom)
[2025-05-31T15:10:57.324Z] 1 functions loaded
[2025-05-31T15:10:57.326Z] LoggerFilterOptions
[2025-05-31T15:10:57.326Z] {
[2025-05-31T15:10:57.326Z]   "MinLevel": "None",
[2025-05-31T15:10:57.326Z]   "Rules": [
[2025-05-31T15:10:57.326Z]     {
[2025-05-31T15:10:57.326Z]       "ProviderName": null,
[2025-05-31T15:10:57.326Z]       "CategoryName": "Microsoft.Hosting.Lifetime",
[2025-05-31T15:10:57.326Z]       "LogLevel": "None",
[2025-05-31T15:10:57.326Z]       "Filter": null
[2025-05-31T15:10:57.326Z]     },
[2025-05-31T15:10:57.326Z]     {
[2025-05-31T15:10:57.326Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2025-05-31T15:10:57.326Z]       "CategoryName": null,
[2025-05-31T15:10:57.326Z]       "LogLevel": "None",
[2025-05-31T15:10:57.326Z]       "Filter": null
[2025-05-31T15:10:57.326Z]     },
[2025-05-31T15:10:57.326Z]     {
[2025-05-31T15:10:57.326Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2025-05-31T15:10:57.326Z]       "CategoryName": null,
[2025-05-31T15:10:57.326Z]       "LogLevel": null,
[2025-05-31T15:10:57.326Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.326Z]     },
[2025-05-31T15:10:57.326Z]     {
[2025-05-31T15:10:57.326Z]       "ProviderName": null,
[2025-05-31T15:10:57.326Z]       "CategoryName": null,
[2025-05-31T15:10:57.326Z]       "LogLevel": null,
[2025-05-31T15:10:57.326Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.326Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": null,
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": null,
[2025-05-31T15:10:57.327Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": "None",
[2025-05-31T15:10:57.327Z]       "Filter": null
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": null,
[2025-05-31T15:10:57.327Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": null,
[2025-05-31T15:10:57.327Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.327Z]     }
[2025-05-31T15:10:57.327Z]   ]
[2025-05-31T15:10:57.327Z] }
[2025-05-31T15:10:57.327Z] LoggerFilterOptions
[2025-05-31T15:10:57.327Z] {
[2025-05-31T15:10:57.327Z]   "MinLevel": "None",
[2025-05-31T15:10:57.327Z]   "Rules": [
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": null,
[2025-05-31T15:10:57.327Z]       "CategoryName": "Microsoft.Hosting.Lifetime",
[2025-05-31T15:10:57.327Z]       "LogLevel": "None",
[2025-05-31T15:10:57.327Z]       "Filter": null
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": "None",
[2025-05-31T15:10:57.327Z]       "Filter": null
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": null,
[2025-05-31T15:10:57.327Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": null,
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": null,
[2025-05-31T15:10:57.327Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": null,
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": null,
[2025-05-31T15:10:57.327Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": "None",
[2025-05-31T15:10:57.327Z]       "Filter": null
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2025-05-31T15:10:57.327Z]       "CategoryName": null,
[2025-05-31T15:10:57.327Z]       "LogLevel": null,
[2025-05-31T15:10:57.327Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.327Z]     },
[2025-05-31T15:10:57.327Z]     {
[2025-05-31T15:10:57.327Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2025-05-31T15:10:57.328Z]       "CategoryName": null,
[2025-05-31T15:10:57.328Z]       "LogLevel": null,
[2025-05-31T15:10:57.328Z]       "Filter": "<AddFilter>b__0"
[2025-05-31T15:10:57.328Z]     }
[2025-05-31T15:10:57.328Z]   ]
[2025-05-31T15:10:57.328Z] }
[2025-05-31T15:10:57.328Z] LanguageWorkerOptions
[2025-05-31T15:10:57.328Z] {
[2025-05-31T15:10:57.328Z]   "WorkerConfigs": [
[2025-05-31T15:10:57.328Z]     {
[2025-05-31T15:10:57.328Z]       "Description": {
[2025-05-31T15:10:57.328Z]         "Language": "dotnet-isolated",
[2025-05-31T15:10:57.328Z]         "DefaultRuntimeName": null,
[2025-05-31T15:10:57.328Z]         "DefaultRuntimeVersion": null,
[2025-05-31T15:10:57.328Z]         "SupportedArchitectures": null,
[2025-05-31T15:10:57.328Z]         "SupportedOperatingSystems": null,
[2025-05-31T15:10:57.328Z]         "SupportedRuntimeVersions": null,
[2025-05-31T15:10:57.328Z]         "SanitizeRuntimeVersionRegex": null,
[2025-05-31T15:10:57.328Z]         "WorkerIndexing": "true",
[2025-05-31T15:10:57.328Z]         "Extensions": [
[2025-05-31T15:10:57.328Z]           ".dll"
[2025-05-31T15:10:57.328Z]         ],
[2025-05-31T15:10:57.328Z]         "UseStdErrorStreamForErrorsOnly": false,
[2025-05-31T15:10:57.328Z]         "DefaultExecutablePath": "dotnet",
[2025-05-31T15:10:57.328Z]         "DefaultWorkerPath": "/Users/nickromney/Developer/personal/funcexperiments/bin/output/funcexperiments.dll",
[2025-05-31T15:10:57.328Z]         "WorkerDirectory": "/Users/nickromney/Developer/personal/funcexperiments/bin/output",
[2025-05-31T15:10:57.328Z]         "Arguments": [],
[2025-05-31T15:10:57.328Z]         "WorkerArguments": null,
[2025-05-31T15:10:57.328Z]         "IsDisabled": null
[2025-05-31T15:10:57.328Z]       },
[2025-05-31T15:10:57.328Z]       "Arguments": {
[2025-05-31T15:10:57.328Z]         "ExecutablePath": "dotnet",
[2025-05-31T15:10:57.328Z]         "ExecutableArguments": [],
[2025-05-31T15:10:57.328Z]         "WorkerPath": "/Users/nickromney/Developer/personal/funcexperiments/bin/output/funcexperiments.dll",
[2025-05-31T15:10:57.328Z]         "WorkerArguments": []
[2025-05-31T15:10:57.328Z]       },
[2025-05-31T15:10:57.328Z]       "CountOptions": {
[2025-05-31T15:10:57.328Z]         "SetProcessCountToNumberOfCpuCores": false,
[2025-05-31T15:10:57.328Z]         "ProcessCount": 1,
[2025-05-31T15:10:57.328Z]         "MaxProcessCount": 10,
[2025-05-31T15:10:57.328Z]         "ProcessStartupInterval": "00:00:10",
[2025-05-31T15:10:57.328Z]         "ProcessStartupTimeout": "00:01:00",
[2025-05-31T15:10:57.328Z]         "InitializationTimeout": "00:00:10",
[2025-05-31T15:10:57.328Z]         "EnvironmentReloadTimeout": "00:00:30",
[2025-05-31T15:10:57.328Z]         "ProcessRestartInterval": "00:00:10",
[2025-05-31T15:10:57.328Z]         "ProcessShutdownTimeout": "00:00:10"
[2025-05-31T15:10:57.328Z]       }
[2025-05-31T15:10:57.328Z]     }
[2025-05-31T15:10:57.328Z]   ]
[2025-05-31T15:10:57.328Z] }
[2025-05-31T15:10:57.328Z] FunctionResultAggregatorOptions
[2025-05-31T15:10:57.328Z] {
[2025-05-31T15:10:57.328Z]   "BatchSize": 1000,
[2025-05-31T15:10:57.328Z]   "FlushTimeout": "00:00:30",
[2025-05-31T15:10:57.328Z]   "IsEnabled": true
[2025-05-31T15:10:57.328Z] }
[2025-05-31T15:10:57.328Z] ConcurrencyOptions
[2025-05-31T15:10:57.328Z] {
[2025-05-31T15:10:57.328Z]   "DynamicConcurrencyEnabled": false,
[2025-05-31T15:10:57.328Z]   "MaximumFunctionConcurrency": 500,
[2025-05-31T15:10:57.328Z]   "CPUThreshold": 0.8,
[2025-05-31T15:10:57.328Z]   "SnapshotPersistenceEnabled": true
[2025-05-31T15:10:57.328Z] }
[2025-05-31T15:10:57.328Z] SingletonOptions
[2025-05-31T15:10:57.328Z] {
[2025-05-31T15:10:57.329Z]   "LockPeriod": "00:00:15",
[2025-05-31T15:10:57.329Z]   "ListenerLockPeriod": "00:00:15",
[2025-05-31T15:10:57.329Z]   "LockAcquisitionTimeout": "10675199.02:48:05.4775807",
[2025-05-31T15:10:57.329Z]   "LockAcquisitionPollingInterval": "00:00:05",
[2025-05-31T15:10:57.329Z]   "ListenerLockRecoveryPollingInterval": "00:01:00"
[2025-05-31T15:10:57.329Z] }
[2025-05-31T15:10:57.329Z] ScaleOptions
[2025-05-31T15:10:57.329Z] {
[2025-05-31T15:10:57.329Z]   "ScaleMetricsMaxAge": "00:02:00",
[2025-05-31T15:10:57.329Z]   "ScaleMetricsSampleInterval": "00:00:10",
[2025-05-31T15:10:57.329Z]   "MetricsPurgeEnabled": true,
[2025-05-31T15:10:57.329Z]   "IsTargetScalingEnabled": true,
[2025-05-31T15:10:57.329Z]   "IsRuntimeScalingEnabled": false
[2025-05-31T15:10:57.329Z] }
[2025-05-31T15:10:57.329Z] Starting JobHost
[2025-05-31T15:10:57.331Z] Starting Host (HostId=nicksmacmini-272155989, InstanceId=55cd622a-3a57-4922-b0e1-330876900dc6, Version=4.1038.300.25164, ProcessId=57223, AppDomainId=1, InDebugMode=False, InDiagnosticMode=False, FunctionsExtensionVersion=(null))
[2025-05-31T15:10:57.350Z] Generating 1 job function(s)
[2025-05-31T15:10:57.350Z] Worker process started and initialized.
[2025-05-31T15:10:57.368Z] Found the following functions:
[2025-05-31T15:10:57.368Z] Host.Functions.MyHttpTrigger
[2025-05-31T15:10:57.368Z]
[2025-05-31T15:10:57.372Z] HttpOptions
[2025-05-31T15:10:57.372Z] {
[2025-05-31T15:10:57.372Z]   "DynamicThrottlesEnabled": false,
[2025-05-31T15:10:57.372Z]   "EnableChunkedRequestBinding": false,
[2025-05-31T15:10:57.372Z]   "MaxConcurrentRequests": -1,
[2025-05-31T15:10:57.372Z]   "MaxOutstandingRequests": -1,
[2025-05-31T15:10:57.372Z]   "RoutePrefix": "api"
[2025-05-31T15:10:57.372Z] }
[2025-05-31T15:10:57.372Z] Initializing function HTTP routes
[2025-05-31T15:10:57.372Z] Mapped function route 'api/MyHttpTrigger' [get,post] to 'MyHttpTrigger'
[2025-05-31T15:10:57.372Z]
[2025-05-31T15:10:57.375Z] Host initialized (40ms)
[2025-05-31T15:10:57.376Z] Host started (43ms)
[2025-05-31T15:10:57.376Z] Job host started

Functions:

        MyHttpTrigger: [GET,POST] http://localhost:7071/api/MyHttpTrigger

Then if we go to the local URL, the logs will show:


[2025-05-31T15:12:14.629Z] Executing HTTP request: {
[2025-05-31T15:12:14.629Z]   "requestId": "03406dca-26f9-46e3-9aa3-b1087f1c7a39",
[2025-05-31T15:12:14.629Z]   "method": "GET",
[2025-05-31T15:12:14.629Z]   "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
[2025-05-31T15:12:14.629Z]   "uri": "/api/MyHttpTrigger"
[2025-05-31T15:12:14.629Z] }
[2025-05-31T15:12:14.733Z] Executing 'Functions.MyHttpTrigger' (Reason='This function was programmatically called via the host APIs.', Id=b78bab00-1013-4801-b3ea-06ec142b200b)
[2025-05-31T15:12:14.820Z] C# HTTP trigger function processed a request.
[2025-05-31T15:12:14.823Z] Executing OkObjectResult, writing value of type 'System.String'.
[2025-05-31T15:12:14.847Z] Executed 'Functions.MyHttpTrigger' (Succeeded, Id=b78bab00-1013-4801-b3ea-06ec142b200b, Duration=126ms)
[2025-05-31T15:12:14.853Z] Executed HTTP request: {
[2025-05-31T15:12:14.853Z]   "requestId": "03406dca-26f9-46e3-9aa3-b1087f1c7a39",
[2025-05-31T15:12:14.853Z]   "identities": "(WebJobsAuthLevel:Admin)",
[2025-05-31T15:12:14.853Z]   "status": "200",
[2025-05-31T15:12:14.853Z]   "duration": "224"
[2025-05-31T15:12:14.853Z] }

Building the container locally

The generated Dockerfile was

FROM mcr.microsoft.com/dotnet/sdk:9.0-preview AS installer-env

COPY . /src/dotnet-function-app
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwroot

# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0-appservice
FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]

Normally we would expect to be able to build with

docker build --tag func:v1.0.0 .

But if we do that with our global.json in place we get this output

 => ERROR [3/3] RUN cd /src/dotnet-function-app && mkdir -p /home/site/wwwroot && dotnet publish *.csproj --output /home/site/wwwroot                                             0.1s
------
 > [3/3] RUN cd /src/dotnet-function-app && mkdir -p /home/site/wwwroot && dotnet publish *.csproj --output /home/site/wwwroot:
0.045 The command could not be loaded, possibly because:
0.045   * You intended to execute a .NET application:
0.045       The application 'publish' does not exist.
0.045   * You intended to execute a .NET SDK command:
0.045       A compatible .NET SDK was not found.
0.045
0.045 Requested SDK version: 9.0.102
0.045 global.json file: /src/dotnet-function-app/global.json
0.045
0.045 Installed SDKs:
0.045
0.045 Install the [9.0.102] .NET SDK or update [/src/dotnet-function-app/global.json] to match an installed SDK.
0.045
0.045 Learn about SDK resolution:
0.045 https://aka.ms/dotnet/sdk-not-found
0.045 9.0.100-preview.7.24407.12 [/usr/share/dotnet/sdk]

If we remove the global.json and rebuild we “fail further”, with:

[+] Building 27.4s (10/10) FINISHED                                                                                                                           docker-container:default
 => [internal] load build definition from Dockerfile                                                                                                                              0.0s
 => => transferring dockerfile: 729B                                                                                                                                              0.0s
 => [internal] load metadata for mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0                                                                           0.2s
 => [internal] load metadata for mcr.microsoft.com/dotnet/sdk:9.0-preview                                                                                                         0.2s
 => [internal] load .dockerignore                                                                                                                                                 0.0s
 => => transferring context: 2B                                                                                                                                                   0.0s
 => CACHED [stage-1 1/2] FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0@sha256:74b8891e7e2cef34da7037c33f07480dc0c1f417b69b8be251a6ecc2af1bf9ea      0.0s
 => => resolve mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0@sha256:74b8891e7e2cef34da7037c33f07480dc0c1f417b69b8be251a6ecc2af1bf9ea                     0.0s
 => [internal] load build context                                                                                                                                                 0.0s
 => => transferring context: 56.89kB                                                                                                                                              0.0s
 => CACHED [installer-env 1/3] FROM mcr.microsoft.com/dotnet/sdk:9.0-preview@sha256:241df9f9fd6365ed075ffcf8db22e16276683feed4cc1caa6c3a4a631a38e5ae                              0.0s
 => => resolve mcr.microsoft.com/dotnet/sdk:9.0-preview@sha256:241df9f9fd6365ed075ffcf8db22e16276683feed4cc1caa6c3a4a631a38e5ae                                                   0.0s
 => [installer-env 2/3] COPY . /src/dotnet-function-app                                                                                                                           0.3s
 => [installer-env 3/3] RUN cd /src/dotnet-function-app && mkdir -p /home/site/wwwroot && dotnet publish *.csproj --output /home/site/wwwroot                                    26.8s
 => [stage-1 2/2] COPY --from=installer-env [/home/site/wwwroot, /home/site/wwwroot]                                                                                              0.0s
WARNING: No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load

View build details: docker-desktop://dashboard/build/default/default/vt0isz41yiscuuqov20uj060u

 1 warning found (use docker --debug to expand):
 - InvalidBaseImagePlatform: Base image mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0 was pulled with platform "linux/amd64", expected "linux/arm64" for current build (line 10)

There is a discussion on the azure-funtions-docker project as to some workarounds

Passing the architecture looked to be almost there:

docker build --platform linux/amd64 --tag funcasb:v1.0.0 .

But failed towards the end with a QEMU issue:

 => ERROR [installer-env 3/3] RUN cd /src/dotnet-function-app && mkdir -p /home/site/wwwroot && dotnet publish *.csproj --output /home/site/wwwroot                               0.0s
------
 > [installer-env 3/3] RUN cd /src/dotnet-function-app && mkdir -p /home/site/wwwroot && dotnet publish *.csproj --output /home/site/wwwroot:
------
WARNING: No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load
Dockerfile:4
--------------------
   3 |     COPY . /src/dotnet-function-app
   4 | >>> RUN cd /src/dotnet-function-app && \
   5 | >>> mkdir -p /home/site/wwwroot && \
   6 | >>> dotnet publish *.csproj --output /home/site/wwwroot
   7 |
--------------------
ERROR: failed to solve: process "/dev/.buildkit_qemu_emulator /bin/sh -c cd /src/dotnet-function-app && mkdir -p /home/site/wwwroot && dotnet publish *.csproj --output /home/site/wwwroot" did not complete successfully: failed to copy xattrs: failed to set xattr "security.selinux" on /tmp/buildkit-qemu-emulator2001177974/dev/.buildkit_qemu_emulator: operation not supported

However, Podman had better support for QEMU:

podman build --platform linux/amd64 --tag funcasb:v1.0.0 .

This completed with

[1/2] STEP 1/3: FROM mcr.microsoft.com/dotnet/sdk:9.0-preview AS installer-env
Trying to pull mcr.microsoft.com/dotnet/sdk:9.0-preview...
Getting image source signatures
Copying blob sha256:f88cdf002049cb0e158db0a0e3924bc51fc6b8c55146f271da8884206b4fec5b
Copying blob sha256:17b1ebe9914e37626333b0bd99a0aadef524a569838764c2fd07a298a26fbed5
Copying blob sha256:5d622b182d9795def4b9b26079c10f7bf9894bf3711dbc3ff38faf179c0036bd
Copying blob sha256:24f7a1fc2b4940975fa942d9015fba6f9784bd20995f00dae48048fc9a3bb5fd
Copying blob sha256:a2318d6c47ec9cac5acc500c47c79602bcf953cec711a18bc898911a0984365b
Copying blob sha256:340611782603ab0bb45d9846e3673973c1d2cd331b21ac1daa6e779dcd57df9a
Copying blob sha256:608c1aabfc3b8f5ad333e1cb9cda0df8e35d9f130d50d942673b0ba2ab197eb2
Copying blob sha256:389d96c749ff94f89d4b59589a13f9ae111b74684ed4874f48cec02d955e2686
Copying blob sha256:7ed5695080334cf8d13b3e53a6174012a3aacb27a9209a4859e055796086853c
Copying config sha256:cb0d8de1b4ea298e62f149de92c56f1fbb80c9eb9ba03976ac206e2371c55c0d
Writing manifest to image destination
[1/2] STEP 2/3: COPY . /src/dotnet-function-app
--> 94a8c15faed9
[1/2] STEP 3/3: RUN cd /src/dotnet-function-app && mkdir -p /home/site/wwwroot && dotnet publish *.csproj --output /home/site/wwwroot
  Determining projects to restore...
  Restored /src/dotnet-function-app/MyFunctionApp.csproj (in 21.84 sec).
/usr/share/dotnet/sdk/9.0.100-preview.7.24407.12/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.RuntimeIdentifierInference.targets(326,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [/src/dotnet-function-app/MyFunctionApp.csproj]
  Determining projects to restore...
  Restored /src/dotnet-function-app/obj/Release/net9.0/WorkerExtensions/WorkerExtensions.csproj (in 10.59 sec).
  WorkerExtensions -> /src/dotnet-function-app/obj/Release/net9.0/WorkerExtensions/buildout/Microsoft.Azure.Functions.Worker.Extensions.dll
  MyFunctionApp -> /src/dotnet-function-app/bin/Release/net9.0/MyFunctionApp.dll
  MyFunctionApp -> /home/site/wwwroot/
--> 8b5f1b2b297b
[2/2] STEP 1/3: FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0
Trying to pull mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated9.0...
Getting image source signatures
Copying blob sha256:54a79b02e795174b13f99931f8a31f401464ac945ff67b7bef0d9fe4d70634d2
Copying blob sha256:0022984bc3f7a6df0ffd4ce42695a3dab09977f03ef67590b0e6fad03d6b6678
Copying blob sha256:61320b01ae5e0798393ef25f2dc72faf43703e60ba089b07d7170acbabbf8f62
Copying blob sha256:6a8895a0ba52c740865875fa34dbc2a3b09a5c89e6cf3479d3b2175f842c2fcd
Copying blob sha256:79714b2f18d502816627610458cbad07b53d7d723dae1cd29fa3dec387af12c3
Copying blob sha256:152867e67c592cf36ffe546b8ab320a4fd64a8f82b596be7bb339f0103fe4f30
Copying blob sha256:2568c5dff37adcf7bbffa5d7bf722eb67ac1a5c76bdc22e1a3b8c5c39d2685ac
Copying blob sha256:5f69367816298655177482e58b82feeba0ce322980149700020b7e2f1f08b80a
Copying blob sha256:32eb6ffc2fca4ce298a77b9e614c9f369b02b2f0b1c282fa9da3ad90d039a5cd
Copying blob sha256:b68abd424bd7cda8a9c7f575f5c57be2d670e5ab359355c8f0f506f1999e9fa5
Copying blob sha256:8b2f93b3e03bd3a9f1fd0c88cc45e2178b14369ec7c4b33046d30cb45a8a7baa
Copying blob sha256:9a9e47a92bc1738f782d2e950d337b36285c2dc5563e40cbe74d73063b0e5ead
Copying blob sha256:21b826a6f5a943227cf0208e920310b670ebd9ed8367add74e5ab8ac36b4baf0
Copying blob sha256:bb06343686edddf8dabae4f1f5a70fb69ced0b07ded7710a43152b1fe62630eb
Copying blob sha256:859a95522ef522c9529c5057f8c4cbe3597a5ce3495d8e879200f47322e2d342
Copying blob sha256:fe7824f4e14fd420ece4139267b2bd1e7feae5bb7344a57eeb4d8acb45d42b9c
Copying config sha256:19720b3c57f1ffe98e39619bb712931b2ddea441675730a9170673a4b7b1790a
Writing manifest to image destination
[2/2] STEP 2/3: ENV AzureWebJobsScriptRoot=/home/site/wwwroot     AzureFunctionsJobHost__Logging__Console__IsEnabled=true
--> 932f0bfa5e13
[2/2] STEP 3/3: COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]
[2/2] COMMIT funcasb:v1.0.0
--> f8e02ec11569
Successfully tagged localhost/funcasb:v1.0.0
f8e02ec115691f3b73b0ac85e0e8972b971c1817530565d28db0ae78b2bdf7f7