DEV Community

Sagar R Ravkhande
Sagar R Ravkhande

Posted on

Create MongoDB Atlas Cluster With Terraform and AWS

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

  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

  1. 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

  1. 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

  2. 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"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
provider "mongodbatlas" {
  public_key = "<YOUR PUBLIC KEY HERE>"
  private_key  = "<YOUR PRIVATE KEY HERE>"
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}

Enter fullscreen mode Exit fullscreen mode

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

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
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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]
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

E.g.
Image description

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
}
Enter fullscreen mode Exit fullscreen mode

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

Resources

Top comments (0)