diff --git a/README.md b/README.md index 4e6fef0..dfde07a 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,6 @@ This repository contains the Atlas CLI plugin for [Terraform's MongoDB Atlas Provider](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs). -It has the following commands to help with your Terraform configurations: -- **clusterToAdvancedCluster**: Convert a `mongodbatlas_cluster` Terraform configuration to `mongodbatlas_advanced_cluster` (preview provider 2.0.0). - ## Installation Install the [Atlas CLI](https://github.com/mongodb/mongodb-atlas-cli) if you haven't done it yet. @@ -26,101 +23,33 @@ If you want to see the list of installed plugins or check if this plugin is inst atlas plugin list ``` -## Convert mongodbatlas_cluster to mongodbatlas_advanced_cluster (preview provider 2.0.0) - -### Usage +## Available Commands -You can find more information in the [Migration Guide: Cluster to Advanced Cluster](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/cluster-to-advanced-cluster-migration-guide). +The plugin provides the following commands to help with your Terraform configurations: -**Note**: In order to use the **Preview for MongoDB Atlas Provider 2.0.0** of `mongodbatlas_advanced_cluster`, you need to set the environment variable `MONGODB_ATLAS_PREVIEW_PROVIDER_V2_ADVANCED_CLUSTER` to `true`. +### 1. clusterToAdvancedCluster (clu2adv) +Convert `mongodbatlas_cluster` resources to `mongodbatlas_advanced_cluster` Provider 2.0.0 schema. -If you want to convert a Terraform configuration from `mongodbatlas_cluster` to `mongodbatlas_advanced_cluster`, use the following command: +**Quick Start:** ```bash atlas terraform clusterToAdvancedCluster --file in.tf --output out.tf -``` - -you can also use shorter aliases, e.g.: -```bash +# or using alias atlas tf clu2adv -f in.tf -o out.tf ``` -If you want to include the `moved blocks` in the output file, use the `--includeMoved` or the `-m` flag. - -If you want to overwrite the output file if it exists, or even use the same output file as the input file, use the `--replaceOutput` or the `-r` flag. - -You can use the `--watch` or the `-w` flag to keep the plugin running and watching for changes in the input file. You can have input and output files open in an editor and see easily how changes to the input file affect the output file. - -You can find [here](https://github.com/mongodb-labs/atlas-cli-plugin-terraform/tree/main/internal/convert/testdata/clu2adv) some examples of input files (suffix .in.tf) and the corresponding output files (suffix .out.tf). +[📖 Full Documentation](./docs/command_clu2adv.md) | [🔄 Migration Guide: Cluster to Advanced Cluster](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/cluster-to-advanced-cluster-migration-guide) -### Dynamic blocks +### 2. advancedClusterToV2 (adv2v2) +Convert previous `mongodbatlas_advanced_cluster` configurations to the new Provider 2.0.0 schema with simplified structure. -`dynamic` blocks are used to generate multiple nested blocks based on a set of values. -Given the different ways of using dynamic blocks, we recommend reviewing the output and making sure it fits your needs. - -#### Dynamic blocks in tags and labels - -You can use `dynamic` blocks for `tags` and `labels`. The plugin assumes that `for_each` has an expression which is evaluated to a `map` of strings. -You can also combine the use of dynamic blocks in `tags` and `labels` with individual blocks in the same cluster definition, e.g.: -```hcl -tags { - key = "environment" - value = var.environment -} -dynamic "tags" { - for_each = var.tags - content { - key = tags.key - value = replace(tags.value, "/", "_") - } -} -``` - -#### Dynamic blocks in regions_config - -You can use `dynamic` blocks for `regions_config`. The plugin assumes that `for_each` has an expression which is evaluated to a `list` or `set` of objects. See this [guide](./docs/guide_clu2adv_dynamic_block.md) to learn more about some limitations. -This is an example of how to use dynamic blocks in `regions_config`: -```hcl -replication_specs { - num_shards = var.replication_specs.num_shards - zone_name = var.replication_specs.zone_name # only needed if you're using zones - dynamic "regions_config" { - for_each = var.replication_specs.regions_config - content { - priority = regions_config.value.priority - region_name = regions_config.value.region_name - electable_nodes = regions_config.value.electable_nodes - read_only_nodes = regions_config.value.read_only_nodes - } - } -} -``` - -#### Dynamic blocks in replication_specs - -You can use `dynamic` blocks for `replication_specs`. The plugin assumes that `for_each` has an expression which is evaluated to a `list` of objects. See this [guide](./docs/guide_clu2adv_dynamic_block.md) to learn more about some limitations. -This is an example of how to use dynamic blocks in `replication_specs`: -```hcl -dynamic "replication_specs" { - for_each = var.replication_specs - content { - num_shards = replication_specs.value.num_shards - zone_name = replication_specs.value.zone_name # only needed if you're using zones - dynamic "regions_config" { - for_each = replication_specs.value.regions_config - content { - electable_nodes = regions_config.value.electable_nodes - priority = regions_config.value.priority - read_only_nodes = regions_config.value.read_only_nodes - region_name = regions_config.value.region_name - } - } - } -} +**Quick Start:** +```bash +atlas terraform advancedClusterToV2 --file in.tf --output out.tf +# or using alias +atlas tf adv2v2 -f in.tf -o out.tf ``` -### Limitations - -- `dynamic` blocks are supported with some [limitations](./docs/guide_clu2adv_dynamic_block.md). +[📖 Full Documentation](./docs/command_adv2v2.md) ## Feedback diff --git a/docs/command_adv2v2.md b/docs/command_adv2v2.md new file mode 100644 index 0000000..d24f919 --- /dev/null +++ b/docs/command_adv2v2.md @@ -0,0 +1,221 @@ +# Convert mongodbatlas_advanced_cluster to Provider 2.0.0 schema + +The advancedClusterToV2 (adv2v2) command helps you migrate previous `mongodbatlas_advanced_cluster` configurations to the new Provider 2.0.0 schema. + +This revised file migrates the Terraform configurations and state to the latest version and doesn't modify the resources deployed in MongoDB Atlas. + +MongoDB Atlas Provider 2.0.0 introduces a new, cleaner structure for `mongodbatlas_advanced_cluster` resources. The main changes include the use of nested attributes instead of blocks and deletion of deprecated attributes like `disk_size_gb` at root level or `num_shards`. + +## Usage + +To convert a Terraform configuration from the previous `mongodbatlas_advanced_cluster` schema to the Provider 2.0.0 schema, use the following command: + +```bash +atlas terraform advancedClusterToV2 --file in.tf --output out.tf +``` + +You can also use shorter aliases: +```bash +atlas tf adv2v2 -f in.tf -o out.tf +``` + +### Command Options + +- `--file` or `-f`: Input file path containing the `mongodbatlas_advanced_cluster` configuration +- `--output` or `-o`: Output file path for the converted Provider 2.0.0 configuration +- `--replaceOutput` or `-r`: Overwrite the file at the output path if it already exists. You can also modify the input file in-place. +- `--watch` or `-w`: Keep the plugin running and watching for changes in the input file + +## Examples + +You can find [here](https://github.com/mongodb-labs/atlas-cli-plugin-terraform/tree/main/internal/convert/testdata/adv2v2) examples of input files (suffix .in.tf) and the corresponding output files (suffix .out.tf). + +## Dynamic Blocks + +`dynamic` blocks are used to generate multiple nested blocks based on a set of values. +We recommend reviewing the output and making sure it fits your needs. + +### Dynamic blocks in tags and labels + +You can use `dynamic` blocks for `tags` and `labels`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `map` of strings. +You can also combine the use of dynamic blocks in `tags` and `labels` with individual blocks in the same cluster definition, for example: +```hcl +tags { + key = "environment" + value = var.environment +} +dynamic "tags" { + for_each = var.tags + content { + key = tags.key + value = replace(tags.value, "/", "_") + } +} +``` + +### Dynamic blocks in region_configs + +You can use `dynamic` blocks for `region_configs`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects. + +This is an example of how to use dynamic blocks in `region_configs`: +```hcl +replication_specs { + num_shards = var.replication_specs.num_shards + zone_name = var.replication_specs.zone_name # only needed if you're using zones + dynamic "region_configs" { + for_each = var.replication_specs.region_configs + content { + priority = region_configs.value.priority + provider_name = region_configs.value.provider_name + region_name = region_configs.value.region_name + electable_specs { + instance_size = region_configs.value.instance_size + node_count = region_configs.value.electable_node_count + } + # read_only_specs, analytics_specs, auto_scaling and analytics_auto_scaling can also be defined + } + } +} +``` + +### Dynamic blocks in replication_specs + +You can use `dynamic` blocks for `replication_specs`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects. + +This is an example of how to use dynamic blocks in `replication_specs`: +```hcl +dynamic "replication_specs" { + for_each = var.replication_specs + content { + num_shards = replication_specs.value.num_shards + zone_name = replication_specs.value.zone_name # only needed if you're using zones + dynamic "region_configs" { + for_each = replication_specs.value.region_configs + priority = region_configs.value.priority + provider_name = region_configs.value.provider_name + region_name = region_configs.value.region_name + electable_specs { + instance_size = region_configs.value.instance_size + node_count = region_configs.value.electable_node_count + } + # read_only_specs, analytics_specs, auto_scaling and analytics_auto_scaling can also be defined + } + } +} +``` + +### Limitations + +If you need to use the plugin for `dynamic` block use cases not yet supported, please send us [feedback](https://github.com/mongodb-labs/atlas-cli-plugin-terraform/issues). + +#### Combination of blocks with dynamic and inline expressions + +Dynamic blocks and individual blocks for `region_configs` or `replication_specs` are not supported at the same time. To continue using this plugin, remove `region_configs` or `replication_specs` blocks and use a local `list` variable with [concat](https://developer.hashicorp.com/terraform/language/functions/concat) to add the individual block information to the variable you're using in the `for_each` expression. + +#### Example + +Let's see an example with `region_configs`, the same idea applies for `replication_specs`. In the original configuration file, the `mongodb_cluster` resource is used inside a module that receives the `region_configs` elements in a `list` variable and we want to add an additional `region_configs` with a read-only node. +```hcl +variable "replication_specs" { + type = object({ + num_shards = number + region_configs = list(object({ + priority = number + provider_name = string + region_name = string + instance_size = string + electable_node_count = number + read_only_node_count = number + })) + }) +} + +resource "mongodbatlas_advanced_cluster" "this" { + project_id = var.project_id + name = var.cluster_name + cluster_type = var.cluster_type + replication_specs { + num_shards = var.replication_specs.num_shards + dynamic "region_configs" { + for_each = var.replication_specs.region_configs + priority = region_configs.value.priority + provider_name = region_configs.value.provider_name + region_name = region_configs.value.region_name + electable_specs { + instance_size = region_configs.value.instance_size + node_count = region_configs.value.electable_node_count + } + read_only_specs { + instance_size = region_configs.value.instance_size + node_count = region_configs.value.read_only_node_count + } + } + region_configs { # individual region + priority = 0 + provider_name = "AWS" + region_name = "US_EAST_1" + read_only_specs { + instance_size = var.instance_size + node_count = 1 + } + } + } +} +``` + +We modify the configuration file to create an intermediate `local` variable to merge the `region_configs` variable elements and the additional `region_configs`: +```hcl +variable "replication_specs" { + type = object({ + num_shards = number + region_configs = list(object({ + priority = number + provider_name = string + region_name = string + instance_size = string + electable_node_count = number + read_only_node_count = number + })) + }) +} + +locals { + region_configs_all = concat( + var.replication_specs.region_configs, + [ + { + priority = 0 + provider_name = "AWS" + region_name = "US_EAST_1" + instance_size = var.instance_size + electable_node_count = 0 + read_only_node_count = 1 + }, + ] + ) +} + +resource "mongodbatlas_advanced_cluster" "this" { + project_id = var.project_id + name = var.cluster_name + cluster_type = var.cluster_type + replication_specs { + num_shards = var.replication_specs.num_shards + dynamic "region_configs" { + for_each = local.region_configs_all # changed to use the local variable + priority = region_configs.value.priority + provider_name = region_configs.value.provider_name + region_name = region_configs.value.region_name + electable_specs { + instance_size = region_configs.value.instance_size + node_count = region_configs.value.electable_node_count + } + read_only_specs { + instance_size = region_configs.value.instance_size + node_count = region_configs.value.read_only_node_count + } + } + } +} +``` +This modified configuration file has the same behavior as the original one, but it doesn't have individual blocks anymore, only the `dynamic` block, so it's supported by the plugin. diff --git a/docs/command_clu2adv.md b/docs/command_clu2adv.md new file mode 100644 index 0000000..88986b5 --- /dev/null +++ b/docs/command_clu2adv.md @@ -0,0 +1,198 @@ +# Convert mongodbatlas_cluster to mongodbatlas_advanced_cluster + +The clusterToAdvancedCluster (clu2adv) command helps you migrate from `mongodbatlas_cluster` to `mongodbatlas_advanced_cluster` Provider 2.0.0 schema. + +This revised file migrates the Terraform configurations and state to the latest version and doesn't modify the resources deployed in MongoDB Atlas. + +You can find more information in the [Migration Guide: Cluster to Advanced Cluster](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/cluster-to-advanced-cluster-migration-guide). + +## Usage + +If you want to convert a Terraform configuration from `mongodbatlas_cluster` to `mongodbatlas_advanced_cluster`, use the following command: +```bash +atlas terraform clusterToAdvancedCluster --file in.tf --output out.tf +``` + +You can also use shorter aliases, for example: +```bash +atlas tf clu2adv -f in.tf -o out.tf +``` + +### Command Options + +- `--file` or `-f`: Input file path containing the `mongodbatlas_cluster` configuration +- `--output` or `-o`: Output file path for the converted `mongodbatlas_advanced_cluster` configuration +- `--replaceOutput` or `-r`: Overwrite the file at the output path if it already exists. You can also modify the input file in-place. +- `--watch` or `-w`: Keep the plugin running and watching for changes in the input file +- `--includeMoved` or `-m`: Include the `moved blocks` in the output file + +## Examples + +You can find [here](https://github.com/mongodb-labs/atlas-cli-plugin-terraform/tree/main/internal/convert/testdata/clu2adv) some examples of input files (suffix .in.tf) and the corresponding output files (suffix .out.tf). + +## Dynamic Blocks + +`dynamic` blocks are used to generate multiple nested blocks based on a set of values. +We recommend reviewing the output and making sure it fits your needs. + +### Dynamic blocks in tags and labels + +You can use `dynamic` blocks for `tags` and `labels`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `map` of strings. +You can also combine the use of dynamic blocks in `tags` and `labels` with individual blocks in the same cluster definition, for exaple: +```hcl +tags { + key = "environment" + value = var.environment +} +dynamic "tags" { + for_each = var.tags + content { + key = tags.key + value = replace(tags.value, "/", "_") + } +} +``` + +### Dynamic blocks in regions_config + +You can use `dynamic` blocks for `regions_config`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects. + +This is an example of how to use dynamic blocks in `regions_config`: +```hcl +replication_specs { + num_shards = var.replication_specs.num_shards + zone_name = var.replication_specs.zone_name # only needed if you're using zones + dynamic "regions_config" { + for_each = var.replication_specs.regions_config + content { + priority = regions_config.value.priority + region_name = regions_config.value.region_name + electable_nodes = regions_config.value.electable_nodes + read_only_nodes = regions_config.value.read_only_nodes + } + } +} +``` + +### Dynamic blocks in replication_specs + +You can use `dynamic` blocks for `replication_specs`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects. + +This is an example of how to use dynamic blocks in `replication_specs`: +```hcl +dynamic "replication_specs" { + for_each = var.replication_specs + content { + num_shards = replication_specs.value.num_shards + zone_name = replication_specs.value.zone_name # only needed if you're using zones + dynamic "regions_config" { + for_each = replication_specs.value.regions_config + content { + electable_nodes = regions_config.value.electable_nodes + priority = regions_config.value.priority + read_only_nodes = regions_config.value.read_only_nodes + region_name = regions_config.value.region_name + } + } + } +} +``` + +### Limitations + +If you need to use the plugin for `dynamic` block use cases not yet supported, please send us [feedback](https://github.com/mongodb-labs/atlas-cli-plugin-terraform/issues). + +#### Combination of blocks with dynamic and inline expressions + +Dynamic blocks and individual blocks for `regions_config` or `replication_specs` are not supported at the same time. To continue using this plugin, remove `regions_config` or `replication_specs` blocks and use a local `list` variable with [concat](https://developer.hashicorp.com/terraform/language/functions/concat) to add the individual block information to the variable you're using in the `for_each` expression. + +#### Example + +Let's see an example with `regions_config`, the same idea applies for `replication_specs`. In the original configuration file, the `mongodb_cluster` resource is used inside a module that receives the `regions_config` elements in a `list` variable and we want to add an additional `regions_config` with a read-only node. +```hcl +variable "replication_specs" { + type = object({ + num_shards = number + regions_config = list(object({ + region_name = string + electable_nodes = number + priority = number + read_only_nodes = number + })) + }) +} + +resource "mongodbatlas_cluster" "this" { + project_id = var.project_id + name = var.cluster_name + cluster_type = var.cluster_type + provider_name = var.provider_name + provider_instance_size_name = var.provider_instance_size_name + replication_specs { + num_shards = var.replication_specs.num_shards + dynamic "regions_config" { + for_each = var.replication_specs.regions_config + content { + region_name = regions_config.value.region_name + electable_nodes = regions_config.value.electable_nodes + priority = regions_config.value.priority + read_only_nodes = regions_config.value.read_only_nodes + } + } + regions_config { # individual region + region_name = "US_EAST_1" + read_only_nodes = 1 + } + } +} +``` + +We modify the configuration file to create an intermediate `local` variable to merge the `regions_config` variable elements and the additional `regions_config`: +```hcl +variable "replication_specs" { + type = object({ + num_shards = number + regions_config = list(object({ + region_name = string + electable_nodes = number + priority = number + read_only_nodes = number + })) + }) +} + +locals { + regions_config_all = concat( + var.replication_specs.regions_config, + [ + { + region_name = "US_EAST_1" + electable_nodes = 0 + priority = 0 + read_only_nodes = 1 + }, + ] + ) +} + +resource "mongodbatlas_cluster" "this" { + project_id = var.project_id + name = var.cluster_name + cluster_type = var.cluster_type + provider_name = var.provider_name + provider_instance_size_name = var.provider_instance_size_name + replication_specs { + num_shards = var.replication_specs.num_shards + dynamic "regions_config" { + for_each = local.regions_config_all # changed to use the local variable + content { + region_name = regions_config.value.region_name + electable_nodes = regions_config.value.electable_nodes + priority = regions_config.value.priority + read_only_nodes = regions_config.value.read_only_nodes + } + } + } +} +``` +This modified configuration file has the same behavior as the original one, but it doesn't have individual blocks anymore, only the `dynamic` block, so it's supported by the plugin. diff --git a/docs/guide_clu2adv_dynamic_block.md b/docs/guide_clu2adv_dynamic_block.md deleted file mode 100644 index 667039c..0000000 --- a/docs/guide_clu2adv_dynamic_block.md +++ /dev/null @@ -1,98 +0,0 @@ -# Guide to handle dynamic block limitations in regions_config and replication_specs - -The plugin command to convert `mongodbatlas_cluster` resources to `mongodbatlas_advanced_cluster` supports `dynamic` blocks for `regions_config` and `replication_specs`. However, there are some limitations when using `dynamic` blocks in these fields. This guide explains how to handle these limitations. - -If you need to use the plugin for use cases not yet supported, please send us [feedback](https://github.com/mongodb-labs/atlas-cli-plugin-terraform/issues). - -## Dynamic block and individual blocks in the same resource - -Dynamic block and individual blocks for `regions_config` or `replication_specs` are not supported at the same time. The recommended way to handle this is to remove the individual `regions_config` or `replication_specs` blocks and use a local variable to add the individual block information to the variable you're using in the `for_each` expression, using [concat](https://developer.hashicorp.com/terraform/language/functions/concat) if you're using a list or [setunion](https://developer.hashicorp.com/terraform/language/functions/setunion) for sets. - -Let's see an example with `regions_config`, it is the same for `replication_specs`. In the original configuration file, the `mongodb_cluster` resource is used inside a module that receives the `regions_config` elements in a `list` variable and we want to add an additional `region_config` with a read-only node. -```hcl -variable "replication_specs" { - type = object({ - num_shards = number - regions_config = list(object({ - region_name = string - electable_nodes = number - priority = number - read_only_nodes = number - })) - }) -} - -resource "mongodbatlas_cluster" "this" { - project_id = var.project_id - name = var.cluster_name - cluster_type = var.cluster_type - provider_name = var.provider_name - provider_instance_size_name = var.provider_instance_size_name - replication_specs { - num_shards = var.replication_specs.num_shards - dynamic "regions_config" { - for_each = var.replication_specs.regions_config - content { - region_name = regions_config.value.region_name - electable_nodes = regions_config.value.electable_nodes - priority = regions_config.value.priority - read_only_nodes = regions_config.value.read_only_nodes - } - } - regions_config { # individual region - region_name = "US_EAST_1" - read_only_nodes = 1 - } - } -} -``` - -We modify the configuration file to create an intermediate `local` variable to merge the `regions_config` variable elements and the additional `region_config`: -```hcl -variable "replication_specs" { - type = object({ - num_shards = number - regions_config = list(object({ - region_name = string - electable_nodes = number - priority = number - read_only_nodes = number - })) - }) -} - -locals { - regions_config_all = concat( - var.replication_specs.regions_config, - [ - { - region_name = "US_EAST_1" - electable_nodes = 0 - priority = 0 - read_only_nodes = 1 - }, - ] - ) -} - -resource "mongodbatlas_cluster" "this" { - project_id = var.project_id - name = var.cluster_name - cluster_type = var.cluster_type - provider_name = var.provider_name - provider_instance_size_name = var.provider_instance_size_name - replication_specs { - num_shards = var.replication_specs.num_shards - dynamic "regions_config" { - for_each = local.regions_config_all # changed to use the local variable - content { - region_name = regions_config.value.region_name - electable_nodes = regions_config.value.electable_nodes - priority = regions_config.value.priority - read_only_nodes = regions_config.value.read_only_nodes - } - } - } -} -``` -This modified configuration file has the same behavior as the original one, but it doesn't have individual blocks anymore, only the `dynamic` block, so it is supported by the plugin.