Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changelog/3912.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```release-note:enhancement
resource/mongodbatlas_advanced_cluster: Adds the `custom_openssl_cipher_config_tls13` attribute
```

```release-note:enhancement
data-source/mongodbatlas_advanced_cluster: Adds the `custom_openssl_cipher_config_tls13` attribute
```

```release-note:enhancement
data-source/mongodbatlas_advanced_clusters: Adds the `custom_openssl_cipher_config_tls13` attribute
```
1 change: 1 addition & 0 deletions docs/data-sources/advanced_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ Key-value pairs that categorize the cluster. Each key and value has a maximum le
* `change_stream_options_pre_and_post_images_expire_after_seconds` - (Optional) The minimum pre- and post-image retention time in seconds This parameter is only supported for MongoDB version 6.0 and above. Defaults to `-1`(off).
* `tls_cipher_config_mode` - The TLS cipher suite configuration mode. Valid values include `CUSTOM` or `DEFAULT`. The `DEFAULT` mode uses the default cipher suites. The `CUSTOM` mode allows you to specify custom cipher suites for both TLS 1.2 and TLS 1.3.
* `custom_openssl_cipher_config_tls12` - The custom OpenSSL cipher suite list for TLS 1.2. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.
* `custom_openssl_cipher_config_tls13` - The custom OpenSSL cipher suite list for TLS 1.3. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.

### pinned_fcv

Expand Down
1 change: 1 addition & 0 deletions docs/data-sources/advanced_clusters.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ Key-value pairs that categorize the cluster. Each key and value has a maximum le
* `change_stream_options_pre_and_post_images_expire_after_seconds` - (Optional) The minimum pre- and post-image retention time in seconds. This parameter is only supported for MongoDB version 6.0 and above. Defaults to `-1`(off).
* `tls_cipher_config_mode` - The TLS cipher suite configuration mode. Valid values include `CUSTOM` or `DEFAULT`. The `DEFAULT` mode uses the default cipher suites. The `CUSTOM` mode allows you to specify custom cipher suites for both TLS 1.2 and TLS 1.3.
* `custom_openssl_cipher_config_tls12` - The custom OpenSSL cipher suite list for TLS 1.2. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.
* `custom_openssl_cipher_config_tls13` - The custom OpenSSL cipher suite list for TLS 1.3. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.

### pinned_fcv

Expand Down
1 change: 1 addition & 0 deletions docs/resources/advanced_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ Include **desired options** within advanced_configuration:
* `default_max_time_ms` - (Optional) Default time limit in milliseconds for individual read operations to complete. This option corresponds to the [defaultMaxTimeMS](https://www.mongodb.com/docs/upcoming/reference/cluster-parameters/defaultMaxTimeMS/) cluster parameter. This parameter is supported only for MongoDB version 8.0 and above.
* `tls_cipher_config_mode` - (Optional) The TLS cipher suite configuration mode. Valid values include `CUSTOM` or `DEFAULT`. The `DEFAULT` mode uses the default cipher suites. The `CUSTOM` mode allows you to specify custom cipher suites for both TLS 1.2 and TLS 1.3. To unset, this should be set back to `DEFAULT`.
* `custom_openssl_cipher_config_tls12` - (Optional) The custom OpenSSL cipher suite list for TLS 1.2. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.
* `custom_openssl_cipher_config_tls13` - (Optional) The custom OpenSSL cipher suite list for TLS 1.3. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.


### tags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (

func AddAdvancedConfig(ctx context.Context, tfModel *TFModel, input *ProcessArgs, diags *diag.Diagnostics) {
var advancedConfig TFAdvancedConfigurationModel
var customCipherConfig *[]string
var customCipherConfigTLS12 *[]string
var customCipherConfigTLS13 *[]string

if input.ArgsDefault != nil {
// Using the new API as source of Truth, only use `inputLegacy` for fields not in `input`
Expand All @@ -37,9 +38,11 @@ func AddAdvancedConfig(ctx context.Context, tfModel *TFModel, input *ProcessArgs
MinimumEnabledTlsProtocol: types.StringValue(conversion.SafeValue(input.ArgsDefault.MinimumEnabledTlsProtocol)),
TlsCipherConfigMode: types.StringValue(conversion.SafeValue(input.ArgsDefault.TlsCipherConfigMode)),
}
customCipherConfig = input.ArgsDefault.CustomOpensslCipherConfigTls12
customCipherConfigTLS12 = input.ArgsDefault.CustomOpensslCipherConfigTls12
customCipherConfigTLS13 = input.ArgsDefault.CustomOpensslCipherConfigTls13
}
advancedConfig.CustomOpensslCipherConfigTls12 = customOpensslCipherConfigTLS12(ctx, diags, customCipherConfig)
advancedConfig.CustomOpensslCipherConfigTls12 = customOpensslCipherConfig(ctx, diags, customCipherConfigTLS12)
advancedConfig.CustomOpensslCipherConfigTls13 = customOpensslCipherConfig(ctx, diags, customCipherConfigTLS13)

overrideTLSIfClusterAdvancedConfigPresent(ctx, diags, &advancedConfig, input.ClusterAdvancedConfig)

Expand All @@ -54,14 +57,15 @@ func overrideTLSIfClusterAdvancedConfigPresent(ctx context.Context, diags *diag.
}
tfAdvConfig.MinimumEnabledTlsProtocol = types.StringValue(conversion.SafeValue(conf.MinimumEnabledTlsProtocol))
tfAdvConfig.TlsCipherConfigMode = types.StringValue(conversion.SafeValue(conf.TlsCipherConfigMode))
tfAdvConfig.CustomOpensslCipherConfigTls12 = customOpensslCipherConfigTLS12(ctx, diags, conf.CustomOpensslCipherConfigTls12)
tfAdvConfig.CustomOpensslCipherConfigTls12 = customOpensslCipherConfig(ctx, diags, conf.CustomOpensslCipherConfigTls12)
tfAdvConfig.CustomOpensslCipherConfigTls13 = customOpensslCipherConfig(ctx, diags, conf.CustomOpensslCipherConfigTls13)
}

func customOpensslCipherConfigTLS12(ctx context.Context, diags *diag.Diagnostics, customOpensslCipherConfigTLS12 *[]string) types.Set {
if customOpensslCipherConfigTLS12 == nil {
func customOpensslCipherConfig(ctx context.Context, diags *diag.Diagnostics, customOpensslCipherConfig *[]string) types.Set {
if customOpensslCipherConfig == nil {
return types.SetNull(types.StringType)
}
val, d := types.SetValueFrom(ctx, types.StringType, customOpensslCipherConfigTLS12)
val, d := types.SetValueFrom(ctx, types.StringType, customOpensslCipherConfig)
diags.Append(d...)
return val
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,16 @@ func newClusterAdvancedConfiguration(ctx context.Context, objInput *types.Object
return nil
}

var customCipherConfigTLS13 *[]string
if !inputAdvConfig.CustomOpensslCipherConfigTls13.IsNull() && !inputAdvConfig.CustomOpensslCipherConfigTls13.IsUnknown() {
customCipherConfigTLS13 = conversion.Pointer(conversion.TypesSetToString(ctx, inputAdvConfig.CustomOpensslCipherConfigTls13))
}

Comment on lines +64 to +68
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not done for tls1.2, why is it needed for 1.3?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For TLS 1.2 I left the existing behaviour unchanged in this PR because the current tests and recorded fixtures rely on encountering an empty array shape, but introducing customOpensslCipherConfigTls13 the same way (always sending an empty array when unset) was causing unit test failures.

I think it would be better to omit customOpensslCipherConfigTls12 when it isn’t configured, unless I'm missing some additional context. Right now, we send it as an empty array when the set is empty, and the API ignores that value, so omitting the field entirely would be my preference.

I’ve added a "omit when unset” behaviour only for customOpensslCipherConfigTls13 so we don’t introduce a new always‑empty field into the payload, and both existing and new tests pass. I think we could align TLS 1.2 to the same pattern if we agree it's a good implementation in terms of consistency.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks! Yes, I would aim to align both as well.

Copy link
Member

@lantoli lantoli Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those "mockable" unit tests are not an issue, it's ok that they fail if request or response payloads change, in that case testdata files must be re-captured with new payloads, I don't think this should influence the decision.

It's the same creating the cluster, but I think it's different in cluster updates: I think sending empty will clear the attribute whereas not sending it will probably keep the current value.

I recommend to create an acc test with an update from 1.2 to 1.3, I suppose it will be a very typical use case in customers with existing clusters, something like:

  • Step 1: Define a cluster with TLS1_2 and customOpensslCipherConfigTls12
  • Step 2: Update the cluster cluster to TLS1_3 and customOpensslCipherConfigTls13 (to check customOpensslCipherConfigTls12 is cleared)
  • Step 3: Same step 1 - TLS1_2 and customOpensslCipherConfigTls12 (to check customOpensslCipherConfigTls13 is cleared)

and see if cluster upgrades successfully from TLS1_2 to TLS1_3 and back

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok sounds good then, I will follow-up on this and will create an additional acceptance test

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edited the acc test to have 3 steps and more certainty that it works as expected.

to clarify about the mockable unit tests, they shouldn't influence how to handle the attributes (we can re-generate the test data), and if customOpensslCipherConfigTls12 and customOpensslCipherConfigTls13 are implemented equally in Atlas, I think we should also follow the same approach for them in Terraform, which ever we think it's better instead of mixing approaches.

return &admin.ApiAtlasClusterAdvancedConfiguration{
MinimumEnabledTlsProtocol: conversion.NilForUnknown(inputAdvConfig.MinimumEnabledTlsProtocol, inputAdvConfig.MinimumEnabledTlsProtocol.ValueStringPointer()),
TlsCipherConfigMode: conversion.NilForUnknown(inputAdvConfig.TlsCipherConfigMode, inputAdvConfig.TlsCipherConfigMode.ValueStringPointer()),
CustomOpensslCipherConfigTls12: conversion.Pointer(conversion.TypesSetToString(ctx, inputAdvConfig.CustomOpensslCipherConfigTls12)),
CustomOpensslCipherConfigTls13: customCipherConfigTLS13,
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/service/advancedcluster/plan_modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var (
// Change mappings uses `attribute_name`, it doesn't care about the nested level.
attributeRootChangeMapping = map[string][]string{
"replication_specs": {},
"tls_cipher_config_mode": {"custom_openssl_cipher_config_tls12"},
"tls_cipher_config_mode": {"custom_openssl_cipher_config_tls12", "custom_openssl_cipher_config_tls13"},
"cluster_type": {"config_server_management_mode", "config_server_type"}, // computed values of config server change when REPLICA_SET changes to SHARDED
}
attributeReplicationSpecChangeMapping = map[string][]string{
Expand Down
47 changes: 45 additions & 2 deletions internal/service/advancedcluster/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1919,6 +1919,9 @@ func configAdvanced(t *testing.T, projectID, clusterName, mongoDBMajorVersion st
if p.CustomOpensslCipherConfigTls12 != nil && len(*p.CustomOpensslCipherConfigTls12) > 0 {
advancedConfig += fmt.Sprintf("custom_openssl_cipher_config_tls12 = [%s]\n", acc.JoinQuotedStrings(*p.CustomOpensslCipherConfigTls12))
}
if p.CustomOpensslCipherConfigTls13 != nil && len(*p.CustomOpensslCipherConfigTls13) > 0 {
advancedConfig += fmt.Sprintf("custom_openssl_cipher_config_tls13 = [%s]\n", acc.JoinQuotedStrings(*p.CustomOpensslCipherConfigTls13))
}
}
if p.MinimumEnabledTlsProtocol != nil {
advancedConfig += fmt.Sprintf("minimum_enabled_tls_protocol = %[1]q\n", *p.MinimumEnabledTlsProtocol)
Expand Down Expand Up @@ -1973,9 +1976,14 @@ func checkAdvanced(name, tls string, processArgs *admin.ClusterDescriptionProces
advancedConfig["advanced_configuration.default_max_time_ms"] = strconv.Itoa(*processArgs.DefaultMaxTimeMS)
}

if processArgs.TlsCipherConfigMode != nil && processArgs.CustomOpensslCipherConfigTls12 != nil {
if processArgs.TlsCipherConfigMode != nil && (processArgs.CustomOpensslCipherConfigTls12 != nil || processArgs.CustomOpensslCipherConfigTls13 != nil) {
advancedConfig["advanced_configuration.tls_cipher_config_mode"] = "CUSTOM"
advancedConfig["advanced_configuration.custom_openssl_cipher_config_tls12.#"] = strconv.Itoa(len(*processArgs.CustomOpensslCipherConfigTls12))
if processArgs.CustomOpensslCipherConfigTls12 != nil {
advancedConfig["advanced_configuration.custom_openssl_cipher_config_tls12.#"] = strconv.Itoa(len(*processArgs.CustomOpensslCipherConfigTls12))
}
if processArgs.CustomOpensslCipherConfigTls13 != nil {
advancedConfig["advanced_configuration.custom_openssl_cipher_config_tls13.#"] = strconv.Itoa(len(*processArgs.CustomOpensslCipherConfigTls13))
}
} else {
advancedConfig["advanced_configuration.tls_cipher_config_mode"] = "DEFAULT"
}
Expand Down Expand Up @@ -2051,6 +2059,41 @@ func checkAdvancedDefaultWrite(name, writeConcern, tls string) resource.TestChec
pluralChecks...)
}

func TestAccAdvancedCluster_tls13CustomCiphers(t *testing.T) {
var (
projectID, clusterName = acc.ProjectIDExecutionWithCluster(t, 4)
resourceName = "mongodbatlas_advanced_cluster.test"
processArgs = &admin.ClusterDescriptionProcessArgs20240805{
TlsCipherConfigMode: conversion.StringPtr("CUSTOM"),
CustomOpensslCipherConfigTls13: conversion.Pointer([]string{"TLS_AES_128_GCM_SHA256"}),
MinimumEnabledTlsProtocol: conversion.StringPtr("TLS1_3"),
}
)

advancedConfig := configAdvanced(t, projectID, clusterName, "", processArgs)
check := resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "project_id", projectID),
resource.TestCheckResourceAttr(resourceName, "name", clusterName),
resource.TestCheckResourceAttr(resourceName, "advanced_configuration.tls_cipher_config_mode", "CUSTOM"),
resource.TestCheckResourceAttr(resourceName, "advanced_configuration.custom_openssl_cipher_config_tls13.#", "1"),

resource.TestCheckResourceAttr(dataSourceName, "advanced_configuration.tls_cipher_config_mode", "CUSTOM"),
resource.TestCheckResourceAttr(dataSourceName, "advanced_configuration.custom_openssl_cipher_config_tls13.#", "1"),

resource.TestCheckResourceAttr(dataSourcePluralName, "results.0.advanced_configuration.tls_cipher_config_mode", "CUSTOM"),
resource.TestCheckResourceAttr(dataSourcePluralName, "results.0.advanced_configuration.custom_openssl_cipher_config_tls13.#", "1"),
)

resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
Steps: []resource.TestStep{
{
Config: advancedConfig,
Check: check,
},
},
})
}
func configReplicationSpecsAutoScaling(t *testing.T, projectID, clusterName string, autoScalingSettings *admin.AdvancedAutoScalingSettings, elecInstanceSize string, elecDiskSizeGB, analyticsNodeCount int) string {
t.Helper()
lifecycleIgnoreChanges := ""
Expand Down
8 changes: 8 additions & 0 deletions internal/service/advancedcluster/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,12 @@ func AdvancedConfigurationSchema(ctx context.Context) schema.SingleNestedAttribu
ElementType: types.StringType,
MarkdownDescription: "The custom OpenSSL cipher suite list for TLS 1.2. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.",
},
"custom_openssl_cipher_config_tls13": schema.SetAttribute{
Computed: true,
Optional: true,
ElementType: types.StringType,
MarkdownDescription: "The custom OpenSSL cipher suite list for TLS 1.3. This field is only valid when `tls_cipher_config_mode` is set to `CUSTOM`.",
},
"tls_cipher_config_mode": schema.StringAttribute{
Optional: true,
Computed: true,
Expand Down Expand Up @@ -699,6 +705,7 @@ var SpecsObjType = types.ObjectType{AttrTypes: map[string]attr.Type{
type TFAdvancedConfigurationModel struct {
OplogMinRetentionHours types.Float64 `tfsdk:"oplog_min_retention_hours"`
CustomOpensslCipherConfigTls12 types.Set `tfsdk:"custom_openssl_cipher_config_tls12"`
CustomOpensslCipherConfigTls13 types.Set `tfsdk:"custom_openssl_cipher_config_tls13"`
MinimumEnabledTlsProtocol types.String `tfsdk:"minimum_enabled_tls_protocol"`
DefaultWriteConcern types.String `tfsdk:"default_write_concern"`
TlsCipherConfigMode types.String `tfsdk:"tls_cipher_config_mode"`
Expand Down Expand Up @@ -726,6 +733,7 @@ var AdvancedConfigurationObjType = types.ObjectType{AttrTypes: map[string]attr.T
"default_max_time_ms": types.Int64Type,
"tls_cipher_config_mode": types.StringType,
"custom_openssl_cipher_config_tls12": types.SetType{ElemType: types.StringType},
"custom_openssl_cipher_config_tls13": types.SetType{ElemType: types.StringType},
}}

type TFPinnedFCVModel struct {
Expand Down
Loading