Some solutions require out-of-band or manual on-boarding steps, such as validating a customer, running scripts manually for deploying resources needed for a new customer etc. This sample uses notifications for a new customer, or any changes on the subscription status made outside of the solution code.
Please note the sample uses a personal NuGet package based on the preview version of .NET client library for Commercial Marketplace.
You can also find a short video published on the Azure Friday channel to see the experience and a brief explanation.
The sample requires .NET 5.*.*, and an Azure Storage account.
In the sections below you will find:
Let's first start with mentioning how to integrate a SaaS solution with Azure Marketplace.
Many different types of solution offers are available on Microsoft commercial marketplace for the customers to subscribe. Those different types include options such as virtual machines (VMs), solution templates, and containers, where a customer can deploy the solution to their Azure subscription. Commercial marketplace also provides the option to subscribe to a Software as a Service (SaaS) solution, which runs in an environment other than the customer's subscription.
A SaaS solution publisher needs to integrate with the marketplace commerce capabilities for enabling the solution to be available for purchase.
Commercial marketplace talks to a SaaS solution on two channels:
The SaaS solution in turn uses the REST API exposed on the marketplace side to perform corresponding operations. Those can be activating, cancelling, or updating a subscription.
To summarize, we can talk about three interaction areas between the Azure Marketplace and the SaaS solution,
On this page, the subscriber provides additional details to the publisher so the publisher can provision required resources for the new subscription. A publisher provides the URL for this page when registering the offer for commercial marketplace.
The publisher can collect other information from the subscriber to onboard the customer, and provision additional resources as needed. The publisher's solution can also ask for consent to access other resources owned by the customer, and protected by AAD, such as Microsoft Graph API, Azure Management API, etc.
:warning: IMPORTANT: the subscriber can access this page after subscribing to an offer to make changes to his/her subscription, such as upgrading, downgrading, or any other changes to the subscription from Azure portal.
This page should authenticate a subscriber through Azure Active Directory (AAD) using the OpenID Connect flow. The publisher should register a multi-tenant AAD application for the landing page.
The commercial marketplace calls this endpoint to notify the solution for the events happening on the marketplace side. Those events can be the cancellation or modification of the subscription through commercial marketplace, or suspending it because of the unavailability of a customer's payment method. A publisher provides the URL for this webhook endpoint when registering the offer for Azure Marketplace.
:warning: IMPORTANT: This endpoint is not protected. The implementation should call the marketplace REST API to ensure the validity of the event. This endpoint also receives a JWT token. You can validate the token against AAD, and check the audience to make sure this call is addressed to you. Please see the startup.cs file to see the implementation.
The Fulfillment API is documented here for subscription integration, and the usage based Metered Billing API documentation is here.
The publisher should register an AAD application and provide the AppID
(ClientId) and the Tenant ID
(AAD directory where the app is registered)
during registering the offer for the marketplace.
The solution is put on an access control list so it can call the marketplace REST API with those details. A client must use client credentials grant to get a token.
:warning: Important: The marketplace fulfillment API V2.0's resource ID is
20e940b3-4c77-4b0b-9a53-9e16a1b010a7
.
If you are using the V1 end point for AAD, use the value 20e940b3-4c77-4b0b-9a53-9e16a1b010a7
for the resource parameter. If you are using AAD V2 endpoint (recommended), use 20e940b3-4c77-4b0b-9a53-9e16a1b010a7/.default
for value of the scope parameter.
Please note the different requirements for the Azure AD interaction for the landing page and calling the APIs. I recommend two separate AAD applications, one for the landing page, and one for the API interactions, so you can have proper separation of concerns when authenticating against Azure AD.
This way, you can ask the subscriber for consent to access his/her Graph API, Azure Management API, or any other API that is protected by Azure AD on the landing page, and separate the security for accessing the marketplace API from this interaction as good practice. The certification policy requires the use of "User.Read" for signing on the user, and incremental consent pattern should you need to request other permissions.
Let's go through the steps of activating a subscription to an offer.
id_token
. There needs to be additional steps for
validating the id_token
— just receiving an id_token
is not enough for
authentication. Also, the solution may need to ask for authorization to
access other resources on behalf of the user. We are not covering them for
brevity and ask you to refer to the related Azure AD documentation."Bearer "
(notice the space) to the access token, and
adds it to the Authorization
header of the outgoing request. We are using
the marketplace token previously received on the landing page to get the
details of the subscription using the resolve API.This sample can be a good starting point, assuming the solution does not have requirements of providing a native experience for cancelling or updating a subscription by a customer.
It exposes a landing page that can be customized for branding. It provides a webhook endpoint for processing the incoming notifications from the Azure Marketplace. It also provides a privacy policy and support page to meet the partner center requirements. The rest of the integration is done via notifications.
The landing page can also used for adding new fields to gather more information from the subscriber; for example: what is the favored region. When a subscriber provides the details on the landing page, the solution generates a notification to the configured operations contact. The sample has both the email and Azure storage queue notification implementation. The operations team then provisions the required resources, on-boards the customer using their internal processes, and then comes back to the generated notification and clicks on the link to activate the subscription.
Please see my overview for the integration points in Integrating a software as a service with Microsoft commercial marketplace.
Remember, this scenario is useful when there is a human element in the mix, for situations such as:
activate
operation on the Fulfillment API.I am assuming you have already cloned the code in this repo. Open the solution in Visual Studio, and follow the steps for deploying the solution starting from this step.
The following is how my Visual Studio Publish profile looks:
Make sure you have Azure Tools or Azure App Service extension.
:warning: Important: Open Visual Studio Code at the src/CommandSenter folder, not at the repo root.
Create a new Web App with the Azure App Service extension.
Deploy to the new Web App with the extension.
Alternatively, follow the tutorial, and modify the process as you see fit.
I usually maintain a separate Azure Active Directory tenant (directory) for my application registrations. If you want to register the apps on your default directory, you can skip the following steps and go directly to Registering the Apps.
Login to the Azure Portal.
Click Create a Resource
and type in Azure Active Directory
in the search
box:
Select Create Directory
then fill in the details as you see fit after
clicking the Create
button
Switch to the new directory.
Select the new directory, if it does not show under "Favorites" check "All directories":
Once you switch to the new directory (or if you have not created a new one, and decided to use the existing one instead), select the Active Directory service (1 on the image below). If you do not see it, find it using "All services" (2 on the image below).
Click on "App registrations", and select "New registration". You will need to create two apps.
As I mentioned in the landing page and webhook sections above, I recommend registering two applications:
For the Landing Page: Commercial marketplace SaaS offers are required to have
a landing page, authenticating through Azure Active Directory. Register it as
described in the
documentation.
Make sure you register a multi-tenant application, you can find the
differences in the
documentation.
Select the "ID tokens" on the "Authentication" page. Also, add two Redirect
URLs: the base /
URL of the web app and another web app URL with
/signin-oidc
added.
To authenticate marketplace fulfillment APIs, you can register a single tenant application.
Follow the steps in the
tutorial,
and grab an API Key. Set the value of the ApiKey in the configuration section,
"CommandCenter:Mail", either using the user-secrets method or in the
appconfig.json
file.
Create an Azure Storage account following the steps here. The solution uses the storage account to keep references to the operations returned by actions done on the fulfillment API, as well as offering a place to store Leads generated from the Marketplace offer (via Table Storage).
You will need to modify the settings with the values for the services you have created above.
You will need to replace the values marked as CHANGE
, either by editing the
appconfig.json
file in the solution, or by using dotnet user-secrets
if you are planning share your work publicly.
Setting | Change/Keep | Notes |
---|---|---|
AzureAd:Instance | Keep | This is used by the library |
AzureAd:Domain | Change | You can find this value on the "Overview" page of the Active Directory you have registered your applications in. If you are not using a custom domain, it is in the format of \<tenant name>.onmicrosoft.com |
AzureAd:TenantId | Keep | Common authentication endpoint, since this is a multi-tenant app |
AzureAd:ClientId | Change | Copy the clientId of the multi-tenant app from its "Overview" page |
AzureAd:CallbackPath | Keep | Default oidc sign in path |
AzureAd:SignedOutCallbackPath | Keep | Default sign out path |
MarketplaceClient:ClientId | Change | Copy the clientId of the single-tenant app from its "Overview" page. This AD app is for calling the Fulfillment API |
MarketplaceClient:TenantId | Change | Copy the tenantId of the single-tenant app from its "Overview" page. |
MarketplaceClient:ClientSecret | Change | Go to the "Certificates & secrets" page of the single-tenant app you have registered, create a new client secret, and copy the value to the clipboard, then set the value for this setting. |
WebHookTokenParameters:Instance | Keep | This is used by the library |
WebHookTokenParameters:TenantId | Change | Set the same value as MarketplaceClient:TenantId |
WebHookTokenParameters:ClientId | Change | Set the same value as MarketplaceClient:ClientId |
CommandCenter:OperationsStoreConnectionString | Change | Copy the connection string of the storage account you have created in the previous step. Please see Client library documentation for details |
CommandCenter:Mail:OperationsTeamEmail | Change | Use this section if ActiveNotificationHandler is EmailNotifications. The sample sends emails to this address. |
CommandCenter:Mail:FromEmail | Change | Use this section if ActiveNotificationHandler is EmailNotifications. Sendgrid requires a "from" email address when sending emails. |
CommandCenter:Mail:ApiKey | Change | Use this section if ActiveNotificationHandler is EmailNotifications. Sendgrid API key. |
CommandCenter:CommandCenterAdmin | Change | Change it to the email address you are logging on to the dashboard. Only the users with the domain name of this email is authorized to use the dashboard to display the subscriptions. |
CommandCenter:ShowUnsubscribed | Change | Change true or false, depending on if you want to see the subscriptions that are not active. |
CommandCenter:ActiveNotificationHandler | Change | Active notification handler the solution uses. It can be EmailNotifications or AzureQueueNotifications. |
CommandCenter:AzureQueue:StorageConnectionString | Change | Use this section if ActiveNotificationHandler is AzureQueueNotifications. Add the storage account connection string for the queue. |
CommandCenter:AzureQueue:QueueName | Change | Use this section if ActiveNotificationHandler is AzureQueueNotifications. Name of the queue the messages will go to. |
CommandCenter:EnableDimensionMeterReporting | Change | Use this section to enable manually sending usage events on a dimension for a customer subscription through this App. Default: false, change to true if there is at least one dimension enabled on an Offer-Plan and would like to trigger usage events manually. More information on Marketplace Metering Service dimensions. |
CommandCenter:Dimensions:DimensionId | Change | Use this section if the above EnableDimensionMeterReporting setting is true. Add DimensionId of the enabled custom meter. |
CommandCenter:Dimensions:PlanIds | Change | Use this section if the above EnableDimensionMeterReporting setting is true. Add PlanId's of the plans for which the above DimensionId is enabled. |
CommandCenter:Dimensions:OfferIds | Change | Use this section if the above EnableDimensionMeterReporting setting is true. Add OfferId's of the plans for which the above DimensionId is enabled. |
Once your AAD directory, AAD applications, and web application are setup and ready to use, an offer must be created in the Commercial Marketplace Portal in the Microsoft Partner Center.
Documentation on creating an offer can be found on Microsoft Docs: Create a New Saas Offer in the Commercial Marketplace. Documentation is also available for all fields and pages for the offer on Docs as well.
You will need the following information to complete the offer:
/
/landingpage
api/webhook
/privacy
/support
Additionally, you will need assets, such as logos and screenshots, to complete
the offer listing as well. They can be found in this code repository under
/resources
.
These are sample configuration values for the offer to pass certification of the sample SaaS solution to help you get started with a sample offer.
:warning: IMPORTANT: This is just meant to be a sample. You will need to make adjustments based on your specific offer, even for this sample (e.g. contact information). Real information needs to be entered; using "Lorem ipsum" style information will not pass the certification steps if you want to preview your offer in the marketplace. Again, reference the Microsoft Docs for a more thorough overview of each section.
Selling Through Microsoft: to simplify for this sample, choose "Yes".
Customer Leads: unless you already have a CRM or other system setup that you'd like to use, choose Azure Table storage and use the connection string for the storage account you created earlier:
Save Draft: Between each screen, be sure to save a draft.
This is where most of your configuration for the offer will be. Pay attention to all the fields, taking care to fill them out as fully as you can.
https://{YOUR_APP_SERVICE_NAME}.azurewebsites.net/privacy
.https://{YOUR_APP_SERVICE_NAME}.azurewebsites.net/support
.resources/OfferListing-SupportDocuments-SupportInformation.pdf
provided in
this repository and name it "Support Information".resources/
in this repository. They are named according to their
sizes.resources/
directory – name it "All Offer Subscriptions".This page has the landing page and webhook configuration for your offer that was deployed in earlier steps.
https://{YOUR_APP_SERVICE_NAME}.azurewebsites.net/LandingPage
.https://{YOUR_APP_SERVICE_NAME}.azurewebsites.net/api/webhook
.Creating a new plan:
Plan Listing:
Pricing and Availability:
Markets: Choose the markets in which this plan will be available:
Pricing - Pricing Model: choose "Flat Rate".
Pricing - Billing Term: choose "Monthly" and set the cost to \$0.
Plan Visibility: set to "Private".
Restricted Audience: use the Tenant ID that hosts the Single-Tenant and Multi-Tenant AAD applications (the same that was used in previous steps).
Save Draft
Nothing needs to be configured here for the purpose of this solution.
Nothing needs to be configured here for the purpose of this solution.
Under Offer Overview, verify that all available information looks correct.
Then choose to Review and Publish
the offer to start the certification
process. Correct any errors that come back and work to the Publisher Signoff
step; this is where you'll be able to sign up for your offer before going live
(with your real SaaS offer in the future).
Customer searches for the offer on Azure Portal
Go to Azure Portal and add a resource
Find the search text box
Type in your offer name
Select the plan
Subscribe
Find the subscription after the deployment is complete, and go the subscription
Subscription details, notice it is not active yet
Landing page
Purchaser submits the form, and Contoso ops team receives an email
Contoso team takes the appropriate action to qualify and onboard the customer