Calling SAP RFC function with Azure Function

Peter KARDA
Published by Peter KARDA
Category : .Net / Azure / Azure Functions
30/06/2023

Recently we’ve been asked to migrate a few data flows running on proprietary software to Azure. The flows were not complicated; the only specialty was that we had to call some SAP RFC functions. As we have already implemented several Logic App workflows using SAP connectors capable of doing such calls we haven’t seen a problem. However, when we tried to connect to the target SAP, we received the following error message: “Required parameters ‘[rfcGroupFilter]’ not set or invalid.” We’ve double-checked the connection parameters, then the error logs on the on-premise data gateway but we haven’t found anything strange. But when we compared side by side the Logic App that is working correctly with the one giving us the error we realized that in case of the error, we are targeting a different (older) version of SAP and there might be a compatibility problem.

 

Looking for solution

As we’ve been unable to find out how to do it in Logic App, we’ve started to take a look for another solution. Wouldn’t be possible to do it with Azure Function?  Let’s quickly summarize what we know and need for that:

  • We would like to use Azure Functions implemented in C#. In .NET the only way to connect to SAP is by using SAP .NET Connector called NCo. The connector however requires .NET Framework 4.x
  • When targeting .NET Framework 4.x we need to use the version of Azure Function supporting this framework. A few years ago it was Azure Function v1 which is now deprecated. Fortunately, Azure Function v4 now supports .NET Framework 4.x functions running in isolated processes so we should be fine (https://learn.microsoft.com/en-us/azure/azure-functions/functions-versions).
  • While the SAP system is on-premise and (in our case) on a private network we need to create an Azure Hybrid connection to SAP

 

Set-up

Let’s start with the provisioning of the Azure resources. Firstly, I’ve provisioned Azure Function App running on App Service Plan and I’ve created the hybrid connection to SAP. The default SAP port is 3300 but it could vary depending on set-up. In case you need help in creating the hybrid connection you might find some information in one article I’ve written a while ago (https://www.middleway.eu/using-azure-hybrid-connections/).

Right now we can create and deploy our function. In my case, I’ve created the function with Visual Studio using the template for Azure Functions choosing Function worker: “.NET Framework Isolated v4”.

Azure Function Visual Studio template

 

Once the function project is created we need to add the NCo references as mentioned before. You can either install the latest official version of the NCo 3.0 connector from the SAP website (you’ll need the SAP client account to download it) or install it as a NuGet package. For example, the package sapnco_x64 does the job:

NuGet SAP NCo

 

This NuGet package does not contain the latest version of the NCo but it’s not a big deal. We have everything to start implementing our Azure Function that can do some SAP calls.

 

Implementation

The implementation of the Azure Function to call the SAP RFC function doesn’t differ much from the implementation of other types of .NET applications. The functional code is just placed in the function trigger. In our demo I’ll create an HTTP trigger function where I’m :

  • Reading input parameters (in my case the input parameters are in the JSON body of the request)
  • Creating RFC connection configuration (that needs to be initialized with your configuration values)
  • Creating RFC destination (connection)
  • Invoking RFC function by its name
  • Obtaining the result

 

And here we are:

[Function("SapRfcCallDemo")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
    // Reading input JSON with parameters
    dynamic inputParameters = await req.ReadFromJsonAsync<dynamic>();

    HttpResponseData response = req.CreateResponse();
    response.Headers.Add("Content-Type", "text/plain; charset=utf-8");

    try
    {
        // Setting up the connection parameters
        RfcConfigParameters rfcPar = new RfcConfigParameters();
        rfcPar.Add(RfcConfigParameters.Name, "ApplicationServer");
        rfcPar.Add(RfcConfigParameters.Client, "100");
        rfcPar.Add(RfcConfigParameters.User, "sap_user_name");
        rfcPar.Add(RfcConfigParameters.Password, "sap_user_password");
        // The server host is the same as the one in hybrid connection
        rfcPar.Add(RfcConfigParameters.AppServerHost, "demosap.domain.com");
        rfcPar.Add(RfcConfigParameters.AppServerService, "sapservice");
        rfcPar.Add(RfcConfigParameters.SystemNumber, "00");
        rfcPar.Add(RfcConfigParameters.Language, "en");
        rfcPar.Add(RfcConfigParameters.SncQOP, "Authentication");

        // Create the destination
        RfcDestination dest = RfcDestinationManager.GetDestination(rfcPar);
        RfcRepository rfcRepository = dest.Repository;

        IRfcFunction sapFunction = rfcRepository.CreateFunction("MY_RFC_FUNCTION");
        sapFunction.SetValue("I_PARAM1", inputParameters.param1);
        sapFunction.SetValue("I_PARAM2", inputParameters.param2);
        sapFunction.SetValue("I_PARAM3", inputParameters.param3);

        // Calling the RFC function
        sapFunction.Invoke(dest);
        // Obtaining the result
        var result = sapFunction.GetTable("MY_RESULT_TABLE");

        response.StatusCode = HttpStatusCode.OK;

        if (result != null)
        {
            response.WriteString(result.ToString());
        }
    }
    catch (Exception ex)
    {
        response.StatusCode = HttpStatusCode.InternalServerError;
        response.WriteString(ex.Message.ToString());

        _logger.LogError($"Error occured: {ex.Message}");
    }

    return response;
}

Of course, passing the input parameters and configuration has to be adapted to your need. Same for the extracting of results. Note that the value of the AppServerHost parameter should be the same which was specified in the hybrid connection.

What is important to mention is that the function has to be compiled to a specific target platform so the NCo connector works properly. In my case, I’m targeting the build to the “x64” platform. The code built for “Any CPU” won’t work.

 

Summary

Calling SAP RFC functions by using Azure Function is a bit more laborious compared to Logic App connector. However, our solution with Azure Function helped us to overcome the compatibility issues we had. We could as well benefit from Azure Function running in an isolated process. It decouples .NET functions from the Azure Functions host so it was possible for us to use .NET Framework 4.x and NCo while being able to use the latest Azure Function trigger interface and deploy it to the latest Azure Function v4.