In this video, Erik explores a new feature introduced in Business Central 2022 Wave 2: the ability to call Azure Functions directly from AL code. With minimal documentation available at the time, he dives into the system application source code, figures out how the new modules work, and successfully calls an Azure Function from a Business Central extension — all in real time.
Background: What’s New in 2022 Wave 2?
Among the new features listed for Business Central 2022 Wave 2 was the ability to connect to Azure Functions from AL code. The official documentation mentioned support for both GET and POST requests, but beyond that, details were scarce. There was no README in the system application’s Azure Functions folder, no test codeunits to reference, and no community content covering the topic.
Erik decided to investigate the feature hands-on, working directly from the system application source code and the available IntelliSense in VS Code.
The System Application Modules
Digging into the system application, Erik found the following objects in the Azure Functions module:
- Azure Functions — the main codeunit with
SendGetRequestandSendPostRequestmethods - Azure Functions Implementation — an internal codeunit containing the actual HTTP logic
- Azure Functions Authentication — a codeunit responsible for creating authentication instances
- Azure Functions Authentication (Interface) — an interface that abstracts authentication methods
- Azure Functions Response — a codeunit for inspecting the result of a call
Two authentication strategies are supported: Code Authentication (using an Azure Function host key) and OAuth 2.0. Erik chose code authentication for this demo since he had a function key readily available.
Setting Up the Azure Function
Before diving into AL, Erik created a basic Azure Function using the default template in Visual Studio. The function is called Function1, accepts both GET and POST requests, and expects a name query parameter. It returns a simple greeting: “Hello, {name}. This HTTP triggered function executed successfully.”
The function was deployed to an Azure Function App called callmefromal. The endpoint follows the standard Azure Functions URL pattern: https://{app-name}.azurewebsites.net/api/{function-name}.
To obtain the authentication code, Erik navigated to the Azure Portal, opened the Function App, went to App Keys, and copied a host key to use as the code authentication parameter.
Writing the AL Code
Here’s the complete AL code that successfully calls the Azure Function:
pageextension 50100 CustomerListExt extends "Customer List"
{
trigger OnOpenPage();
var
AzureAuth: Codeunit "Azure Functions Authentication";
AzureFunc: Codeunit "Azure Functions";
Auth: Interface "Azure Functions Authentication";
Response: Codeunit "Azure Functions Response";
Query: Dictionary of [Text, Text];
Result: Text;
begin
Auth := AzureAuth.CreateCodeAuth(
'https://callmefromal20221106104102.azurewebsites.net/api/Function1',
'1tz26BtEaDtNpASSQp8bLu0QjEwCmD8menaQogWJbm-8AzFu1O94kQ==');
Query.Add('name', 'Erik');
Response := AzureFunc.SendGetRequest(Auth, Query);
Response.GetResultAsText(Result);
message('%1\%2\%3', Response.IsSuccessful(), Response.GetError(), Result);
end;
}
Let’s walk through what’s happening step by step.
Step 1: Create the Authentication
The Azure Functions Authentication codeunit has a method called CreateCodeAuth that takes two parameters: the endpoint URL and the authentication code (your function key). It returns an Azure Functions Authentication interface:
Auth := AzureAuth.CreateCodeAuth(
'https://callmefromal20221106104102.azurewebsites.net/api/Function1',
'1tz26BtEaDtNpASSQp8bLu0QjEwCmD8menaQogWJbm-8AzFu1O94kQ==');
It’s worth noting that with code authentication, the key is passed as a query parameter rather than in the HTTP headers. OAuth authentication, on the other hand, would add a bearer access token to the request headers.
Step 2: Build the Query Parameters
Query string parameters are passed as a Dictionary of [Text, Text]. In this case, the Azure Function expects a name parameter:
Query.Add('name', 'Erik');
Step 3: Send the Request
The SendGetRequest method on the Azure Functions codeunit takes the authentication interface and the query dictionary, and returns an Azure Functions Response codeunit:
Response := AzureFunc.SendGetRequest(Auth, Query);
Step 4: Inspect the Response
The Azure Functions Response codeunit provides several useful methods:
IsSuccessful()— returns a Boolean indicating whether the call succeededGetError()— returns the error message if the call failedGetResultAsText(var Result: Text)— populates a text variable with the response bodyGetResultAsStream()— returns the response body as a streamGetHttpResponse()— returns the underlying HTTP response message (internal)
Note that GetResultAsText uses a var parameter rather than returning text directly. Erik points out that this is a deliberate design choice to avoid an extra memory copy of potentially large response data — by passing your variable directly, the framework can write into it without creating an intermediate copy.
Response.GetResultAsText(Result);
message('%1\%2\%3', Response.IsSuccessful(), Response.GetError(), Result);
The Result
After publishing and running the extension, opening the Customer List triggered the Azure Function call. The message box displayed:
- IsSuccessful: Yes
- GetError: (empty — no error)
- Result: “Hello, Erik. This HTTP triggered function executed successfully.”
It worked on the first real attempt!
Debugging Challenges
Erik encountered a notable frustration during debugging: interface variables cannot be inspected in the AL debugger. The Auth variable simply shows “Interface” with no further details, and the Response codeunit is similarly opaque. This makes it difficult to troubleshoot issues without adding explicit logging or message statements to your code.
Limitations and Observations
After getting the GET request working, Erik explored the SendPostRequest method signature:
Response := AzureFunc.SendPostRequest(Auth, Body: Text);
He noted several limitations of the current implementation:
- POST requests accept only a text body — there’s no overload for binary data or streams
- POST requests don’t accept query parameters — unlike GET, you can’t pass a dictionary alongside the body
- No support for PATCH or other HTTP methods — only GET and POST are available
- No tests or documentation — the module shipped without test codeunits or a README
Looking at the implementation codeunit, Erik found that the underlying Send function is only about 60 lines of code. It builds a query string from the dictionary, attaches the authentication (as a query parameter for code auth, or as a bearer token header for OAuth), and sends the HTTP request. As Erik points out, you could easily replicate this yourself using the standard HttpClient, HttpRequestMessage, and HttpResponseMessage types in AL.
The app.json Configuration
For reference, here’s the app.json used for this extension. Note the runtime version of 10.0 and application version of 21.0.0.0, which correspond to Business Central 2022 Wave 2:
{
"id": "b9a8de49-0d6d-43eb-b4d0-25bc6755c0f6",
"name": "AzureFunctions",
"publisher": "Default publisher",
"version": "1.0.0.0",
"platform": "1.0.0.0",
"application": "21.0.0.0",
"idRanges": [
{
"from": 50100,
"to": 50149
}
],
"runtime": "10.0",
"features": [
"NoImplicitWith"
]
}
Conclusion
The new Azure Functions module in Business Central 2022 Wave 2 provides a lightweight, convenient way to call Azure Functions from AL code. With just a few lines, you can authenticate using a function key or OAuth, send GET or POST requests, and inspect the response. While the current implementation is somewhat limited — lacking support for binary payloads, additional HTTP methods, and query parameters on POST requests — it covers the most common scenarios. Since the system application is open source, these gaps could potentially be addressed through pull requests. For more complex scenarios, you can always fall back to using HttpClient directly in AL, but for straightforward Azure Function integrations, this new module is a welcome addition.