Define your MTA resources and extension descriptors
- How to define modules and resources for a multitarget application
- The different services involved in SaaS enabling an add-on on SAP BTP, ABAP Environment
- Step 1
To SaaS enable an ABAP Solution, we use the following BTP services alongside the services needed for creation of ABAP systems (
abap/standard) or (abap/saas_oem)
-
ABAP Solution:
ABAP Solution Service is used to enable multi-tenancy for ABAP Environment systems: Instead of creating the ABAP system manually it is created by the ABAP Solution service during the first subscription of an application consumer.
Credentials provided in the ASP_CC destination are used by ABAP Solution Service to authenticate at the Cloud Foundry Cloud Controller API endpoint and to create required ABAP service instances.
-
XSUAA:
The XSUAA service instance acts as an OAuth 2.0 client to the Multitenant Application and to the ABAP Solution service instance. It provides access to the SaaS Provisioning service for calling callbacks and getting the dependencies API by granting corresponding scopes.
A role collection is defined as part of the XSUAA configuration which provides authorization for the initial user onboarding in the new ABAP tenant after a subscription was done.
-
SaaS Provisioning Service:
The SaaS Provisioning service allows application providers to register applications and services in the Cloud Foundry environment in SAP Business Technology Platform.
-
- Step 2
In your Business Application Studio dev space, access the project you created in the tutorial ‘Create an MTA project for the Multitenant Application using SAP Business Application Studio’ and open the mta.yaml file. The MTA file starts with the schema version, ID of the MTA and the MTA version. You can pick your own ID and version, the schema-version should be kept as is.
YAMLCopy_schema-version: "3.1" ID: tutorial_mta version: 0.0.1To define parameters which can be reused in further sections, add the following snippet to the mta.yaml file
YAMLCopyparameters: app-domain: ${default-domain} route-prefix: -${appname} appname: addon-product-name: provider-admin-email: saas-display-name: saas-description: tenant-mode: enable-parallel-deployments: trueField Name Description appname Name of your SaaS Application addon-product-name Name of your registered add-on product saas-display-name Name what your consumer shall see while subscribing tenant-mode Define whether you have a single/multi mode - which defines whether you shall have multiple tenants provider-admin-email Email id of your administrator The resultant mta.yaml file shall look as follows:
YAMLCopy_schema-version: "3.1" ID: tutorial_mta version: 0.0.1 parameters: app-domain: ${default-domain} route-prefix: -${appname} appname: ProductX # The registered add on product name addon-product-name: /NAMESPC/PRODUCTX # Administrator for the SaaS Solution provider-admin-email: administrator@example.com # What your consumer shall see while subscribing saas-display-name: Product X saas-description: Do A, B, C # Single/Multi - To define whether you shall have multiple tenants/consumers in one ABAP system v/s a single tenant/consumer per system tenant-mode: multi enable-parallel-deployments: true - Step 3
The modules for our SaaS application must be defined. In our case, that’s only the ‘approuter’. Keep in mind that all values under ‘properties’ will be available as environment variables to the application once deployed
Furthermore, it is assumed that the source code to the approuter is in the folder ‘router’ next to the
'mta.yaml'as defined by the ‘path’ propertyThe syntax defined with “${name}” is picked from the parameters of the same name. For example,
${appname}shall pick"ProductX"as per the parameter value defined in the previous stepYAMLCopymodules: - name: approuter type: javascript.nodejs path: router parameters: keep-existing-routes: true routes: - route: cis${route-prefix}.${app-domain} app-name: ${appname} memory: 1024MB properties: TENANT_HOST_PATTERN: (.*)${route-prefix}.${app-domain} XS_APP_LOG_LEVEL: error SAP_JWT_TRUST_ACL: - clientid: "*" identityzone: sap-provisioning requires: - name: tutorial_xsuaa - name: tutorial_saas-registry - name: tutorial_abap-solution parameters: config: abap_endpoint_timeout: 6000The resultant mta.yaml file looks similar to the following with property values reflecting your context. Here, some values are also added for the various parameters defined earlier:
YAMLCopy_schema-version: "3.1" ID: tutorial_mta version: 0.0.1 parameters: app-domain: ${default-domain} route-prefix: -${appname} appname: TutorialSample addon-product-name: /NAMESPC/ProductX provider-admin-email: administrator@example.com saas-display-name: Tutorial saas-description: Sample SaaS Application for Tutorial tenant-mode: multi enable-parallel-deployments: true modules: - name: approuter type: javascript.nodejs path: router parameters: keep-existing-routes: true routes: - route: cis${route-prefix}.${app-domain} app-name: ${appname} memory: 1024MB properties: TENANT_HOST_PATTERN: (.*)${route-prefix}.${app-domain} XS_APP_LOG_LEVEL: error SAP_JWT_TRUST_ACL: - clientid: "*" identityzone: sap-provisioning requires: - name: tutorial_xsuaa - name: tutorial_saas-registry - name: tutorial_abap-solution parameters: config: abap_endpoint_timeout: 6000 - Step 4
Define the resources for XSUAA, SaaS Registry and the ABAP Solution Service
-
XSUAA instance needs parameters to be configured as described here
-
The SaaS Provisioning Service needs the parameters as described here
-
The ABAP Solution Service needs the parameters as described here
The resources section could look as follows:
YAMLCopyresources: - name: tutorial_xsuaa type: com.sap.xs.uaa requires: - name: tutorial_abap-solution parameters: service-plan: application service-name: xsuaa config: xsappname: ${appname} tenant-mode: shared scopes: - name: $XSAPPNAME.Callback description: With this scope set, the callbacks for tenant onboarding, offboarding and getDependencies can be called. grant-as-authority-to-apps: - $XSAPPNAME(application,sap-provisioning,tenant-onboarding) foreign-scope-references: - uaa.user role-collections: - name: ${appname}-admin role-template-references: - $XSSERVICENAME(tutorial_abap-solution).SolutionAdmin - name: tutorial_saas-registry type: org.cloudfoundry.managed-service parameters: service: saas-registry service-plan: application service-name: saas-registry config: xsappname: ${appname} appName: ${appname} appUrls: getDependencies: https://cis${route-prefix}.${app-domain}/callback/v1.0/dependencies onSubscription: https://cis${route-prefix}.${app-domain}/callback/v1.0/tenants/{tenantId} displayName: ${saas-display-name} description: ${saas-description} - name: tutorial_abap-solution type: org.cloudfoundry.managed-service parameters: service: abap-solution service-plan: standard service-name: abap-solution config: name: ${appname} addon_product_name: ${addon-product-name} size_of_runtime: 1 size_of_persistence: 2 tenant_mode: ${tenant-mode} consumer_id_pattern: ([^-]*).* provider_admin_email: ${provider-admin-email} usage: prod xs-security: xsappname: ${appname}The full MTA descriptor (mta.yaml file) looks as follows (customized to your context):
YAMLCopy_schema-version: "3.1" ID: tutorial_mta version: 0.0.1 parameters: app-domain: ${default-domain} route-prefix: -${appname} appname: TutorialSample addon-product-name: /NAMESPC/ProductX provider-admin-email: administrator@example.com saas-display-name: Tutorial saas-description: Sample SaaS Application for Tutorial tenant-mode: multi enable-parallel-deployments: true modules: - name: approuter type: javascript.nodejs # Path to the folder where the approuter module is defined with package.json, index.js and xs-app.json path: router parameters: keep-existing-routes: true # Routes for various subscriptions/callbacks routes: - route: cis${route-prefix}.${app-domain} # Name of the application that shall be seen in the space under the applications tab in BTP cockpit app-name: ${appname} # Run-time memory of the application - Value should be less than (1024 * units of entitlements for cloud foundry runtime-memory in the provider sub-account) memory: 1024MB properties: # The application router derives the tenant from the URL(cf route) and calls the tenant-aware XSUAA # Then, the XSUAA delegates the authentication to the configured IdP, and creates a JSON Web Token (JWT) that contains # the tenant, the current user, and the authorization scopes of the user. # The JWT is then sent back to the application router, and from there to the application. TENANT_HOST_PATTERN: (.*)${route-prefix}.${app-domain} # The approuter contains logs which can be referred in case of errors/failures. The XS_APP_LOG_LEVEL defines the level of detail of this log XS_APP_LOG_LEVEL: error SAP_JWT_TRUST_ACL: - clientid: "*" identityzone: sap-provisioning requires: - name: tutorial_xsuaa - name: tutorial_saas-registry - name: tutorial_abap-solution parameters: config: abap_endpoint_timeout: 6000 resources: - name: tutorial_xsuaa type: com.sap.xs.uaa requires: - name: tutorial_abap-solution parameters: service-plan: application # The service-name of the service instance that shall be created in the provider sub-account. Must be unique service-name: tutorial_xsuaa config: xsappname: ${appname} tenant-mode: shared # Provide access to the SaaS Provisioning service (saas-registry) for calling # callbacks and getting the dependencies API by granting scopes scopes: - name: $XSAPPNAME.Callback description: With this scope set, the callbacks for tenant onboarding, offboarding and getDependencies can be called. grant-as-authority-to-apps: - $XSAPPNAME(application,sap-provisioning,tenant-onboarding) # To ensure a token exchange will be possible and the ABAP systems can be called foreign-scope-references: - uaa.user # Provides authorization for the initial user onboarding in the new ABAP tenant after a subscription was done. # It must reference the 'SolutionAdmin' Role Template of the ABAP Solution service instance role-collections: - name: ${appname}-admin role-template-references: - $XSSERVICENAME(tutorial_abap-solution).SolutionAdmin - name: tutorial_saas-registry type: org.cloudfoundry.managed-service parameters: service: saas-registry service-plan: application service-name: tutorial_saas-registry config: xsappname: ${appname} appName: ${appname} appUrls: getDependencies: https://cis${route-prefix}.${app-domain}/callback/v1.0/dependencies onSubscription: https://cis${route-prefix}.${app-domain}/callback/v1.0/tenants/{tenantId} displayName: ${saas-display-name} description: ${saas-description} - name: tutorial_abap-solution type: org.cloudfoundry.managed-service parameters: service: abap-solution service-plan: standard service-name: tutorial_abap-solution config: name: ${appname} addon_product_name: ${addon-product-name} size_of_runtime: 1 size_of_persistence: 2 tenant_mode: ${tenant-mode} consumer_id_pattern: ([^-]*).* provider_admin_email: ${provider-admin-email} usage: prod xs-security: xsappname: ${appname} -
- Step 5
The parameters defined in the MTA can be adapted for the corresponding landscape using MTA extension descriptor files. These files extend the original MTA descriptor file (mta.yaml) and allow it to change certain values during deployment.
-
Create a folder named “extensions” alongside the mta.yaml file
-
Create a file in the extensions folder named “dev.mtaext”. This is our development descriptor. It could have the following content (customize to your context):
YAMLCopyID: tutorial_mta-dev _schema-version: "3.1" extends: tutorial_mta-solution parameters: addon-product-name: /NAMESPC/PRODUCTX # Avoid special characters or mixed casing in the name. Alternately, the TENANT_HOST_PATTERN parameter needs to be adapted to accommodate special characters appname: tutorial provider-admin-email: administrator@example.com saas-description: "ABAP based SaaS solution with an ABAP tenant per subscription" saas-display-name: "SaaS Solution (Multitenancy)" tenant-mode: multi modules: - name: approuter properties: XS_APP_LOG_LEVEL: debug- Similarly, create a file named “prod.mtaext” in the extensions folder with the following content:
YAMLCopy
ID: tutorial_mta-prod _schema-version: "3.1" extends: tutorial_mta parameters: addon-product-name: /NAMESPC/PRODUCTX appname: tutorial_mta provider-admin-email: administrator@example.com saas-description: "ABAP based SaaS solution with an ABAP tenant per subscription" saas-display-name: "SaaS Solution (Multitenancy)" tenant-mode: multi route-prefix: "" app-domain: mydomain.com modules: - name: approuter parameters: routes: - route: "cis${route-prefix}.${app-domain}" - route: "*${route-prefix}.${app-domain}"Depending on the stage of implementation of the multitenant application, a different approuter configuration is being used. This is controlled via different extension descriptor files used during MTA deployment.
The workspace looks as follows after both files are created:

-
- Step 6
Which of the following services are required for SaaS enabling an add-on on SAP BTP, ABAP Environment