Create MongoDB Atlas Cluster With Terraform and AWS
Tags: luxoft, aws, mongodb, awscommunity
Published: 2024-03-31 22:39:27

This project aims to set up an Atlas MongoDB cluster with an AWS network peering to access the resources from AWS EC2, utilizing Terraform as an Infrastructure as a Code(IAC).

## Prerequisites Needed:

- Basic understanding of Terraform concepts and Terraform CLI installed.
- AWS VPC with subnets and route tables.
- MongoDB Atlas account.
- MongoDB Atlas Organization and a Project under your account with public and private keys with **Organization Project Creator** API key.

## Table of Contents

- [MongoDB Atlas API key for terraform](#MongoDB Atlas API key for terraform)
- [MongoDB atlas terraform provider](#infrastructure-setup)
- [Module for cluster resource](#jenkins-integration)
  - [Cluster Resources](#jenkins-installation)
  - [Users and Roles](#jenkins-configuration)
  - [Network peering with atlas](#jenkins-sonarqube-plugin)
- [Plan and apply](#jenkins-integration)
- [Resources](#Resources)

## MongoDB Atlas API key for terraform

1. Once you create an organization in Atlas, access to an organization or project can only be managed using the API key, so we need to create an API key.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3kfq9ua72mgnp1jkk7r.png)

2. API keys have two parts: a Public Key and a Private Key. These two parts serve the same function as a username and a personal API key when you make API requests to Atlas.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/to9zpay4fn2gxjesh9y6.png)

3. You must grant roles to API keys as you would for users to ensure the API keys can call API endpoints without errors. Here we will need an API key that will have **Organization Project Creator** permissions.
e.g.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jgill1j81kgguf6ux20f.png)

4. All API keys belong to the organization. You can give an API key access to a project. Each API key belongs to only one organization, but you can grant an API key access to any number of projects in that organization.

## Configuring the MongoDB atlas provider for Terraform

The Terraform MongoDB Atlas Provider is a plugin that allows you to manage MongoDB Atlas resources using Terraform.
Syntax:

> provider.tf

```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.4"
    }
    mongodbatlas = {
      source  = "mongodb/mongodbatlas"
      version = "~> 1.9"
    }
  }
}
```

```
provider "mongodbatlas" {
  public_key = "<YOUR PUBLIC KEY HERE>"
  private_key  = "<YOUR PRIVATE KEY HERE>"
}
```
In our case, all the required variables like Organization ID, Public key, and Private key are created in the AWS Systems Manager Parameter Store and are being referred to respectively.

```
data "aws_ssm_parameter" "private_key" {
  name            = "/atlas/private-key"
  with_decryption = true
}

data "aws_ssm_parameter" "public_key" {
  name            = "/atlas/public-key"
  with_decryption = true
}

data "aws_ssm_parameter" "atlas_organization_id" {
  name            = "/atlas/org-id"
  with_decryption = true
}
```

## Module for cluster resources

### Cluster resources

1. **mongodbatlas_project** provides a Project resource. This allows the project to be created.
2. **mongodbatlas_network_container** provides a Network Peering Container resource. The resource lets you create, edit, and delete network peering containers. You must delete network peering containers before creating clusters in your project. You can't delete a network peering container if your project contains clusters. The resource requires your Project ID.
3. **mongodbatlas_project_ip_access_list** provides an IP Access List entry resource that grants access from IPs, CIDRs, or AWS Security Groups (if VPC Peering is enabled) to clusters within the Project.

```
resource "mongodbatlas_project" "this" {
  name   = var.atlas_project_name
  org_id = var.atlas_organization_id
}

resource "mongodbatlas_network_container" "this" {
  atlas_cidr_block = var.atlas_cidr_block
  project_id       = mongodbatlas_project.this.id
  provider_name    = "AWS"
  region_name      = local.region_name
}

resource "mongodbatlas_project_ip_access_list" "this" {
  project_id = mongodbatlas_network_peering.this.project_id
  cidr_block = var.vpc_cidr_block
  comment    = "Grant AWS ${var.vpc_cidr_block} environment access to Atlas resources"
}

resource "aws_route" "this" {
  for_each                  = toset(var.private_route_table_ids)
  route_table_id            = each.value
  destination_cidr_block    = mongodbatlas_network_container.this.atlas_cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection_accepter.this.vpc_peering_connection_id
}

```
### Users and Roles
**mongodbatlas_custom_db_role** allows you to create custom roles in Atlas when the built-in roles don't include your desired set of privileges. Atlas applies each database user's custom roles together with:

- Any built-in roles you assign when you add a database user or modify a database user.
- Any specific privileges you assign when you add a database user or modify a database user. You can assign multiple custom roles to each database user.
E.g.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpsx8wf5jljbit61ktj1.png)

```
resource "mongodbatlas_custom_db_role" "roles" {
  for_each   = var.custom_roles
  project_id = module.atlas_project.project_id
  role_name  = each.value.role_name

  dynamic "actions" {
    for_each = each.value.actions
    content {
      action = actions.value.action
      resources {
        collection_name = actions.value.resources.collection_name
        database_name   = actions.value.resources.database_name
      }
    }
  }
}
```

**mongodbatlas_database_user** Creates database users to provide clients access to the database deployments in your project. A database user's access is determined by the roles assigned to the user. 
```
resource "mongodbatlas_database_user" "database_users" {
  for_each           = var.database_users
  username           = each.value.username
  password           = jsondecode(data.aws_secretsmanager_secret_version.creds.secret_string)[each.value.username]
  auth_database_name = "admin"
  project_id         = module.atlas_project.project_id

  dynamic "roles" {
    for_each = each.value.roles
    content {
      database_name = roles.value.database_name
      role_name     = roles.value.role_name
    }
  }
  depends_on = [mongodbatlas_custom_db_role.roles]
}
```
You can create and upload the username and password details as a key pair values in AWS secret manager using,

```
aws secretsmanager create-secret --name atlas-users --description "Mytest with multiples values" --secret-string file://secretmanagervalues.json
```
Where,
> file://secretmanagervalues.json

Which will have below values e.g.
`{"Juan":"mykey1","Pedro":"mykey2","Pipe":"mykey3"}`

### Network peering with atlas

**mongodbatlas_network_peering** Network peering establishes a private connection between your Atlas VPC and your cloud provider's VPC. The connection isolates traffic from public networks for added security.

```
resource "mongodbatlas_network_peering" "this" {
  container_id           = mongodbatlas_network_container.this.id
  project_id             = mongodbatlas_project.this.id
  provider_name          = "AWS"
  accepter_region_name   = data.aws_region.current.id
  vpc_id                 = var.vpc_id
  aws_account_id         = data.aws_caller_identity.current.account_id
  route_table_cidr_block = var.vpc_cidr_block
}

resource "aws_vpc_peering_connection_accepter" "this" {
  vpc_peering_connection_id = mongodbatlas_network_peering.this.connection_id
  auto_accept               = true
  tags = {
    Name = var.peering_connection_name
  }
}
```
E.g.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2txbbvde4357lj1yqlx.png)

## Plan and Apply your terraform code

Your tfvars file should look like this,

> environment.tfvars
```
vpc_id = "<VPC-ID>"
vpc_cidr_block = "10.0.0.0/16"
peering_connection_name = "tf-mongo-atlas"
atlas_project_name = "<Cluster-Name>"

atlas_cidr_block = "<Atlas-CIDR>"

database_users = {
  user1 = {
    username           = "Juan",
    password           = "",
    auth_database_name = "admin"
    roles = [
      { database_name = "admin", role_name = "readWriteAnyDatabase" },
    ]
  },
  user2 = {
    username           = "Pedro",
    password           = "",
    auth_database_name = "admin"
    roles = [
      { database_name = "admin", role_name = "readWriteAnyDatabase" },
      { database_name = "admin", role_name = "oplogRead" },
    ]
  },
  user3 = {
    username           = "Pipe",
    password           = "",
    auth_database_name = "admin"
    roles = [
      { database_name = "admin", role_name = "readWriteAnyDatabase" },
    ]
  },
  // Add more users as needed
}

custom_roles = {
  oplogRead = {
    role_name = "oplogRead"
    actions = [
      {
        action = "FIND"
        resources = {
          collection_name = ""
          database_name   = "anyDatabase"
        }
      },
      {
        action = "CHANGE_STREAM"
        resources = {
          collection_name = ""
          database_name   = "anyDatabase"
        }
      },
    ]
  }
  # Add more roles if needed
}
```

Now all you need to do is to plan your terraform code using the terraform plan and verify the changes in the printed plan. You should be seeing one module to be added which will be the atlas cluster with the provided configuration.

After verifying the plan in the above step, run terraform apply to apply your changes. You will see the cluster getting created message on the terminal and your changes will start appearing in your mongoDB console as well. It usually takes about 10 to 15 mins for the cluster to be created.

You will see cluster created like this,

![atlas-cluster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opkamd35vm0v4dawv3v2.png)

## Resources

- You can find the project demo code here,
https://github.com/sagary2j/atlas-mongodb-terraform/tree/initial-code

- MongoDB atlas: https://registry.terraform.io/providers/mongodb/mongodbatlas/latest

- Atlas organizations apiKeys create: https://www.mongodb.com/docs/atlas/cli/stable/command/atlas-organizations-apiKeys-create/#inherited-options