Connect to Azure Function in BC 2022 wave 2 (v21)

by Oct 15, 2022AL Language

Home 9 Development 9 AL Language 9 Connect to Azure Function in BC 2022 wave 2 (v21)

The new version of the Microsoft Dynamics 365 Business Central brought a new system module “Azure Functions” that makes integration with Azure Functions much easier and straightforward. The typical scenario, why to use Azure Functions together with Business Central, is if we need to use the .NET interop component (for example custom DLL) from AL. Some examples of why I use Azure Functions are creating integration with SFTP (to send warehouse data to old-school shipping agents), loading data from machines in manufacturing, or just signing documents using SignPads.

The new module contains three (public) codeunits.

  • Azure Functions (codeunit 7804)
  • Azure Functions Authentication (codeunit 7800)
  • Azure Functions Response (codeunit 7805)

Authentication

Let’s start with authentication. There are two possible ways how to use this module from the point of authentication. If your Azure Function is secured at function-level only (= without user identification), we can use the method CreateCodeAuth(…) from Codeunit 7800 “Azure Functions Authentication”. This method accepts two parameters, Azure Functions endpoint (URL) and authentication code that is defined for your Azure Function. This approach is far from ideal as anyone who knows this code can access your Azure Functions.

The second method is CreateOAuth2(…) which allows creating a connection with both OAuth2 and the Authentication code for your function.

Both these methods return an instance of codeunit that implements an interface “Azure Functions Authentication” which is later used for API requests.

Create own integration

Once we know the authentication methods, we know everything necessary to build our integration. To send a GET request to our Azure Function we can use the method SendGetRequest(…) available in Codeunit 7804 “Azure Functions”.

This method has two parameters, the first one is the implementation of Azure Functions Authentication that we discussed earlier. The second one is a Dictionary of Texts that will be translated to URL parameters (the dictionary key = parameter name, dictionary value = parameter value).

Method SendGetRequest(…) returns an instance of another codeunit, Codeunit 7805 “Azure Functions Response”. This object contains all information about the API response, including whether the API call was successful (method IsSuccessful()). You can also get error information (GetError()), response content (GetResultAsText(…), GetResultAsStream(…)) or whole instance of HttpResponseMessage (GetHttpResponse(…)).

procedure SendAzureFunctionGetRequest()
var
    AzureFunctions: Codeunit "Azure Functions";
    AzureFunctionsResponse: Codeunit "Azure Functions Response";
    AzureFunctionsAuthentication: Codeunit "Azure Functions Authentication";
    IAzureFunctionsAuthentication: Interface "Azure Functions Authentication";
    QueryDictinary: Dictionary of [Text, Text];
begin
    QueryDictinary.Add('param1', 'value1');
    QueryDictinary.Add('param2', 'value2');
    QueryDictinary.Add('param3', 'value3');

    IAzureFunctionsAuthentication := AzureFunctionsAuthentication.CreateCodeAuth('AZURE FUNCTION ENDPOINT', 'AUTHENTICATION CODE');
    AzureFunctionsResponse := AzureFunctions.SendGetRequest(IAzureFunctionsAuthentication, QueryDictinary);
    if AzureFunctionsResponse.IsSuccessful() then
        Message('Get request successful.')
    else
        Error('Get request failed.\Details: %1', AzureFunctionsResponse.GetError());
end;

The POST request could be created similarly – we just need to use the SendPostRequest(…) method instead of SendGetRequest(…). This time, the method does not accept Dictionary, but RequestBody (only as Text) and Content-Type header.

Unfortunately, I really miss something like SendPostRequest(AzureFunctionAuthentication: Interface “Azure Functions Authentication”; Body: JsonObject) as it will be much easier to use…

procedure SendAzureFunctionPostRequest()
var
    AzureFunctions: Codeunit "Azure Functions";
    AzureFunctionsResponse: Codeunit "Azure Functions Response";
    AzureFunctionsAuthentication: Codeunit "Azure Functions Authentication";
    IAzureFunctionsAuthentication: Interface "Azure Functions Authentication";
    RequestBody: Text;
    JsonObject: JsonObject;
begin
    JsonObject.Add('param1', 'value1');
    JsonObject.Add('param2', 'value2');
    JsonObject.Add('param3', 'value3');

    JsonObject.WriteTo(RequestBody);
    IAzureFunctionsAuthentication := AzureFunctionsAuthentication.CreateCodeAuth('AZURE FUNCTION ENDPOINT', 'AUTHENTICATION CODE');
    AzureFunctionsResponse := AzureFunctions.SendPostRequest(IAzureFunctionsAuthentication, RequestBody, 'application/json');
    if AzureFunctionsResponse.IsSuccessful() then
        Message('Post request successful.')
    else
        Error('Post request failed.\Details: %1', AzureFunctionsResponse.GetError());
end;

There is also an available universal method Send(…), which allows using any HTTP Request Type and defining both Header & Body content.

procedure SendAzureFunctionUniversalRequest()
var
    AzureFunctions: Codeunit "Azure Functions";
    AzureFunctionsResponse: Codeunit "Azure Functions Response";
    AzureFunctionsAuthentication: Codeunit "Azure Functions Authentication";
    IAzureFunctionsAuthentication: Interface "Azure Functions Authentication";
    HttpRequestType: Enum "Http Request Type";
    QueryDictinary: Dictionary of [Text, Text];
    RequestBody: Text;
    JsonObject: JsonObject;
begin
    QueryDictinary.Add('urlParam1', 'urlValue1');
    QueryDictinary.Add('urlParam2', 'urlValue2');
    JsonObject.Add('bodyParam1', 'bodyValue1');
    JsonObject.Add('bodyParam2', 'bodyValue2');

    JsonObject.WriteTo(RequestBody);
    IAzureFunctionsAuthentication := AzureFunctionsAuthentication.CreateCodeAuth('AZURE FUNCTION ENDPOINT', 'AUTHENTICATION CODE');
    AzureFunctionsResponse := AzureFunctions.Send(IAzureFunctionsAuthentication, HttpRequestType::/*METHOD*/, QueryDictinary, RequestBody, 'application/json');
    if AzureFunctionsResponse.IsSuccessful() then
        Message('Universal request successful.')
    else
        Error('Universal request failed.\Details: %1', AzureFunctionsResponse.GetError());
end;

In my opinion, this new system module made the integration with Azure Functions a bit easier as we do not longer need to set all headers manually and also we do not have to think about the implementation of authentication.

What do you think? Let me know by sharing this article on Twitter/LinkedIn.

Recent Articles from the category

NavigationAction for ErrorInfo data type

NavigationAction for ErrorInfo data type

One more article about ErrorInfo data type. Have you already read my previous posts about ErrorInfo and Collectible Errors? ErrorInfo data type & Collectible Errors | MSDyn365 Business Central - Ing. Tomáš Kapitán (kepty.cz) Collectible Errors?! | MSDyn365...

read more
Error actions for ErrorInfo data type

Error actions for ErrorInfo data type

It is already almost one and a half years since the ErrorInfo data type was introduced (we saw this data type for the first time in BC2021w2). If you do not know what it is and how to use this data type, check my previous posts: ErrorInfo data type & Collectible...

read more
Data types under the microscope: List

Data types under the microscope: List

The List data type in AL language represents an ordered collection of objects that can be accessed by their index. Unlike an Array data type, a List does not have a fixed size and does not need to have its dimension specified when it is declared. The List data type...

read more
Collectible Errors?! Is it already in use?

Collectible Errors?! Is it already in use?

Collectible Errors?! Is it already in use? This is the second part of my new article series about Collectible Errors. Let's check out the first part here: Collectible Errors?! | Microsoft Dynamics 365 - Ing. Tomáš Kapitán (kepty.cz) or you might be interested in my...

read more
Collectible Errors?!

Collectible Errors?!

Collectible Errors?! It has been already almost a year since ErrorInfo datatype & CollectibleErrors were introduced (I already have an article about basic structure: ErrorInfo data type & Collectible Errors). This article was released for the first time in...

read more
Substituting standard reports

Substituting standard reports

Report objects cannot be extended in versions previous to the Business Central 2021 release wave 1 version when ReportExtensions object was introduced. Since then, many of changes we need to do in reports can be done without creating a new copy of object. On the other...

read more

Sign Up for News

Certifications

Highest certification
Microsoft Data Management and
also in D365 Business Central

Microsoft Certified: Dynamics 365 Business Central Functional Consultant Associate

See other certifications here