This post is part of a series sharing service implementation experience and possible applications of SAP Private Link Service on Azure.
Find the table of contents and my curated news regarding series updates here.
Find the associated GitHub repos here and here. |
Dear community,
Continuing with the implementation journey of
SAP Private Link Service (PLS) for Azure we will have a closer look at connecting
privately to Azure services that don’t have an OData interface today from apps implemented with
SAP Cloud Application Programming Model (CAP). You might say that SAP CAP can connect to non-BTP sources via the
SAP Cloud SDK or custom coding. However, if an OData API is available, it works out of the box and allows for separation of concerns. Multiple BTP apps may use the same interface without the need for each of them to take a dependency on the Azure SDK.
So, why not meet CAP halfway?
We will do so using a
translator app running on
Azure App Service in a private virtual network on Azure exposed via SAP Private Link.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLXBpbmt5LnBuZw==&__proxy_form=0)
The provided translator app is pre-configured to work with Azure Cosmos DB and Azure Blob storage. Its structure is modular and open for you to add any service you desire.
Any scenario top of mind already? Reach out via GitHub or
leave a note down below under this post.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLW92ZXJ2aWV3LTIucG5n&__proxy_form=0)
Fig.1 Architecture overview depicting the protocol translation and the SAP Private Link exposure.
Let’s take a look under the hood
To replicate the scenario, create a basic private Azure VNet, provision a serverless Azure Cosmos DB for NoSQL instance and windows-based Azure App Service into it. Make sure all resources are in the same region. You may do this via CLI, the Azure portal, bicep, or terraform for instance. See below guidance for the portal-based approach.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLXZuZXQucG5n&__proxy_form=0)
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWNvc21vcy5wbmc=&__proxy_form=0)
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWFwcC5wbmc=&__proxy_form=0)
Fig.2 Detailed Azure deployment view from the Azure portal
Data setup and integration test
Before we make everything private and troubleshooting becomes harder (would require private VNet access via
Azure Bastion service for instance or VPN), let’s verify initial integration.
I am using the famous SAP SFlight demo data set on Cosmos as a sample. It is stored in database “saps4” in container “sflight”.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWRhdGFiYXNlLnBuZw==&__proxy_form=0)
Fig.3 Screenshot of Sflight data set in Data Explorer of Cosmos DB
You may
synch the data from SAP or for the sake of quicker getting started generating it using the Cosmos DB Data Explorer with below steps:
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWNoYXRncHQucG5n&__proxy_form=0)
Fig.4 ChatGPT generated guidance to create SFlight data set in Cosmos DB
{
"id": "006",
"carrid": "AA",
"connid": "64",
"fldate": "2016-06-17",
"planetype": "A310-300",
"seatsmax": 280,
"seatsocc": 10
}
Deploy the code for the translator app via the provided script for convenience:
az login
.\publish.bat [your resource group] [name of above created app service]
Once finished call the APIs to verify proper setup with public endpoints still.
Local execution (or from
GitHub Codespaces) is also possible at this point:
cd .\GenericODataWebAPI\
dotnet run
Find a sample call library on the repos
here.
Call
http://localhost:52056/api/odata/Sflight for the integration test. It should look something like this:
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWRhdGEtdGVzdC5wbmc=&__proxy_form=0)
Fig.5 Sflight output from local OData request via translator app
So far so good. Now we will inject all services into our private VNet and prepare the SAP Private Link.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWNvc21vcy1wcml2YXRlLTEucG5n&__proxy_form=0)
Fig.6 Screenshot of Cosmos DB private network setup
Note the associated VNet in above screen and the marked
checkbox to retain access to the database via the portal. Believe me been there, done that
😉
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWFwcC1wcml2YXRlLnBuZw==&__proxy_form=0)
Fig.7 Screenshot of App Service private network setup
Inject the Azure App Service into the same VNet as your Cosmos instance. See above for a reference.
Swoosh 🌪️ At this point you can reach the database only via the App Service! Don’t believe me? Go on and check. Trust is good but verification is better as they say🧐
Now onwards to the private link. Collect the resource id of your app service either from the properties pane or my favourite option: From the JSON view of the Overview pane.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWFwcC1pZC5wbmc=&__proxy_form=0)
Fig.8 Screenshot of App Service resource id retrieval
Login into your BTP subaccount and create a new instance of SAP Private Link called “
odata-proxy-pls” providing the required properties for Azure App Service. See the
SAP docs for further details.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWJ0cC1jcmVhdGUucG5n&__proxy_form=0)
Fig.9 Screenshot of SAP Private Link create view for Azure App Service
This triggers the connection request on the Azure App Service from your BTP account. Go on hit approve. I will wait
⏳
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWFwcHJvdmUucG5n&__proxy_form=0)
Fig.10 Screenshot of SAP Private Link approval in Azure
Done? Ok, great. Now we are ready to deploy our CAP app to “seal” the private link and create the Cloud Foundry service binding.
I provided a
repos for this purpose. Find more details on how to deploy via Business Application Studio (BAS) or the likes there too. I ran “cf deploy”.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLW10YS5wbmc=&__proxy_form=0)
Fig.11 Screenshot of CAP app config with emphasis on SAP Private Link config
See above the reference on the CAP app binding itself to the SAP Private Link pointing to the Azure App Service.
Be aware that you require an SSH tunnel on BAS for local testing reaching through the private link to Azure. Otherwise only deployed apps have access due to the Cloud Foundry security groups. Find more details here. |
With that we can retrieve the hostname to reach the private link and maintain our destination on BTP.
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWhvc3QucG5n&__proxy_form=0)
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWRlc3QucG5n&__proxy_form=0)
Fig.12 Screenshot of SAP BTP Destination setup using Private Link
For now, I didn’t put authentication on the App Service but is pre-configured to use Azure AD. Have a look
here. Once you decided what authentication flow to use adjust the destination above accordingly.
Broke a sweat yet? 🥵
Either way you may be glad to hear we are done 💪Yes, go on call the CAP app URL and marvel and the privately served data from Cosmos DB as OData stream:
![](./?url=aHR0cHM6Ly9jb21tdW5pdHkuc2FwLmNvbS9sZWdhY3lmcy9vbmxpbmUvc3RvcmFnZS9ibG9nX2F0dGFjaG1lbnRzLzIwMjMvMDQvYXBwLXNlcnZpY2UtcGxzLWNhcC5wbmc=&__proxy_form=0)
Fig.13 Screenshot of SAP CAP app OData output requested from Cosmos DB
Thanks to the translator app we are serving OData from Azure Cosmos DB tapping into the dotnet NoSQL API even though Cosmos DB doesn't have an OData interface for the NoSQL API. And all of that privately!
Focus of this blog was on advocating for the private link approach and deeper understanding of the setup rather than enterprise scale. However, all steps taken can be accomplished via infrastructure-as-Code. I can highly recommend the Azure Developer CLI (AZD). For BTP components consider the BTP Setup Automator.
In addition to that I am considering adding the AZD configuration to the repos at later point in time. Would appreciate any help from the community! |
Thoughts on production readiness
SAP Private Link is generally available and therefore completely ready for prime time (quoting
gowrisankar.m2 from the SAP engineering team
😊).
Cosmos DB powers critical globally available solutions like
NEXT GAMES apps with real-time leader board requirements or the
data hub of Deutsche Börse AG for instance. For additional stories, have a look at the official
portal or the nice community offering
Azure Charts.
Cosmos DB
scales on its own, match that setup for your CF app on BTP where required with the
Application Autoscaler.
Azure App Service is a full-blown PaaS offering to run apps, APIs in all major programming languages, with native integration with Azure AD (no need for custom code), Log Analytics, Application Insights, and Key Vault etc. Have a look
here for more goodies. You may even
run the SAP Cloud SDK on App Service.
Final Words
Alrighty, folks! Today we continued our SAP Private Link with Azure journey to the Azure App Service - a
match made in tech-heaven! We took a closer look at how to
connect privately to Azure services that don’t offer an
OData interface today, using a
translator app running on the App Service.
The provided
repos is
fully operational to translate to OData for Azure Cosmos DB and has the
foundation pre-configured for Azure Blob Storage. You need a different service or prefer to private-link directly sacrificing OData? Let me know in the comments or open an Issue on GitHub or go all-in with a Pull-Request
😊
Till then: sit back, relax, and let's get private linking!
Any comments regarding your individual integration journey with SAP Private Link SAP @Developers and @Architects?
Find the related GitHub repos
here and
here.
Find your way back to the aggregator blog post
here.
As always feel free to ask lots of follow-up questions.
Best Regards
Martin