diff --git a/.changelog/26411.txt b/.changelog/26411.txt new file mode 100644 index 00000000000..97898ac4966 --- /dev/null +++ b/.changelog/26411.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_dms_endpoint: Add `redis_settings` configuration block +``` \ No newline at end of file diff --git a/internal/service/dms/endpoint.go b/internal/service/dms/endpoint.go index 43dd1315291..55ec11d92d2 100644 --- a/internal/service/dms/endpoint.go +++ b/internal/service/dms/endpoint.go @@ -333,6 +333,49 @@ func ResourceEndpoint() *schema.Resource { Optional: true, ConflictsWith: []string{"secrets_manager_access_role_arn", "secrets_manager_arn"}, }, + "redis_settings": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auth_password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "auth_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(dms.RedisAuthTypeValue_Values(), false), + }, + "auth_user_name": { + Type: schema.TypeString, + Optional: true, + }, + "port": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "server_name": { + Type: schema.TypeString, + Required: true, + }, + "ssl_ca_certificate_arn": { + Type: schema.TypeString, + Optional: true, + }, + "ssl_security_protocol": { + Type: schema.TypeString, + Optional: true, + Default: dms.SslSecurityProtocolValueSslEncryption, + ValidateFunc: validation.StringInSlice(dms.SslSecurityProtocolValue_Values(), false), + }, + }, + }, + }, "redshift_settings": { Type: schema.TypeList, Optional: true, @@ -757,6 +800,8 @@ func resourceEndpointCreate(d *schema.ResourceData, meta interface{}) error { // Set connection info in top-level namespace as well expandTopLevelConnectionInfo(d, input) } + case engineNameRedis: + input.RedisSettings = expandRedisSettings(d.Get("redis_settings").([]interface{})[0].(map[string]interface{})) case engineNameRedshift: var settings = &dms.RedshiftSettings{ DatabaseName: aws.String(d.Get("database_name").(string)), @@ -1062,6 +1107,11 @@ func resourceEndpointUpdate(d *schema.ResourceData, meta interface{}) error { expandTopLevelConnectionInfoModify(d, input) } } + case engineNameRedis: + if d.HasChanges("redis_settings") { + input.RedisSettings = expandRedisSettings(d.Get("redis_settings").([]interface{})[0].(map[string]interface{})) + input.EngineName = aws.String(engineName) + } case engineNameRedshift: if d.HasChanges( "username", "password", "server_name", "port", "database_name", @@ -1223,6 +1273,10 @@ func resourceEndpointCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, if v, ok := diff.GetOk("mongodb_settings"); !ok || len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { return fmt.Errorf("mongodb_settings must be set when engine_name = %q", engineName) } + case engineNameRedis: + if v, ok := diff.GetOk("redis_settings"); !ok || len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { + return fmt.Errorf("redis_settings must be set when engine_name = %q", engineName) + } case engineNameS3: if v, ok := diff.GetOk("s3_settings"); !ok || len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { return fmt.Errorf("s3_settings must be set when engine_name = %q", engineName) @@ -1317,6 +1371,14 @@ func resourceEndpointSetState(d *schema.ResourceData, endpoint *dms.Endpoint) er } else { flattenTopLevelConnectionInfo(d, endpoint) } + case engineNameRedis: + // Auth password isn't returned in API. Propagate state value. + tfMap := flattenRedisSettings(endpoint.RedisSettings) + tfMap["auth_password"] = d.Get("redis_settings.0.auth_password").(string) + + if err := d.Set("redis_settings", []interface{}{tfMap}); err != nil { + return fmt.Errorf("setting redis_settings: %w", err) + } case engineNameRedshift: if endpoint.RedshiftSettings != nil { d.Set("username", endpoint.RedshiftSettings.Username) @@ -1648,6 +1710,70 @@ func flattenMongoDBSettings(settings *dms.MongoDbSettings) []map[string]interfac return []map[string]interface{}{m} } +func expandRedisSettings(tfMap map[string]interface{}) *dms.RedisSettings { + if tfMap == nil { + return nil + } + + apiObject := &dms.RedisSettings{} + + if v, ok := tfMap["auth_password"].(string); ok && v != "" { + apiObject.AuthPassword = aws.String(v) + } + if v, ok := tfMap["auth_type"].(string); ok && v != "" { + apiObject.AuthType = aws.String(v) + } + if v, ok := tfMap["auth_user_name"].(string); ok && v != "" { + apiObject.AuthUserName = aws.String(v) + } + if v, ok := tfMap["port"].(int); ok { + apiObject.Port = aws.Int64(int64(v)) + } + if v, ok := tfMap["server_name"].(string); ok && v != "" { + apiObject.ServerName = aws.String(v) + } + if v, ok := tfMap["ssl_ca_certificate_arn"].(string); ok && v != "" { + apiObject.SslCaCertificateArn = aws.String(v) + } + if v, ok := tfMap["ssl_security_protocol"].(string); ok && v != "" { + apiObject.SslSecurityProtocol = aws.String(v) + } + + return apiObject +} + +func flattenRedisSettings(apiObject *dms.RedisSettings) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AuthPassword; v != nil { + tfMap["auth_password"] = aws.StringValue(v) + } + if v := apiObject.AuthType; v != nil { + tfMap["auth_type"] = aws.StringValue(v) + } + if v := apiObject.AuthUserName; v != nil { + tfMap["auth_user_name"] = aws.StringValue(v) + } + if v := apiObject.Port; v != nil { + tfMap["port"] = aws.Int64Value(v) + } + if v := apiObject.ServerName; v != nil { + tfMap["server_name"] = aws.StringValue(v) + } + if v := apiObject.SslCaCertificateArn; v != nil { + tfMap["ssl_ca_certificate_arn"] = aws.StringValue(v) + } + if v := apiObject.SslSecurityProtocol; v != nil { + tfMap["ssl_security_protocol"] = aws.StringValue(v) + } + + return tfMap +} + func flattenRedshiftSettings(settings *dms.RedshiftSettings) []map[string]interface{} { if settings == nil { return []map[string]interface{}{} diff --git a/internal/service/dms/endpoint_test.go b/internal/service/dms/endpoint_test.go index ae0a030824f..73c922d1c3a 100644 --- a/internal/service/dms/endpoint_test.go +++ b/internal/service/dms/endpoint_test.go @@ -1310,6 +1310,54 @@ func TestAccDMSEndpoint_db2(t *testing.T) { }) } +func TestAccDMSEndpoint_redis(t *testing.T) { + resourceName := "aws_dms_endpoint.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, dms.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEndpointConfig_redis(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckEndpointExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_arn"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.auth_password", ""), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.auth_type", "none"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.auth_user_name", ""), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.port", "6379"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.server_name", "redis1.test"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.ssl_ca_certificate_arn", ""), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.ssl_security_protocol", "plaintext"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEndpointConfig_redisUpdate(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckEndpointExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "redis_settings.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.auth_password", "avoid-plaintext-passwords"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.auth_type", "auth-role"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.auth_user_name", "tfacctest"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.port", "6379"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.server_name", "redis2.test"), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.ssl_ca_certificate_arn", ""), + resource.TestCheckResourceAttr(resourceName, "redis_settings.0.ssl_security_protocol", "ssl-encryption"), + ), + }, + }, + }) +} + func TestAccDMSEndpoint_Redshift_basic(t *testing.T) { resourceName := "aws_dms_endpoint.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2918,6 +2966,41 @@ resource "aws_dms_endpoint" "test" { `, rName) } +func testAccEndpointConfig_redis(rName string) string { + return fmt.Sprintf(` +resource "aws_dms_endpoint" "test" { + endpoint_id = %[1]q + endpoint_type = "target" + engine_name = "redis" + + redis_settings { + auth_type = "none" + port = 6379 + server_name = "redis1.test" + ssl_security_protocol = "plaintext" + } +} +`, rName) +} + +func testAccEndpointConfig_redisUpdate(rName string) string { + return fmt.Sprintf(` +resource "aws_dms_endpoint" "test" { + endpoint_id = %[1]q + endpoint_type = "target" + engine_name = "redis" + + redis_settings { + auth_password = "avoid-plaintext-passwords" + auth_type = "auth-role" + auth_user_name = "tfacctest" + port = 6379 + server_name = "redis2.test" + } +} +`, rName) +} + func testAccEndpointConfig_redshiftBase(rName string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptInExclude("usw2-az2"), fmt.Sprintf(` resource "aws_redshift_cluster" "test" { diff --git a/website/docs/r/dms_endpoint.html.markdown b/website/docs/r/dms_endpoint.html.markdown index 095e22cd677..b335c0e3b1b 100644 --- a/website/docs/r/dms_endpoint.html.markdown +++ b/website/docs/r/dms_endpoint.html.markdown @@ -125,6 +125,18 @@ The following arguments are optional: * `extract_doc_id` - (Optional) Document ID. Use this setting when `nesting_level` is set to `none`. Default is `false`. * `nesting_level` - (Optional) Specifies either document or table mode. Default is `none`. Valid values are `one` (table mode) and `none` (document mode). +### redis_settings + +-> Additional information can be found in the [Using Redis as a target for AWS Database Migration Service](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.Redis.html). + +* `auth_password` - (Optional) The password provided with the auth-role and auth-token options of the AuthType setting for a Redis target endpoint. +* `auth_type` - (Required) The type of authentication to perform when connecting to a Redis target. Options include `none`, `auth-token`, and `auth-role`. The `auth-token` option requires an `auth_password` value to be provided. The `auth-role` option requires `auth_user_name` and `auth_password` values to be provided. +* `auth_user_name` - (Optional) The username provided with the `auth-role` option of the AuthType setting for a Redis target endpoint. +* `server_name` - (Required) Fully qualified domain name of the endpoint. +* `port` - (Required) Transmission Control Protocol (TCP) port for the endpoint. +* `ssl_ca_certificate_arn` - (Optional) The Amazon Resource Name (ARN) for the certificate authority (CA) that DMS uses to connect to your Redis target endpoint. +* `ssl_security_protocol`- (Optional) The plaintext option doesn't provide Transport Layer Security (TLS) encryption for traffic between endpoint and database. Options include `plaintext`, `ssl-encryption`. The default is `ssl-encryption`. + ### redshift_settings -> Additional information can be found in the [Using Amazon Redshift as a Target for AWS Database Migration Service documentation](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.Redshift.html).