Pax8 leverages Acronis Cyber Platform to develop customized commitment models in its cloud marketplace
Pax8 is the leader in cloud distribution for the IT channel. Born out of the challenges emerging within the traditional distribution ecosystem and the growth of cloud delivery and consumption models, Pax8’s mission is to enable the IT channel to simplify cloud buying through billing, provisioning, automation, PSA integrations, and pre-and-post sales support. The company is an award-winning disruptor in the market, earning accolades like NexGen’s Best in Show two years in a row, Biggest Buzz at IT Nation, CRN’s Coolest Cloud Vendor, Best in Show at two consecutive XChange conferences.

Pax8 recognized early on that it needed to make the right investments in technology and integrations to provide its MSP (Managed Service Provider) partners as well as third-party solutions vendors an optimal experience. This ultimately involved automation and APIs – the cornerstone to interacting with the Pax8 cloud marketplace. According to Craig Donnovan, Pax8’s VP of Partner Solutions, “Integrations are key to the MSP space. This is because an MSP is constantly managing a complex ecosystem of billing and quoting tools, inventory and ticket tracking, ordering products and provisioning. Often these systems are in different locations which adds to the complexity.”
This is where the Acronis Cyber Platform came into the picture. Pax8’s MSP partners had been asking about Acronis and were particularly drawn to its flexible architecture that housed multiple products under a single, multi-tenant management platform that let MSP partners provide cyber protection solutions and backup to the Acronis cloud or their own cloud.
In order to get the suite of Acronis Cyber Protection products into the Pax8 marketplace, the two companies worked together on the integration. Pax8 was one of the first companies to take advantage of the Acronis Cyber Platform, a set of open APIs and SDKs designed to enable third parties to easily integrate with Acronis solutions. The technical integration took about 2 months in total. Because the Acronis APIs were easy to use and well documented and the Acronis development team was very responsive and collaborative, Pax8 was able to do some custom development which was a first of a kind for the company. Specifically, Pax8 was able to implement both commitment and non-commitment models on a single vendor. Essentially, a partner is therefore able to commit to a tier upfront and get a discount based on the aggregate number of licenses are being deployed.
Watch the video case study for more details.
Let’s drill down one of the tasks you may perform as a part of MSP automation tasks.
Enabling the backup service for a user in a customer tenant
This guide was written for the Acronis Cyber Cloud 8.0. Please, check the possible changes API paramters and API flows in the recent blog posts.
Let’s assume that the partner tenant has the following:
- The backup service is enabled.
- The Workstations and Backup storage offering items are enabled.
- An administrator account is activated. This account will be used to perform the operations in the tenant via the API.
- A customer and a user are absent
To enable the backup services for a user in a customer tenant we need to perform the following tasks:
- Create a customer tenant in your partner tenant:
POST /tenants
- Enable the Workstations and Backup storage offering items for the customer tenant:
GET /tenants/{partner_id}/offering_items/available_for_child
PUT /tenants/{customer_id}/offering_items
This automatically enables the backup service for the tenant. - Create a user account in the customer tenant:
GET /users/check_login
POST /users
- Assign the Backup User role to this account:
PUT /users/{user_id}/access_policies
This automatically enables the backup service with the Workstations and Backup storage offering items for the account and set the same quotas for the account as the ones set for the tenant. - Activate the account by setting its password:
POST /users/{user_id}/password
Let’s use the Python shell to perform step-by-step task. As well a full script for this task can be created. For more examples of APIs usage from Python, please see here.
At first, we need to receive an authentication token and retrieve partner tenant ID - partner_id. More detailed information how to receive an authentication token can be found here and here.
As you expected to be a partner, you can retrieve a tenant id using GET /users/me
request. It can be found in this tutorial.
Thus, you have the authentication token and the partner_id
>>> auth # the 'Authorization' header value with the access token
{'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImMwMD...'}
>>> partner_id # the UUID of the tenant to which the token provides access
'ede9f834-70b3-476c-83d9-736f9f8c7dae'
Creating a customer tenant in the partner tenant
Define a variable named customer
, and then assign an object containing the minimum information about the customer tenant to this variable:
>>> customer = {
... 'name': 'Customer, Inc',
... 'kind': 'customer',
... 'parent_id': partner_id,
... }
Name | Value type | Required | Description |
---|---|---|---|
name |
string | Yes | A tenant name. |
kind |
string | Yes | A tenant type. |
parent_id |
UUID string | Yes | The UUID of a tenant where this tenant will be created. |
Convert the customer
object to a JSON text:
>>> customer = json.dumps(customer, indent=4)
>>> print(customer)
{
"name": "Customer, Inc",
"kind": "customer",
"parent_id": "ede9f834-70b3-476c-83d9-736f9f8c7dae"
}
Send a POST request with the JSON text to the /tenants
endpoint:
>>> response = requests.post(
... f'{base_url}/tenants',
... headers={'Content-Type': 'application/json', auth},
... data=customer,
... )
Check the status code of the response:
>>> response.status_code
201
The 201 code means that the platform has created the customer tenant in the trial mode.
A different code means that an error has occurred. For the details, refer to the "Errors" section of the endpoint.
Also, the response body contains the tenant information formatted as a JSON text. When converted to an object, it will look as follows:
>>> pprint.pprint(response.json())
{'ancestral_access': True,
'brand_id': 3579,
'contact': {...},
'customer_id': None,
'customer_type': 'default',
'default_idp_id': '11111111-1111-1111-1111-111111111111',
'enabled': True,
'has_children': False,
'id': '95303d96-628c-4265-9afa-07bee3fccf39',
'internal_tag': None,
'kind': 'customer',
'language': 'en',
'name': 'Customer, Inc',
'owner_id': None,
'parent_id': 'ede9f834-70b3-476c-83d9-736f9f8c7dae',
'update_lock': {'enabled': False, 'owner_id': None},
'version': 1}
Define a variable named customer_id
, and then assign the UUID of the created tenant to this variable:
>>> customer_id = response.json()['id']
>>> customer_id
'95303d96-628c-4265-9afa-07bee3fccf39'
For more information about how the API represents tenants and what operations are available with them, see here.
Enabling offering items for the customer tenant
Fetch the list of offering items available for customer tenants of the partner tenant by sending a GET request to the /tenants/{partner_id}/offering_items/available_for_child endpoint
. The endpoint URL should contain a kind query parameter set to customer:
>>> response = requests.get(
... f'{base_url}/tenants/{partner_id}/offering_items/available_for_child',
... headers=auth,
... params={'kind': 'customer'},
... )
Check the status code of the response:
>>> response.status_code
200
The 200 code means that the response body text contains an encoded JSON object consisting of the items member. The items member is an array of objects of offering items that can be enabled for customer tenants. If no items can be enabled, this array is empty.
A different code means that an error has occurred. For the details, refer to the "Errors" section of the endpoint.
Convert the JSON text to an object, and then store the value of the object's items key in a variable named offering_items:
>>> offering_items = response.json()['items']
>>> pprint.pprint(offering_items)
[{'application_id': '6e6d758d-8e74-3ae3-ac84-50eb0dff12eb',
'infra_id': '019097a6-114f-4418-bd54-e01ef049f209',
'locked': False,
'measurement_unit': 'bytes',
'name': 'storage',
'quota': {'overage': None, 'value': None, 'version': 0},
'status': 1,
'type': 'infra'},
{'application_id': '6e6d758d-8e74-3ae3-ac84-50eb0dff12eb',
'locked': False,
'measurement_unit': 'quantity',
'name': 'workstations',
'quota': {'overage': None, 'value': None, 'version': 0},
'status': 1,
'type': 'count'}, ...]
In this list, find the objects where the value of the name
key is either workstations
(the Workstations offering item) or storage
(the Backup storage offering item) and store them in the following items
object:
>>> item_names = {'workstations', 'storage'}
>>> items_to_enable = [item for item in offering_items if item['name'] in item_names]
>>> items = {'offering_items': items_to_enable}
Convert the items
object to a JSON text:
>>> items = json.dumps(items, indent=4)
Send a PUT request with the JSON text to the /tenants/{customer_id}/offering_items
endpoint:
>>> response = requests.put(
... f'{base_url}/tenants/{customer_id}/offering_items',
... headers={'Content-Type': 'application/json', auth},
... data=items,
... )
Check the status code of the response:
>>> response.status_code
200
The 200 code means that the platform has enabled the backup service with the specified offering items for the tenant.
A different code means that an error has occurred. For the details, refer to the "Errors" section of the endpoint.
Creating a user account in the customer tenant
Define a variable named user_login
, and then assign the login for a new user account to this variable:
>>> user_login = 'RichardDoe'
Check if this login is available in the platform. To do this, send a GET request to the /users/check_login
endpoint. The endpoint URL should contain a username
query parameter set to the login:
>>> response = requests.get(
... f'{base_url}/users/check_login',
... headers=auth,
... params={'username': user_login},
... )
Check the status code of the response:
>>> response.status_code
204
The 204 code means that the login is not taken by any other account registered in the platform.
A different code means that an error has occurred. For the details, refer to the "Errors" section of the endpoint.
Define a variable named account
, and then assign an object containing the minimum information about the user account to this variable:
>>> account = {
... 'tenant_id': customer_id,
... 'login': user_login,
... 'contact': {
... 'email': 'richard.doe@example.com',
... 'firstname': 'Richard',
... 'lastname': 'Doe',
... },
... }
Name | Value type | Required | Description |
---|---|---|---|
tenant_id |
string | Yes | The UUID of a tenant where an account will be created. |
login |
string | Yes | An account login. |
contact |
object | Yes | The contact information of an account. |
email |
string | Yes | An email address that will be used for account activation and service notifications. |
firstname |
string | No | The first name of a user. |
lastname |
string | No | The last name of a user. |
Convert the account
object to a JSON text:
>>> account = json.dumps(account, indent=4)
>>> print(account)
{
"tenant_id": "95303d96-628c-4265-9afa-07bee3fccf39",
"login": "'RichardDoe'",
"contact": {
"email": "richard.doe@example.com",
"firstname": "Richard",
"lastname": "Doe"
}
}
Send a POST request with the JSON text to the /users
endpoint:
>>> response = requests.post(
... f'{base_url}/users',
... headers={'Content-Type': 'application/json', auth},
... data=account,
... )
Check the status code of the response:
>>> response.status_code
200
The 200 code means that the platform has created, in the customer tenant, a non-activated user account with the RichardDoe
login and a personal tenant. The personal tenant is for managing the account's offering item quotas. A different code means that an error has occurred. For the details, refer to the "Errors" section of the endpoint.
Also, the response body contains the account information formatted as a JSON text. When converted to an object, it will look as follows:
>>> pprint.pprint(response.json())
{'activated': False,
'business_types': [],
'contact': {'email': 'richard.doe@example.com',
'firstname': 'Richard',
'lastname': 'Doe', ...},
'created_at': '2019-07-09T06:03:00.502053+00:00',
'enabled': True,
'id': 'fb00afe1-c8c5-43c6-9ca5-76ea33091715',
'idp_id': '11111111-1111-1111-1111-111111111111',
'language': 'en',
'login': 'RichardDoe',
'notifications': ['quota', 'reports', 'backup_daily_report'],
'personal_tenant_id': 'b1ab6d40-f88e-46a3-9092-2aadeae0b888',
'tenant_id': '95303d96-628c-4265-9afa-07bee3fccf39',
'terms_accepted': False,
'version': 1}
Define a variable named user_id
, and then assign the UUID of the created user account to this variable:
>>> user_id = response.json()['id']
>>> user_id
'fb00afe1-c8c5-43c6-9ca5-76ea33091715'
For more information about how the API represents user accounts and what operations are available with them, see here.
Assigning the backup user role to the user account
Define a variable named roles
, and then assign the following object containing the role information to this variable:
>>> roles = {
... 'items': [
... {
... 'tenant_id': customer_id,
... 'trustee_id': user_id,
... 'role_id': 'backup_user',
... 'id': '00000000-0000-0000-0000-000000000000',
... 'issuer_id': '00000000-0000-0000-0000-000000000000',
... 'trustee_type': 'user',
... 'version': 0,
... },
... ],
... }
Name | Value type | Required | Description |
---|---|---|---|
items |
array of objects | Yes | The list of roles to be assigned to an account. |
tenant_id |
string | Yes | The UUID of a tenant where the account is registered. |
trustee_id |
string | Yes | The account UUID. |
role_id |
string | Yes | The internal name of a role. |
id , issuer_id |
string | Yes | The value must be 00000000-0000-0000-0000-000000000000 . |
trustee_type |
string | Yes | The value must be user . |
version |
number | Yes | The value must be 0 . |
Convert the roles
object to a JSON text:
>>> roles = json.dumps(roles, indent=4)
>>> print(roles)
{
"items": [
{
"tenant_id": "95303d96-628c-4265-9afa-07bee3fccf39",
"trustee_id": "fb00afe1-c8c5-43c6-9ca5-76ea33091715",
"role_id": "backup_user",
"id": "00000000-0000-0000-0000-000000000000",
"issuer_id": "00000000-0000-0000-0000-000000000000",
"trustee_type": "user",
"version": 0
}
]
}
Send a PUT request with the JSON text to the /users/{user_id}/access_policies
endpoint:
>>> response = requests.put(
... f'{base_url}/users/{user_id}/access_policies',
... headers={'Content-Type': 'application/json', auth},
... data=roles,
... )
Check the status code of the response:
>>> response.status_code
200
The 200 code means that the platform has assigned the specified role to the user account and enabled the backup service with the Workstations and Backup storage offering items for the account.
A different code means that an error has occurred. For the details, refer to the "Errors" section of the endpoint.
Activating the user account by setting its password
Define a variable named password
, and then assign an object containing the password
key with a password string to this variable:
>>> password = {'password': 'newUserPassword312'}
A password may be of any length and contain any unicode characters. If the password is empty, the user will be able to log in to the backup console by using a generated login URL only.
Convert the password
object to a JSON text:
>>> password = json.dumps(password, indent=4)
>>> print(password)
{
"password": "newUserPassword312"
}
Send a POST request with the JSON text to the /users/{user_id}/password
endpoint:
>>> response = requests.post(
... f'{base_url}/users/{user_id}/password',
... headers={'Content-Type': 'application/json', auth},
... data=password,
... )
Check the status code of the response:
>>> response.status_code
204
The 204 code means that the platform has set the specified password for the user account and activated this account.
So, we have created a customer, a user and enable backup for the user.
Summary
Now you know how to enable the backup service for a user in a customer tenant.
Start today, register on the Acronis Developer Portal and see the code samples available
Review solutions available in the Acronis Cyber Cloud Solutions Portal.