# MongoDB Atlas With Terraform - Cluster and Backup Policies

In this tutorial, I will show you how to create a MongoDB cluster in Atlas using Terraform. We saw in a previous article how to create an API key to start using Terraform and create our first project module. Now, we will go ahead and create our first cluster. If you don't have an API key and a project, I recommend you look at the previous article.

This article is for anyone who intends to use or already uses infrastructure as code (IaC) on the MongoDB Atlas platform or wants to learn more about it.

Everything we do here is contained in the provider/resource documentation: mongodbatlas_advanced_cluster | Resources | mongodb/mongodbatlas | Terraform

> Note: We will not use a backend file. However, for productive implementations, it is extremely important and safer to store the state file in a remote location such as S3, GCS, Azurerm, etc.

 
## Creating a cluster
At this point, we will create our first replica set cluster using Terraform in MongoDB Atlas. As discussed in the previous article, Terraform is a powerful infrastructure-as-code tool that allows you to manage and provision IT resources in an efficient and predictable way. By using it in conjunction with MongoDB Atlas, you can automate the creation and management of database resources in the cloud, ensuring a consistent and reliable infrastructure.

Before we begin, make sure that all the prerequisites mentioned in the previous article are properly configured: Install Terraform, create an API key in MongoDB Atlas, and set up a project in Atlas. These steps are essential to ensure the success of creating your replica set cluster.
### Terraform provider configuration for MongoDB Atlas
The first step is to configure the Terraform provider for MongoDB Atlas. This will allow Terraform to communicate with the MongoDB Atlas API and manage resources within your account. Add the following block of code to your provider.tf file: 

```
provider "mongodbatlas" {}
```

In the previous article, we configured the Terraform provider by directly entering our public and private keys. Now, in order to adopt more professional practices, we have chosen to use environment variables for authentication. The MongoDB Atlas provider, like many others, supports several authentication methodologies. The safest and most recommended option is to use environment variables. This implies only defining the provider in our Terraform code and exporting the relevant environment variables where Terraform will be executed, whether in the terminal, as a secret in Kubernetes, or a secret in GitHub Actions, among other possible contexts. There are other forms of authentication, such as using MongoDB CLI, AWS Secrets Manager, directly through variables in Terraform, or even specifying the keys in the code. However, to ensure security and avoid exposing our keys in accessible locations, we opt for the safer approaches mentioned.

### Creating the Terraform version file
Inside the versions.tf file, you will start by specifying the version of Terraform that your project requires. This is important to ensure that all users and CI/CD environments use the same version of Terraform, avoiding possible incompatibilities or execution errors. In addition to defining the Terraform version, it is equally important to specify the versions of the providers used in your project. This ensures that resources are managed consistently. For example, to set the MongoDB Atlas provider version, you would add a `required_providers` block inside the Terraform block, as shown below:

```terraform
terraform {
  required_version = ">= 0.12"
  required_providers {
    mongodbatlas = {
      source = "mongodb/mongodbatlas"
      version = "1.14.0"
    }
  }
}
```

### Defining the cluster resource
After configuring the version file and establishing the Terraform and provider versions, the next step is to define the cluster resource in MongoDB Atlas. This is done by creating a .tf file, for example main.tf, where you will specify the properties of the desired cluster. As we are going to make a module that will be reusable, we will use variables and default values so that other calls can create clusters with different architectures or sizes, without having to write a new module.

I will look at some attributes and parameters to make this clear.
```terraform
# ------------------------------------------------------------------------------
# MONGODB CLUSTER
# ------------------------------------------------------------------------------
resource "mongodbatlas_advanced_cluster" "default" {
  project_id = data.mongodbatlas_project.default.id
  name = var.name
  cluster_type = var.cluster_type
  backup_enabled = var.backup_enabled
  pit_enabled = var.pit_enabled
  mongo_db_major_version = var.mongo_db_major_version
  disk_size_gb = var.disk_size_gb
  
``` 
In this first block, we are specifying the name of our cluster through the name parameter, its type (which can be a `REPLICASET`, `SHARDED`, or `GEOSHARDED`), and if we have backup and point in time activated, in addition to the database version and the amount of storage for the cluster.

```terraform
  advanced_configuration {
    fail_index_key_too_long = var.fail_index_key_too_long
    javascript_enabled = var.javascript_enabled
    minimum_enabled_tls_protocol = var.minimum_enabled_tls_protocol
    no_table_scan = var.no_table_scan
    oplog_size_mb = var.oplog_size_mb
    default_read_concern = var.default_read_concern
    default_write_concern = var.default_write_concern
    oplog_min_retention_hours = var.oplog_min_retention_hours
    transaction_lifetime_limit_seconds = var.transaction_lifetime_limit_seconds
    sample_size_bi_connector = var.sample_size_bi_connector
    sample_refresh_interval_bi_connector = var.sample_refresh_interval_bi_connector
}
```

Here, we are specifying some advanced settings. Many of these values will not be specified in the .tfvars as they have default values in the variables.tf file.

Parameters include the type of read/write concern, oplog size in MB, TLS protocol, whether JavaScript will be enabled in MongoDB, and transaction lifetime limit in seconds. no_table_scan is for when the cluster disables the execution of any query that requires a collection scan to return results, when true. There are more parameters that you can look at in the documentation, if you have questions.

```terraform
  replication_specs {
    num_shards = var.cluster_type == "REPLICASET" ? null : var.num_shards
    
    dynamic "region_configs" {
      for_each = var.region_configs

      content {
        provider_name = region_configs.value.provider_name
        priority = region_configs.value.priority
        region_name = region_configs.value.region_name

        electable_specs {
          instance_size = region_configs.value.electable_specs.instance_size
          node_count = region_configs.value.electable_specs.node_count
          disk_iops = region_configs.value.electable_specs.instance_size == "M10" || region_configs.value.electable_specs.instance_size == "M20" ? null :            region_configs.value.electable_specs.disk_iops
          ebs_volume_type = region_configs.value.electable_specs.ebs_volume_type
}

        auto_scaling {
          disk_gb_enabled = region_configs.value.auto_scaling.disk_gb_enabled
          compute_enabled = region_configs.value.auto_scaling.compute_enabled
          compute_scale_down_enabled = region_configs.value.auto_scaling.compute_scale_down_enabled
          compute_min_instance_size = region_configs.value.auto_scaling.compute_min_instance_size
          compute_max_instance_size = region_configs.value.auto_scaling.compute_max_instance_size
}

        analytics_specs {
          instance_size = try(region_configs.value.analytics_specs.instance_size, "M10")
          node_count = try(region_configs.value.analytics_specs.node_count, 0)
          disk_iops = try(region_configs.value.analytics_specs.disk_iops, null)
         ebs_volume_type = try(region_configs.value.analytics_specs.ebs_volume_type, "STANDARD")
}

       analytics_auto_scaling {
         disk_gb_enabled = try(region_configs.value.analytics_auto_scaling.disk_gb_enabled, null)
         compute_enabled = try(region_configs.value.analytics_auto_scaling.compute_enabled, null)
         compute_scale_down_enabled = try(region_configs.value.analytics_auto_scaling.compute_scale_down_enabled, null)
         compute_min_instance_size = try(region_configs.value.analytics_auto_scaling.compute_min_instance_size, null)
         compute_max_instance_size = try(region_configs.value.analytics_auto_scaling.compute_max_instance_size, null)
}

       read_only_specs {
         instance_size = try(region_configs.value.read_only_specs.instance_size, "M10")
         node_count = try(region_configs.value.read_only_specs.node_count, 0)
         disk_iops = try(region_configs.value.read_only_specs.disk_iops, null)
         ebs_volume_type = try(region_configs.value.read_only_specs.ebs_volume_type, "STANDARD")
}
}
}
}

```

At this moment, we are placing the number of shards we want, in case our cluster is not a REPLICASET. In addition, we specify the configuration of the cluster, region, cloud, priority for failover, autoscaling, electable, analytics, and read-only node configurations, in addition to its autoscaling configurations.

```terraform
  dynamic "tags" {
    for_each = local.tags
    content {
     key = tags.key
     value = tags.value
}
}

  bi_connector_config {
    enabled = var.bi_connector_enabled
    read_preference = var.bi_connector_read_preference
}

  lifecycle {
    ignore_changes = 
    disk_size_gb,
    ]
}
}
```

Next, we create a dynamic block to loop for each tag variable we include. In addition, we specify the BI connector, if desired, and the lifecycle block. Here, we are only specifying `disk_size_gb` for an example, but it is recommended to read the documentation that has important warnings about this block, such as including `instance_size`, as autoscaling can change and you don't want to accidentally retire an instance during peak times.

```
# ------------------------------------------------------------------------------
# MONGODB BACKUP SCHEDULE
# ------------------------------------------------------------------------------
resource "mongodbatlas_cloud_backup_schedule" "default" {
project_id = data.mongodbatlas_project.default.id
cluster_name = mongodbatlas_advanced_cluster.default.name
update_snapshots = var.update_snapshots
reference_hour_of_day = var.reference_hour_of_day
reference_minute_of_hour = var.reference_minute_of_hour
restore_window_days = var.restore_window_days

policy_item_hourly {
frequency_interval = var.policy_item_hourly_frequency_interval
retention_unit = var.policy_item_hourly_retention_unit
retention_value = var.policy_item_hourly_retention_value
}

policy_item_daily {
frequency_interval = var.policy_item_daily_frequency_interval
retention_unit = var.policy_item_daily_retention_unit
retention_value = var.policy_item_daily_retention_value
}

policy_item_weekly {
frequency_interval = var.policy_item_weekly_frequency_interval
retention_unit = var.policy_item_weekly_retention_unit
retention_value = var.policy_item_weekly_retention_value
}

policy_item_monthly {
frequency_interval = var.policy_item_monthly_frequency_interval
retention_unit = var.policy_item_monthly_retention_unit
retention_value = var.policy_item_monthly_retention_value
}
}
```
  
Finally, we create the backup block, which contains the policies and settings regarding the backup of our cluster.

This module, while detailed, encapsulates the full functionality offered by the `mongodbatlas_advanced_cluster` and `mongodbatlas_cloud_backup_schedule` resources, providing a comprehensive approach to creating and managing clusters in MongoDB Atlas. It supports the configuration of replica set, sharded, and geosharded clusters, meeting a variety of scalability and geographic distribution needs.

One of the strengths of this module is its flexibility in configuring backup policies, allowing fine adjustments that precisely align with the requirements of each database. This is essential to ensure resilience and effective data recovery in any scenario. Additionally, the module comes with vertical scaling enabled by default, in addition to offering advanced storage auto-scaling capabilities, ensuring that the cluster dynamically adjusts to the data volume and workload.

To complement the robustness of the configuration, the module allows the inclusion of analytical nodes and read-only nodes, expanding the possibilities of using the cluster for scenarios that require in-depth analysis or intensive read operations without impacting overall performance.

The default configuration includes smart preset values, such as the MongoDB version, which is set to "7.0" to take advantage of the latest features while maintaining the option to adjust to specific versions as needed. This “best practices” approach ensures a solid starting point for most projects, reducing the need for manual adjustments and simplifying the deployment process.

Additionally, the ability to deploy clusters in any region and cloud provider — such as AWS, Azure, or GCP — offers unmatched flexibility, allowing teams to choose the best solution based on their cost, performance, and compliance preferences.

In summary, this module not only facilitates the configuration and management of MongoDB Atlas clusters with an extensive range of options and adjustments but also promotes secure and efficient configuration practices, making it a valuable tool for developers and database administrators in implementing scalable and reliable data solutions in the cloud.

The use of the lifecycle directive with the `ignore_changes` option in the Terraform code was specifically implemented to accommodate manual upscale situations of the MongoDB Atlas cluster, which should not be automatically reversed by Terraform in subsequent executions. This approach ensures that, after a manual increase in storage capacity (`disk_size_gb`) or other specific replication configurations (`replication_specs`), Terraform does not attempt to undo these changes to align the resource state with the original definition in the code. Essentially, it allows configuration adjustments made outside of Terraform, such as an upscale to optimize performance or meet growing demands, to remain intact without being overwritten by future Terraform executions, ensuring operational flexibility while maintaining infrastructure management as code.

In the variable.tf file, we create variables with default values:

```terraform
variable "name" {
description = "The name of the cluster."
type = string
}

variable "cluster_type" {
description = < Note: Remember to export the environment variables with the public and private keys.

```
export MONGODB_ATLAS_PUBLIC_KEY="public"                    
export MONGODB_ATLAS_PRIVATE_KEY="private"
```

Now, we run `terraform init`.

```
(base) samuelmolling@Samuels-MacBook-Pro cluster % terraform init

Initializing the backend...

Initializing provider plugins...
- Finding mongodb/mongodbatlas versions matching "1.14.0"...
- Installing mongodb/mongodbatlas v1.14.0...
- Installed mongodb/mongodbatlas v1.14.0 (signed by a HashiCorp partner, key ID 2A32ED1F3AD25ABF)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run `terraform init` in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running `terraform plan` to see any changes that are required for your infrastructure. All Terraform commands should now work.

If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.

```

Now that init has worked, let's run `terraform plan` and evaluate what will happen:

```
(base) samuelmolling@Samuels-MacBook-Pro cluster % terraform plan
data.mongodbatlas_project.default: Reading...
data.mongodbatlas_project.default: Read complete after 2s [id=65bfd71a08b61c36ca4d8eaa]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # mongodbatlas_advanced_cluster.default will be created
  + resource "mongodbatlas_advanced_cluster" "default" {
      + advanced_configuration         = [
          + {
              + default_read_concern                 = "local"
              + default_write_concern                = "majority"
              + fail_index_key_too_long              = false
              + javascript_enabled                   = true
              + minimum_enabled_tls_protocol         = "TLS1_2"
              + no_table_scan                        = false
              + oplog_size_mb                        = (known after apply)
              + sample_refresh_interval_bi_connector = 300
              + sample_size_bi_connector             = 100
              + transaction_lifetime_limit_seconds   = 60
            },
        ]
      + backup_enabled                 = true
      + cluster_id                     = (known after apply)
      + cluster_type                   = "REPLICASET"
      + connection_strings             = (known after apply)
      + create_date                    = (known after apply)
      + disk_size_gb                   = 10
      + encryption_at_rest_provider    = (known after apply)
      + id                             = (known after apply)
      + mongo_db_major_version         = "7.0"
      + mongo_db_version               = (known after apply)
      + name                           = "cluster-demo"
      + paused                         = (known after apply)
      + pit_enabled                    = true
      + project_id                     = "65bfd71a08b61c36ca4d8eaa"
      + root_cert_type                 = (known after apply)
      + state_name                     = (known after apply)
      + termination_protection_enabled = (known after apply)
      + version_release_system         = (known after apply)

      + bi_connector_config {
          + enabled         = false
          + read_preference = "secondary"
        }

      + replication_specs {
          + container_id = (known after apply)
          + id           = (known after apply)
          + num_shards   = 1
          + zone_name    = "ZoneName managed by Terraform"

          + region_configs {
              + priority      = 7
              + provider_name = "AWS"
              + region_name   = "US_EAST_1"

              + analytics_auto_scaling {
                  + compute_enabled            = (known after apply)
                  + compute_max_instance_size  = (known after apply)
                  + compute_min_instance_size  = (known after apply)
                  + compute_scale_down_enabled = (known after apply)
                  + disk_gb_enabled            = (known after apply)
                }

              + analytics_specs {
                  + disk_iops       = (known after apply)
                  + ebs_volume_type = "STANDARD"
                  + instance_size   = "M10"
                  + node_count      = 0
                }

              + auto_scaling {
                  + compute_enabled            = true
                  + compute_max_instance_size  = "M30"
                  + compute_min_instance_size  = "M10"
                  + compute_scale_down_enabled = true
                  + disk_gb_enabled            = true
                }

              + electable_specs {
                  + disk_iops       = (known after apply)
                  + ebs_volume_type = "STANDARD"
                  + instance_size   = "M10"
                  + node_count      = 3
                }

              + read_only_specs {
                  + disk_iops       = (known after apply)
                  + ebs_volume_type = "STANDARD"
                  + instance_size   = "M10"
                  + node_count      = 0
                }
            }
        }

      + tags {
          + key   = "environment"
          + value = "dev"
        }
      + tags {
          + key   = "name"
          + value = "teste-cluster"
        }
    }
    
  # mongodbatlas_cloud_backup_schedule.default will be created
  + resource "mongodbatlas_cloud_backup_schedule" "default" {
      + auto_export_enabled                      = (known after apply)
      + cluster_id                               = (known after apply)
      + cluster_name                             = "cluster-demo"
      + id                                       = (known after apply)
      + id_policy                                = (known after apply)
      + next_snapshot                            = (known after apply)
      + project_id                               = "65bfd71a08b61c36ca4d8eaa"
      + reference_hour_of_day                    = 3
      + reference_minute_of_hour                 = 30
      + restore_window_days                      = 3
      + update_snapshots                         = false
      + use_org_and_group_names_in_export_prefix = (known after apply)

      + policy_item_daily {
          + frequency_interval = 1
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "days"
          + retention_value    = 7
        }

      + policy_item_hourly {
          + frequency_interval = 12
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "days"
          + retention_value    = 3
        }

      + policy_item_monthly {
          + frequency_interval = 1
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "months"
          + retention_value    = 12
        }

      + policy_item_weekly {
          + frequency_interval = 1
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "weeks"
          + retention_value    = 4
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run `terraform apply` now.

```

Show! It was exactly the output we expected to see, the creation of a cluster resource with the backup policies. Let's apply this!

When running the `terraform apply` command, you will be prompted for approval with `yes` or `no`. Type `yes`.

```
(base) samuelmolling@Samuels-MacBook-Pro cluster % terraform apply 

data.mongodbatlas_project.default: Reading...

data.mongodbatlas_project.default: Read complete after 2s [id=65bfd71a08b61c36ca4d8eaa]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  
Terraform will perform the following actions:

  # mongodbatlas_advanced_cluster.default will be created
  + resource "mongodbatlas_advanced_cluster" "default" {
      + advanced_configuration         = [
          + {
              + default_read_concern                 = "local"
              + default_write_concern                = "majority"
              + fail_index_key_too_long              = false
              + javascript_enabled                   = true
              + minimum_enabled_tls_protocol         = "TLS1_2"
              + no_table_scan                        = false
              + oplog_size_mb                        = (known after apply)
              + sample_refresh_interval_bi_connector = 300
              + sample_size_bi_connector             = 100
              + transaction_lifetime_limit_seconds   = 60
            },
        ]
      + backup_enabled                 = true
      + cluster_id                     = (known after apply)
      + cluster_type                   = "REPLICASET"
      + connection_strings             = (known after apply)
      + create_date                    = (known after apply)
      + disk_size_gb                   = 10
      + encryption_at_rest_provider    = (known after apply)
      + id                             = (known after apply)
      + mongo_db_major_version         = "7.0"
      + mongo_db_version               = (known after apply)
      + name                           = "cluster-demo"
      + paused                         = (known after apply)
      + pit_enabled                    = true
      + project_id                     = "65bfd71a08b61c36ca4d8eaa"
      + root_cert_type                 = (known after apply)
      + state_name                     = (known after apply)
      + termination_protection_enabled = (known after apply)
      + version_release_system         = (known after apply)
      + bi_connector_config {
          + enabled         = false
          + read_preference = "secondary"
        }

      + replication_specs {
          + container_id = (known after apply)
          + id           = (known after apply)
          + num_shards   = 1
          + zone_name    = "ZoneName managed by Terraform"

          + region_configs {
              + priority      = 7
              + provider_name = "AWS"
              + region_name   = "US_EAST_1"

              + analytics_auto_scaling {
                  + compute_enabled            = (known after apply)
                  + compute_max_instance_size  = (known after apply)
                  + compute_min_instance_size  = (known after apply)
                  + compute_scale_down_enabled = (known after apply)
                  + disk_gb_enabled            = (known after apply)
                }
                
              + analytics_specs {
                  + disk_iops       = (known after apply)
                  + ebs_volume_type = "STANDARD"
                  + instance_size   = "M10"
                  + node_count      = 0
                }

              + auto_scaling {
                  + compute_enabled            = true
                  + compute_max_instance_size  = "M30"
                  + compute_min_instance_size  = "M10"
                  + compute_scale_down_enabled = true
                  + disk_gb_enabled            = true
                }
                
              + electable_specs {
                  + disk_iops       = (known after apply)
                  + ebs_volume_type = "STANDARD"
                  + instance_size   = "M10"
                  + node_count      = 3
                }

              + read_only_specs {
                  + disk_iops       = (known after apply)
                  + ebs_volume_type = "STANDARD"
                  + instance_size   = "M10"
                  + node_count      = 0
                }
            }
        }

      + tags {
          + key   = "environment"
          + value = "dev"
        }
      + tags {
          + key   = "name"
          + value = "teste-cluster"
        }
    }

  # mongodbatlas_cloud_backup_schedule.default will be created
  + resource "mongodbatlas_cloud_backup_schedule" "default" {
      + auto_export_enabled                      = (known after apply)
      + cluster_id                               = (known after apply)
      + cluster_name                             = "cluster-demo"
      + id                                       = (known after apply)
      + id_policy                                = (known after apply)
      + next_snapshot                            = (known after apply)
      + project_id                               = "65bfd71a08b61c36ca4d8eaa"
      + reference_hour_of_day                    = 3
      + reference_minute_of_hour                 = 30
      + restore_window_days                      = 3
      + update_snapshots                         = false
      + use_org_and_group_names_in_export_prefix = (known after apply)
      + policy_item_daily {
          + frequency_interval = 1
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "days"
          + retention_value    = 7
        }

      + policy_item_hourly {
          + frequency_interval = 12
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "days"
          + retention_value    = 3
        }

      + policy_item_monthly {
          + frequency_interval = 1
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "months"
          + retention_value    = 12
        }

      + policy_item_weekly {
          + frequency_interval = 1
          + frequency_type     = (known after apply)
          + id                 = (known after apply)
          + retention_unit     = "weeks"
          + retention_value    = 4
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes 

mongodbatlas_advanced_cluster.default: Creating...
mongodbatlas_advanced_cluster.default: Still creating... [10s elapsed]
mongodbatlas_advanced_cluster.default: Still creating... [8m40s elapsed]
mongodbatlas_advanced_cluster.default: Creation complete after 8m46s [id=Y2x1c3Rlcl9pZA==:NjViZmRmYzczMTBiN2Y2ZDFhYmIxMmQ0-Y2x1c3Rlcl9uYW1l:Y2x1c3Rlci1kZW1v-cHJvamVjdF9pZA==:NjViZmQ3MWEwOGI2MWMzNmNhNGQ4ZWFh]
mongodbatlas_cloud_backup_schedule.default: Creating...
mongodbatlas_cloud_backup_schedule.default: Creation complete after 2s [id=Y2x1c3Rlcl9uYW1l:Y2x1c3Rlci1kZW1v-cHJvamVjdF9pZA==:NjViZmQ3MWEwOGI2MWMzNmNhNGQ4ZWFh]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
```

This process took eight minutes and 40 seconds to execute. I shortened the log output, but don't worry if this step takes time.

Now, let’s look in Atlas to see if the cluster was created successfully…

![Atlas Cluster overview][1]
![Atlas cluster Backup information screen][2]

We were able to create our first replica set with a standard backup policy with PITR and scheduled snapshots.

In this tutorial, we saw how to create the first cluster in our project created in the last article. We created a module that also includes a backup policy. In an upcoming article, we will look at how to create an API key and user using Terraform and Atlas.

To learn more about MongoDB and various tools, I invite you to visit the [Developer Center to read the other articles.

  [1]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltef08af8a99b7af22/65e0d4dbeef4e3792e1e6ddf/image1.png
  [2]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blte24ff6c1fea2a907/65e0d4db31aca16b3e7efa80/image2.png