# Building Service-Based Atlas Cluster Management

## Developer Productivity

MongoDB Atlas is changing the database industry standards when it comes to database provisioning, maintenance, and scaling, as it just works. However, even superheroes like Atlas know that with Great Power Comes Great Responsibility.

For this reason, Atlas provides Enterprise-grade security features for your clusters and a set of user management roles that can be assigned to log in users or programmatic API keys.

However, since the management roles were built for a wide use case of
our customers there are some customers who need more fine-grained
permissions for specific teams or user types. Although, at the moment
the management roles are predefined, with the help of a simple Realm
service and the programmatic API we can allow user access for very
specific management/provisioning features without exposing them to a
wider sudo all ability.

To better understand this scenario I want to focus on the specific use
case of database user creation for the application teams. In this
scenario perhaps each developer per team may need its own user and
specific database permissions. With the current Atlas user roles you
will need to grant the team a `Cluster Manager Role`, which allows them
to change cluster properties as well as pause and resume a cluster. In
some cases this power is unnecessary for your users.

> If you haven't yet set up your free cluster on MongoDB Atlas, now is a great time to do so. You have all the instructions in this blog post.

## Proposed Solution

Your developers will submit their requests to a pre-built service which
will authenticate them and request an input for the user description.
Furthermore, the service will validate the input and post it to the
Atlas Admin API without exposing any additional information or API keys.

The user will receive a confirmation that the user was created and ready
to use.

## Work Flow

To make the service more accessible for users I am using a form-based
service called Typeform, you can choose many other available form builders (e.g Google Forms). This form will gather the information and password/secret for the service authentication from the user and pass it to the Realm webhook which will perform the action.

  

The input is an Atlas Admin API user object that we want to create, looking something like the following object:

``` javascript
{
  "databaseName": ,
  "password": ,
  "roles": ...],
  "username":  
}
```

For more information please refer to our Atlas Role Based Authentication
[documentation.

## Webhook Back End

This section will require you to use an existing Realm Application or
build a new one.

MongoDB Realm is a serverless platform and mobile database. In our case
we will use the following features:

-   Realm webhooks
-   Realm context HTTP Module
-   Realm Values/Secrets

You will also need to configure an Atlas Admin API key for the relevant Project and obtain it's Project Id. This can be done from your Atlas project url (e.g., `https://cloud.mongodb.com/v2/#clusters`).

The main part of the Realm application is to hold the Atlas Admin API keys and information as private secure secrets.

This is the webhook configuration that will call our Realm Function each
time the form is sent:

The function below receives the request. Fetch the needed API
information and sends the Atlas Admin API command. The result of which is
returned to the Form.

``` javascript
// This function is the webhook's request handler.
exports = async function(payload, response) {
     // Get payload
     const body = JSON.parse(payload.body.text());

     // Get secrets for the Atlas Admin API
     const username = context.values.get("AtlasPublicKey");
     const password = context.values.get("AtlasPrivateKey");
     const projectID = context.values.get("AtlasGroupId");

     //Extract the Atlas user object description
     const userObject = JSON.parse(body.form_response.answers0].text);

     // Database users post command
     const postargs = {
     scheme: 'https',
     host: 'cloud.mongodb.com',
     path: 'api/atlas/v1.0/groups/' + projectID + '/databaseUsers',
     username: username,
     password: password,
     headers: {'Content-Type': ['application/json'], 'Accept': ['application/json']},
     digestAuth:true,
     body: JSON.stringify(userObject)};

     var res = await context.http.post(postargs);
     console.log(JSON.stringify(res));

     // Check status of the user creation and report back to the user.
     if (res.statusCode == 201)
     {
       response.setStatusCode(200)
       response.setBody(`Successfully created ${userObject.username}.`);
     } else {
       // Respond with a malformed request error
       response.setStatusCode(400)
       response.setBody(`Could not create user ${userObject.username}.`);
     }
};
```

Once the webhook is set and ready we can use it as a webhook url input
in the Typeform configuration.

The Realm webhook url can now be placed in the Typform webhook section.
Now the submitted data on the form will be forwarded via Webhook
integration to our webhook:

![

To strengthen the security around our Realm app we can strict the
allowed domain for the webhook request origin. Go to Realm application
"Manage" - "Settings" \> "Allowed Request Origins":

We can test the form now by providing an Atlas Admin API user
object.

If you go to the Atlas UI under the Database Access tab you will see the
created user.

## Summary

Now our developers will be able to create users quickly without being
exposed to any unnecessary privileges or human errors.

The webhook code can be converted to a function that can be called from
other webhooks or triggers allowing us to build sophisticated controlled
and secure provisioning methods. For example, we can configure a
scheduled trigger that pulls any newly created clusters and continuously
provision any new required users for our applications or edit any
existing users to add the needed new set of permissions.

MongoDB Atlas and Realm platforms can work in great synergy allowing us to bring our devops and development cycles to the
next level.