Consumability Across Global BTP Accounts Via Custom Service Brokers

As of the writing of this blog post in early 2022, the best-practice way to provide a multitenant solution such that the same application is used by several different customers while each retaining their own data/identity/destinations/etc. is by following the documentation Developing Multitenant Applications in the Cloud Foundry Environment or Philip Mugglestone’s excellent video series SAP Business Technology Platform Multitenant Business Applications.

However, there is one key limitation to this approach.  The subscription to the multitenant application can only occur from subaccounts that exist within the global account where the multitenant application is running.  If you want to make the application available to a customer that already has their own BTP account, you must create a subaccount within the one where multitenant application is running in on their behalf and manage it for them(or give them sufficient permissions to manage it themselves).  Either way the operations of such a solution can get unmanageable and messy (what if you have 100s of customers?).

An alternate approach is to provide the solution in the form of a custom service broker.  Custom services brokers can be registered across global BTP accounts as long as both global accounts exist in the same landscape.  However there are drawbacks.  Using this approach requires that that application take on much of the responsibility of features that the multitenancy approach gives you for free.  Depending on your needs this might be fine or might be a deal-breaker.  But your solution will be consumed in the same way other BTP provided services are consumed and that makes for a more consistent experience for customers using your solution.

There are many little details that need to be aligned in order to create a functional custom service broker, so I created a yeoman generator to assist in the templating of your own project.  What follows in this blog post is the README.md of a project generated with the yeoman yo command and all the defaults taken.  You will want to answer the prompts with your own values so that your work in refactoring your project won’t be too much once you see a working custom service broker in action.  With this in mind, yours will be different from the below, but should still be functional.  Just follow the steps in order.

I’ve taken care the the generated service broker doesn’t have any undue dependencies.  If you look at the @sap/sbf docs, you’ll see that there are ways of using HANA or Postgres to manage the provisioning and credentialing.  I chose to use the XSUAA service for this so that there wouldn’t be any heavy dependencies on external persistence engines.  The key provisioning/deprovisioning hooks are implemented such that you can customize the handling logic to your needs.  I’ve stubbed up a simple use case with SQLite3 as an example.  Do keep in mind that since the SQLite is using an in-memory DB and not persisting to the file system, any contents will be lost if the module is restarted/redeployed.

Again, what follows is the result of running.

yo sap-service-broker

I had to rework the file to make it compatible with this blog engine so things will look a little different.

– Andrew

===== README.me =====

Welcome to the products service broker project.

Description

This is an example of how to provide a Software As A Service(SaaS) offering in the form of a custom service broker(not a subscribable multitenant application). Once registered in a Cloudfoundry organization and space, it will appear in the marketplace of that org/space just like any other service. You may then create a service instance of it and bind it to your consuming application or create a service key for it to reveal the particular credentials and endpoints.

Advantages

The key advantage to providing your solution in this way is to enable it to be consumed in a different Business Technology Platform(BTP) global account from which it resides. This is currently not possible with the standard multitenant/subcription model as they are restricted to only be subscribable from subaccounts within the same global account.

Another advantage is that it allows your solution to be consumed in the same way that other system provided services are (ex: AuditLog Service).

Disadvantages

Since this approach doesn’t rely on subaccounts, the endpoints are common for all consumers of the service and only the client_credentials grant_type is allowed. You can’t authenticate as a particular named user since there is no mechanism to provide an external identity provider(no trust relationships). Nor can you use destinations or cloud connector features that a subaccount provides.

Your solution must also completely handle the provisioning and deprovisioning logic as required. This includes setting up any required persistency(files, schemas, and/or database containers), tracking which are allocated to which tenant, and tearing them down or archiving them as your solution requires.

Documentation

This template project was built using the SAP Service Broker Framework Node package @sap/sbf and the XSUAA credential option. See the documentation for details.

Installation

The best approach is to use a yeoman template generator. When you run the yeoman generator, you will be prompted to enter the details particular to your situation(don’t just take the defaults) and avoid having to refactor it once it’s generated.

npm install -g yo
npm install -g generator-sap-service-broker

Now that yeoman and the service broker generator is installed on your local system, run the generator with this command.

yo sap-service-broker

Follow the prompts and substitute your own values as needed. This will help to create uniqueness and avoid naming collisions as mentioned above.

Perform the following steps or if you have a bash shell available, run the finalize-setup script

npm i -g @sap/sbf
cd broker
gen-catalog-ids
cd ..
hash-broker-password

or try the included script.

tools/finalize-setup.sh

You will be prompted to enter a password. The hash-broker-password tool will take your provided password and create a sha256 hashed version of it. You should see something like this.

Broker password to be hashed: Warning: For ISO/SOC compliance, the password should be at least 15 characters long.
Hashed credentials: [password]
sha256:cysd0RjF1dHqcJ5CDgZvddTD9DgJa78ov1hXlnCxKsQ=:lTETIvPe+ZYETjw5ELjk7a0uKjvc6oLOtwGlxhrXn/A=

Edit manifest.yml and replace [hashed-password] with the output of hash-broker-password.

You will need to use the password you provided in the create-service-broker command listed below (replacing [password]) in order to register your service broker.

Manual steps

Since we are using the manifest method of deploying our service broker components, we need to set up the services it requires prior to pushing.

Create UAA service of plan broker

As a first step we create an UAA service instance of plan broker to bind to our service and service broker. The service name is provided to service and service broker applications via environment variables.

cf create-service xsuaa broker products-uaa -c xs-security.json

Create auditlog service of plan standard (from prerequisites.md)

cf create-service auditlog standard products-audit

Verify that the services were created correctly.

cf s

You should see this.

products-uaa xsuaa broker products-audit auditlog standard 

Push service

This will create an application that handles the service broker proisioning callbacks(anonymous authentication) and another one that handles the service broker features(authentication required).

cf push

When this is completed check the deployed apps.

cf a

You should see the following apps.

name requested state instances ...
products-service-broker started 1/1 ...
products-service started 1/1 ...

Register the service broker in Cloud Foundry

This command registers the new service broker with space scope at the provided URL.

cf create-service-broker products-service-broker-dev broker [password] https://products-service-broker-dev.cfapps.us10.hana.ondemand.com --space-scoped

Verify that the service broker is registered and appearing in the marketplace

cf service-brokers
cf m | grep products-service-dev

Monitoring the broker and underlying service

If you want to keep watch on the provisioning and deprovisioning events, watch the logs of the service and service-broker in separate terminal windows.

cf logs products-service
cf logs products-service-broker

Deploying within the same global account but different org/space within the same landscape.

Now change into the consumer1 folder and create a service instance of your custom service broker and bind it to an example consumer app

cd consumer1

Create service instance of type products-service

cf create-service products-service-dev default products-service-instance1 -c parameters.json

Keep checking on the status of the service instance creation until you get ‘create succeeded’

cf service products-service-instance1

Deploy the consumer application

cf push

Call the application

Get the consumer application URL using CF cli, like:

cf app products-service-consumer

Get products by appending the `/products` to the URL and request it via browser for example.

https://products-service-consumer1-dev.cfapps.us10.hana.ondemand.com/products

You can add products to your service broker like this.

Add beer = https://products-service-consumer1-dev.cfapps.us10.hana.ondemand.com/products?action=add&product=beer

Add chips = https://products-service-consumer1-dev.cfapps.us10.hana.ondemand.com/products?action=add&product=chips

Check what’s been added.

https://products-service-consumer1-dev.cfapps.us10.hana.ondemand.com/products

You can remove products as well with the del action.

Del(ete) chips = https://products-service-consumer1-dev.cfapps.us10.hana.ondemand.com/products?action=del&product=chips

Check the service logs

cf logs products-service --recent

You should see the data extracted from the token, e.g.

2017-07-13T17:54:16.82+0300 [APP/PROC/WEB/0] OUT Service instance id: 167395a5-de69-422f-bfe0-1ea553ad7e30
2017-07-13T17:54:16.82+0300 [APP/PROC/WEB/0] OUT Caller tenant id: cc-sap
2017-07-13T17:54:16.82+0300 [APP/PROC/WEB/0] OUT Token grant type: client_credentials
2017-07-13T17:54:16.82+0300 [APP/PROC/WEB/0] OUT Calling app has name products-service-consumer and id f407548d-0854-435b-8d26-d91a95ad4c64

Deploying within a different global account and different org/space but within the same landscape.

Change to a different global account and potentially login as a different user. A good way to do this is to utilize your trial account.

cf t -o beta 

Now change into the consumer2 folder and create a service instance of your custom service broker and bind it to an example consumer app

cd ..
cd consumer2

Register the service broker in the second org/space on the same landscape

This command registers the service broker additionally with space scope at the provided URL. Make sure to use the password you provided above or the registration with fail.

cf create-service-broker products-service-broker-dev/beta broker [password] https://products-service-broker-dev.cfapps.us10.hana.ondemand.com --space-scoped

Verify that the service broker is registered and appearing in the marketplace

cf service-brokers
cf m | grep products-service-dev

Create service instance of type products-service

cf create-service products-service-dev default products-service-instance2 -c parameters.json

Keep checking on the status of the service instance creation until you get ‘create succeeded’

cf service products-service-instance2

Deploy the consumer application

cf push

Call the application

Get the consumer application URL using CF cli, like:

cf app products-service-consumer

Again, get products by appending the `/products` to the URL and request it via browser for example.

https://products-service-consumer2-dev.cfapps.us10.hana.ondemand.com/products

Add wine = https://products-service-consumer2-dev.cfapps.us10.hana.ondemand.com/products?action=add&product=wine

Add cheese = https://products-service-consumer2-dev.cfapps.us10.hana.ondemand.com/products?action=add&product=cheese

Check what’s been added.

https://products-service-consumer2-dev.cfapps.us10.hana.ondemand.com/products

Cleanup

When you no longer need this example, you can delete its artifacts from Cloud Foundry:

Note: Make sure to delete things in this order otherwise the service broker can get messed up and you won’t be able to remove it.

cf t -o beta
cf delete -r -f products-service-consumer2
cf delete-service -f products-service-instance2
cf delete-service-broker -f products-service-broker-dev/beta
cf t -o alpha cf delete -r -f products-service-consumer1
cf delete-service -f products-service-instance1
cf delete-service-broker -f products-service-broker-dev/alpha
cf delete-service-broker -f products-service-broker-dev
cf delete -r -f products-service-broker
cf delete -r -f products-service
cf delete-service -f products-uaa
cf delete-service -f products-audit

-Andrew