Skip to content

Commit

Permalink
Sample solution
Browse files Browse the repository at this point in the history
  • Loading branch information
gjoshevski committed Jul 28, 2020
1 parent 1439324 commit 408f343
Show file tree
Hide file tree
Showing 26 changed files with 1,311 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.vscode
.aws-sam
.DS_Store
packaged.yaml
deploy.sh
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
[email protected] with any additional questions or comments.
[email protected] with any additional questions or comments.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ If you discover a potential security issue in this project we ask that you notif
See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.

We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.

200 changes: 193 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,203 @@
## My Project
# AWS Marketplace - Serverless integration for SaaS products (Example)

TODO: Fill this README out!
![](misc/banner.png)

Be sure to:
This project provides example of serverless integration for SaaS products listed on the AWS Marketplace.

If you are a new seller on AWS Marketplace, we advise you to check the following resources:

* [SaaS Product Requirements & Recommendations](https://docs.aws.amazon.com/marketplace/latest/userguide/what-is-marketplace.html) : This document outlines the requirements that must be met before gaining approval to publish a SaaS product to the catalog.
* [SaaS Listing Process & Integration Guide](https://awsmp-loadforms.s3.amazonaws.com/AWS+Marketplace+-+SaaS+Integration+Guide.pdf) : This document outlines what is required to integrate with Marketplace for each SaaS pricing model. You will find integration diagrams, codes examples, FAQs, and additional resources.
* [SaaS Integration Video](https://www.youtube.com/watch?v=glG44f-L8us) : This video guides you through the requirements and steps needed to integrate.
* [SaaS Pricing Video](https://www.youtube.com/watch?v=E0uWp8nhzAk) : This video guides you through the pricing options available when choosing to list a SaaS product.
* [AWS Marketplace - Seller Guide](https://docs.aws.amazon.com/marketplace/latest/userguide/what-is-marketplace.html) : This document covers more information about creating a SaaS product, pricing, and setting up your integration.

# Project Structure

The sample in this repository demonstrates how to use AWS SAM (Serverless application mode) to integrate your SaaS product with AWS Marketplace and how to perform:

- [Register new customers](#register-new-customers)
- [Grant and revoke access to your product](#grant-and-revoke-access-to-your-product)
- [Metering for usage](#metering-for-usage)


## Register new customers

With SaaS subscriptions and SaaS contracts, your customers subscribe to your products through AWS Marketplace, but access the product on environment you manage in your AWS account. After subscribing to the product, your customer is directed to a website you create and manage as a part of your SaaS product to register their account and configure the product.

When creating your product, you provide a URL to your registration landing page. AWS Marketplace uses that URL to redirect customers to your registration landing page after they subscribe. On your software's registration URL, you collect whatever information is required to create an account for the customer. AWS Marketplace recommends collecting your customer’s email addresses if you plan to contact them through email for usage notifications.

The registration landing page needs to be able to identify and accept the x-amzn-marketplace-token token in the form data from AWS Marketplace with the customer’s identifier for billing. It should then pass that token value to the AWS Marketplace Metering Service and AWS Marketplace Entitlement Service APIs to resolve for the unique customer identifier and corresponding product code.

![](misc/onbording.gif)

> NOTE: Deploying the static landing page is optional.
You can choose to use your existing SaaS registration page, after collecting the data you should call the register new subscriber endpoint. Please see the deployment section.

### Implementation

In this sample we created CloudFront Distribution, which can be configured to use domain/CNAME by your choice. The POST request coming from AWS Marketplace is intercepted by the Edge `src/lambda-edge/edege-redirect.js`, which transforms the POST request to GET request, and passes the x-amzn-marketplace-token in the query string.
We have created static HTML page hosted on S3 which takes the users inputs defined in the html form and submits them to marketplace/customer endpoint.

The handler for the marketplace/customer endpoint is defined in the `src/register-new-subscriber.js` file, where we call the `resolveCustomer` and validate the token. If the token is valid the customer record is created in the `AWSMarketplaceSubscribers` DynamoDB table and the new customer data are stored.

![](misc/Onbording-CF.png)

## Grant and revoke access to your product

### Grant access to new subscribers

Once the resolveCustomer endpoint return successful response, the SaaS vendors must to provide access to the solution to the new subscriber.
Based on the type of listing contract or subscription we have defined different conditions in the `environment-provisioning.js` stream handler that is executed on adding new or updating existing rows.

In our implementation the Marketplace Tech Admin (The email adress you have entered when deplyoing), will receive email when new environment needs to be provisioned or existing environment needs to be updated. AWS Marketplace strongly recommends automating the access and environment management which can be achieved by modifying the `environment-provisioning.js` function.

The property successfully subscribed is set when successful response is returned from the SQS entitlement handler for SaaS Contract based listings or after receiving **subscribe-success message from the Subscription SNS Topic in the case of AWS SaaS subscriptions in the `subscription-sqs-handler.js`.


### Update entitlement levels to new subscirbers (SaaS Contracts only)

Each time the entitlement is update we receive message on the SNS topic.
The lambda function `eintilemnet-sqs.js` on each message is calling the marketplaceEntitlementService and storing the response in the dynamoDB.

We are using the same DynamoDB stream to detect changes in the entailment for SaaS contracts. When the entitlement is update notification is sent to the `MarketplaceTechAdmin`.


### Revoke access to customers with expired contracts and canceled subscriptions

The revoke access logic is implemented in a similar manner as the grant access logic.

In our implementation the `MarketplaceTechAdmin` receives email when the contract expires or the subscription is cancelled.
AWS Marketplace strongly recommends automating the access and environment management which can be achieved by modifying the `environment-provisioning.js` function.

## Metering for usage

For SaaS subscriptions, the SaaS provider must meter for all usage, and then customers are billed by AWS based on the metering records provided. For SaaS contracts, you only meter for usage beyond a customer’s contract entitlements. When your application meters usage for a customer, your application is providing AWS with a quantity of usage accrued. Your application meters for the pricing dimensions that you defined when you created your product, such as gigabytes transferred or hosts scanned in a given hour.

### Implementation

We have created MeteringSchedule CloudWatch Event rule that is **triggered every hour**. The `metering-hourly-job.js` gets triggered by this rule and it's querying all of the pending/unreported metering records from the `AWSMarketplaceMeteringRecords` table using the PendingMeteringRecordsIndex.
All of the pending records are aggregated based on the customerIdentifier and dimension name, and sent to the SQSMetering queue.
The records in the `AWSMarketplaceMeteringRecords` table are expected to be inserted programaticaly by your SaaS aplication. In this case you will have to give permissions to the service in charge of collecting usage data in your existing SaaS product to be able to write to `AWSMarketplaceMeteringRecords` table.

The lambda function `metering-sqs.js` is sending all of the queued metering records to the AWS marketplace Metering API.
After every call to the `batchMeterUsage` endpoint the rows are updated in the AWSMarketplaceMeteringRecords table, with the response returned from the Metering Service, which can be found in the `metering_response` field. If the request was unsuccessful the metering_failed value with be set to true and you will have to investigate the issue the error will be also stored in the `metering_response` field.

The new records in the AWSMarketplaceMeteringRecords table should be stored in the following format:


```javascript
{
"create_timestamp": 113123,
"customerIdentifier": "ifAPi5AcF3",
"dimension_usage": [
{
"dimension": "users",
"value": 3
},
{
"dimension": "admin_users",
"value": 1
}
],
"metering_pending": "true"
}
```

Where the create_timestamp is the sort key and customerIdentifier is the partition key, and they are both forming the Primary key.
The AWSMarketplaceMeteringRecords table

After the record is submitted to AWS Marketplace API, it will be updated and I.E. will look like this:

```javascript
{
"create_timestamp": 113123,
"customerIdentifier": "ifAPi5AcF3",
"dimension_usage": [
{
"dimension": "admin_users",
"value": 3
}
],
"metering_failed": false,
"metering_response": "{\"Results\":[{\"UsageRecord\":{\"Timestamp\":\"2020-06-24T04:04:53.776Z\",\"CustomerIdentifier\":\"ifAPi5AcF3\",\"Dimension\":\"admin_users\",\"Quantity\":3},\"MeteringRecordId\":\"35155d37-56cb-423f-8554-5c4f3e3ff56d\",\"Status\":\"Success\"}],\"UnprocessedRecords\":[]}"
}
```

## Deploy the sample application

The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API.

To use the SAM CLI, you need the following tools.

* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)
* Node.js - [Install Node.js 10](https://nodejs.org/en/), including the NPM package management tool.
* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community)

To build and deploy your application for the first time, run the following in your shell:

```bash

#Replace all `<PLACEHOLDER_VALUES>` with their `actual values` (e.g. `<PROJECT_NAME>` with `My Cool Project`).

sam build
sam package --output-template-file packaged.yaml --s3-bucket <DEPLOYMENT_ARTEFACT_S3_BUCKET>

sam deploy --template-file packaged.yaml --stack-name <STACK_NAME> --capabilities CAPABILITY_IAM \
--region us-east-1 \
--parameter-overrides \
ParameterKey=WebsiteS3BucketName,ParameterValue=<WEBSITE_BUCKET_NAME> \
ParameterKey=ProductCode,ParameterValue=<MARKETPLACE_PRODUCT_CODE> \
ParameterKey=EntitlementSNSTopic,ParameterValue=<MARKETPLACE_ENTITLEMENT_SNS_TOPIC> \
ParameterKey=SubscriptionSNSTopic,ParameterValue=<MARKETPLACE_SUBSCRIPTION_SNS_TOPIC> \
ParameterKey=MarketplaceTechAdminEmail,ParameterValue=<MARKETPLACE_SUBSCRIPTION_SNS_TOPIC> \

aws s3 cp ./web/ s3://<WEBSITE_BUCKET_NAME>/ --recursive
```
### List of parameters

Parameter name | Description
------------ | -------------
WebsiteS3BucketName | S3 bucket to store the HTML files; Mandatory if CreateRegistrationWebPage is set to true;
NewSubscribersTableName | Use custome name for the New Subscribers Table; Default value: AWSMarketplaceSubscribers
AWSMarketplaceMeteringRecordsTableName | Use custome name for the Metering Records Table; Default value: AWSMarketplaceMeteringRecords
TypeOfSaaSListing | allowed values: contracts_with_subscription, contracts, subscriptions; Default value: contracts_with_subscription
ProductCode | Product code provided from AWS Marketplace
EntitlementSNSTopic | SNS topic ARN provided from AWS Marketplace
SubscriptionSNSTopic | SNS topic ARN provided from AWS Marketplace
CreateRegistrationWebPage | true or false; Default value: true
MarketplaceTechAdminEmail | Email to be notified on changes requring action


### Diagram of created resources

Based on the value of the **TypeOfSaaSListing** parameter different set of resources will be created.

In the case of *contracts_with_subscription* all of the resources depicted on the diagram below will be created.

In the case of a *contracts*, the resources market with orange circless will not be created.

In the case of a *subscriptions* the resources market with purple circless will not be created.

The landing page is optional. Use the CreateRegistrationWebPage parameter.


![](misc/Serverless-MP.png)


## Cleanup

To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following:

```bash
aws cloudformation delete-stack --stack-name app
```

* Change the title in this README
* Edit your repository description on GitHub

## Security

See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.

## License

This library is licensed under the MIT-0 License. See the LICENSE file.

This library is licensed under the MIT-0 License. See the LICENSE file.
Binary file added misc/Onbording-CF.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions misc/Onbording.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<mxfile host="drawio.corp.amazon.com" modified="2020-06-26T16:01:39.020Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:68.0) Gecko/20100101 Firefox/68.0" etag="7zZT8eVGUde7MjI6KHlQ" version="12.4.8" type="device"><diagram id="vOaW_M2EUiB265jeho9g" name="Page-1">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</diagram></mxfile>
Loading

0 comments on commit 408f343

Please sign in to comment.