Add `name_prefix` to RDS resources (#13232)

Adds `name_prefix` (or, in some cases, `identifier_prefix`) support to all AWS RDS resources.
This commit is contained in:
Joshua Spence 2017-04-01 04:22:57 +11:00 committed by Paul Stack
parent e7c3e8df68
commit d25c310468
23 changed files with 985 additions and 157 deletions

View File

@ -105,7 +105,15 @@ func resourceAwsDbInstance() *schema.Resource {
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsId,
ConflictsWith: []string{"identifier_prefix"},
ValidateFunc: validateRdsIdentifier,
},
"identifier_prefix": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsIdentifierPrefix,
},
"instance_class": {
@ -336,10 +344,16 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error
conn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
identifier := d.Get("identifier").(string)
// Generate a unique ID for the user
if identifier == "" {
identifier = resource.PrefixedUniqueId("tf-")
var identifier string
if v, ok := d.GetOk("identifier"); ok {
identifier = v.(string)
} else {
if v, ok := d.GetOk("identifier_prefix"); ok {
identifier = resource.PrefixedUniqueId(v.(string))
} else {
identifier = resource.UniqueId()
}
// SQL Server identifier size is max 15 chars, so truncate
if engine := d.Get("engine").(string); engine != "" {
if strings.Contains(strings.ToLower(engine), "sqlserver") {

View File

@ -53,6 +53,46 @@ func TestAccAWSDBInstance_basic(t *testing.T) {
})
}
func TestAccAWSDBInstance_namePrefix(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBInstanceConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBInstanceExists("aws_db_instance.test", &v),
testAccCheckAWSDBInstanceAttributes(&v),
resource.TestMatchResourceAttr(
"aws_db_instance.test", "identifier", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBInstance_generatedName(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBInstanceConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBInstanceExists("aws_db_instance.test", &v),
testAccCheckAWSDBInstanceAttributes(&v),
),
},
},
})
}
func TestAccAWSDBInstance_kmsKey(t *testing.T) {
var v rds.DBInstance
keyRegex := regexp.MustCompile("^arn:aws:kms:")
@ -628,6 +668,39 @@ resource "aws_db_instance" "bar" {
}
}`
const testAccAWSDBInstanceConfig_namePrefix = `
resource "aws_db_instance" "test" {
allocated_storage = 10
engine = "MySQL"
identifier_prefix = "tf-test-"
instance_class = "db.t1.micro"
password = "password"
username = "root"
security_group_names = ["default"]
publicly_accessible = true
skip_final_snapshot = true
timeouts {
create = "30m"
}
}`
const testAccAWSDBInstanceConfig_generatedName = `
resource "aws_db_instance" "test" {
allocated_storage = 10
engine = "MySQL"
instance_class = "db.t1.micro"
password = "password"
username = "root"
security_group_names = ["default"]
publicly_accessible = true
skip_final_snapshot = true
timeouts {
create = "30m"
}
}`
var testAccAWSDBInstanceConfigKmsKeyId = `
resource "aws_kms_key" "foo" {
description = "Terraform acc test %s"

View File

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"log"
"regexp"
"time"
"github.com/aws/aws-sdk-go/aws"
@ -32,10 +31,19 @@ func resourceAwsDbOptionGroup() *schema.Resource {
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbOptionGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateDbOptionGroupNamePrefix,
},
"engine_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
@ -48,8 +56,9 @@ func resourceAwsDbOptionGroup() *schema.Resource {
},
"option_group_description": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
ForceNew: true,
Default: "Managed by Terraform",
},
"option": &schema.Schema{
@ -107,11 +116,20 @@ func resourceAwsDbOptionGroupCreate(d *schema.ResourceData, meta interface{}) er
rdsconn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := &rds.CreateOptionGroupInput{
EngineName: aws.String(d.Get("engine_name").(string)),
MajorEngineVersion: aws.String(d.Get("major_engine_version").(string)),
OptionGroupDescription: aws.String(d.Get("option_group_description").(string)),
OptionGroupName: aws.String(d.Get("name").(string)),
OptionGroupName: aws.String(groupName),
Tags: tags,
}
@ -121,7 +139,7 @@ func resourceAwsDbOptionGroupCreate(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error creating DB Option Group: %s", err)
}
d.SetId(d.Get("name").(string))
d.SetId(groupName)
log.Printf("[INFO] DB Option Group ID: %s", d.Id())
return resourceAwsDbOptionGroupUpdate(d, meta)
@ -343,28 +361,3 @@ func buildRDSOptionGroupARN(identifier, partition, accountid, region string) (st
arn := fmt.Sprintf("arn:%s:rds:%s:%s:og:%s", partition, region, accountid, identifier)
return arn, nil
}
func validateDbOptionGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters and hyphens allowed in %q", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if regexp.MustCompile(`-$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot end with a hyphen", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 255 characters", k))
}
return
}

View File

@ -2,6 +2,7 @@ package aws
import (
"fmt"
"regexp"
"testing"
"github.com/aws/aws-sdk-go/aws"
@ -34,6 +35,66 @@ func TestAccAWSDBOptionGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBOptionGroup_namePrefix(t *testing.T) {
var v rds.OptionGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBOptionGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDBOptionGroup_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v),
testAccCheckAWSDBOptionGroupAttributes(&v),
resource.TestMatchResourceAttr(
"aws_db_option_group.test", "name", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBOptionGroup_generatedName(t *testing.T) {
var v rds.OptionGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBOptionGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDBOptionGroup_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v),
testAccCheckAWSDBOptionGroupAttributes(&v),
),
},
},
})
}
func TestAccAWSDBOptionGroup_defaultDescription(t *testing.T) {
var v rds.OptionGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBOptionGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDBOptionGroup_defaultDescription(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v),
resource.TestCheckResourceAttr(
"aws_db_option_group.test", "option_group_description", "Managed by Terraform"),
),
},
},
})
}
func TestAccAWSDBOptionGroup_basicDestroyWithInstance(t *testing.T) {
rName := fmt.Sprintf("option-group-test-terraform-%s", acctest.RandString(5))
@ -160,42 +221,6 @@ func testAccCheckAWSDBOptionGroupAttributes(v *rds.OptionGroup) resource.TestChe
}
}
func TestResourceAWSDBOptionGroupName_validation(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "testing123!",
ErrCount: 1,
},
{
Value: "1testing123",
ErrCount: 1,
},
{
Value: "testing--123",
ErrCount: 1,
},
{
Value: "testing123-",
ErrCount: 1,
},
{
Value: randomString(256),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbOptionGroupName(tc.Value, "aws_db_option_group_name")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Option Group Name to trigger a validation error")
}
}
}
func testAccCheckAWSDBOptionGroupExists(n string, v *rds.OptionGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
@ -387,3 +412,30 @@ resource "aws_db_option_group" "bar" {
}
`, r)
}
const testAccAWSDBOptionGroup_namePrefix = `
resource "aws_db_option_group" "test" {
name_prefix = "tf-test-"
option_group_description = "Test option group for terraform"
engine_name = "mysql"
major_engine_version = "5.6"
}
`
const testAccAWSDBOptionGroup_generatedName = `
resource "aws_db_option_group" "test" {
option_group_description = "Test option group for terraform"
engine_name = "mysql"
major_engine_version = "5.6"
}
`
func testAccAWSDBOptionGroup_defaultDescription(n int) string {
return fmt.Sprintf(`
resource "aws_db_option_group" "test" {
name = "tf-test-%d"
engine_name = "mysql"
major_engine_version = "5.6"
}
`, n)
}

View File

@ -33,10 +33,19 @@ func resourceAwsDbParameterGroup() *schema.Resource {
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbParamGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateDbParamGroupNamePrefix,
},
"family": &schema.Schema{
Type: schema.TypeString,
Required: true,
@ -81,8 +90,17 @@ func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{})
rdsconn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := rds.CreateDBParameterGroupInput{
DBParameterGroupName: aws.String(d.Get("name").(string)),
DBParameterGroupName: aws.String(groupName),
DBParameterGroupFamily: aws.String(d.Get("family").(string)),
Description: aws.String(d.Get("description").(string)),
Tags: tags,

View File

@ -3,6 +3,7 @@ package aws
import (
"fmt"
"math/rand"
"regexp"
"testing"
"time"
@ -290,6 +291,44 @@ func TestAccAWSDBParameterGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBParameterGroup_namePrefix(t *testing.T) {
var v rds.DBParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBParameterGroupConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBParameterGroupExists("aws_db_parameter_group.test", &v),
resource.TestMatchResourceAttr(
"aws_db_parameter_group.test", "name", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBParameterGroup_generatedName(t *testing.T) {
var v rds.DBParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBParameterGroupConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBParameterGroupExists("aws_db_parameter_group.test", &v),
),
},
},
})
}
func TestAccAWSDBParameterGroup_withApplyMethod(t *testing.T) {
var v rds.DBParameterGroup
@ -671,3 +710,16 @@ resource "aws_db_parameter_group" "large" {
parameter { name = "tx_isolation" value = "REPEATABLE-READ" }
}`, n)
}
const testAccDBParameterGroupConfig_namePrefix = `
resource "aws_db_parameter_group" "test" {
name_prefix = "tf-test-"
family = "mysql5.6"
}
`
const testAccDBParameterGroupConfig_generatedName = `
resource "aws_db_parameter_group" "test" {
family = "mysql5.6"
}
`

View File

@ -3,7 +3,6 @@ package aws
import (
"fmt"
"log"
"regexp"
"strings"
"time"
@ -32,9 +31,18 @@ func resourceAwsDbSubnetGroup() *schema.Resource {
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ValidateFunc: validateSubnetGroupName,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbSubnetGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateDbSubnetGroupNamePrefix,
},
"description": &schema.Schema{
@ -65,8 +73,17 @@ func resourceAwsDbSubnetGroupCreate(d *schema.ResourceData, meta interface{}) er
subnetIds[i] = aws.String(subnetId.(string))
}
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := rds.CreateDBSubnetGroupInput{
DBSubnetGroupName: aws.String(d.Get("name").(string)),
DBSubnetGroupName: aws.String(groupName),
DBSubnetGroupDescription: aws.String(d.Get("description").(string)),
SubnetIds: subnetIds,
Tags: tags,
@ -238,20 +255,3 @@ func buildRDSsubgrpARN(identifier, partition, accountid, region string) (string,
return arn, nil
}
func validateSubnetGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 255 characters", k))
}
if regexp.MustCompile(`(?i)^default$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q is not allowed as %q", "Default", k))
}
return
}

View File

@ -2,6 +2,7 @@ package aws
import (
"fmt"
"regexp"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
@ -43,6 +44,46 @@ func TestAccAWSDBSubnetGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBSubnetGroup_namePrefix(t *testing.T) {
var v rds.DBSubnetGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDBSubnetGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBSubnetGroupConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckDBSubnetGroupExists(
"aws_db_subnet_group.test", &v),
resource.TestMatchResourceAttr(
"aws_db_subnet_group.test", "name", regexp.MustCompile("^tf_test-")),
),
},
},
})
}
func TestAccAWSDBSubnetGroup_generatedName(t *testing.T) {
var v rds.DBSubnetGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDBSubnetGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBSubnetGroupConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckDBSubnetGroupExists(
"aws_db_subnet_group.test", &v),
),
},
},
})
}
// Regression test for https://github.com/hashicorp/terraform/issues/2603 and
// https://github.com/hashicorp/terraform/issues/2664
func TestAccAWSDBSubnetGroup_withUndocumentedCharacters(t *testing.T) {
@ -105,38 +146,6 @@ func TestAccAWSDBSubnetGroup_updateDescription(t *testing.T) {
})
}
func TestResourceAWSDBSubnetGroupNameValidation(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "tEsting",
ErrCount: 1,
},
{
Value: "testing?",
ErrCount: 1,
},
{
Value: "default",
ErrCount: 1,
},
{
Value: randomString(300),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateSubnetGroupName(tc.Value, "aws_db_subnet_group")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Subnet Group name to trigger a validation error")
}
}
}
func testAccCheckDBSubnetGroupDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).rdsconn
@ -263,6 +272,49 @@ resource "aws_db_subnet_group" "foo" {
}`, rName)
}
const testAccDBSubnetGroupConfig_namePrefix = `
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.1.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.2.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name_prefix = "tf_test-"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}`
const testAccDBSubnetGroupConfig_generatedName = `
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.1.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.2.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}`
const testAccDBSubnetGroupConfig_withUnderscoresAndPeriodsAndSpaces = `
resource "aws_vpc" "main" {
cidr_block = "192.168.0.0/16"

View File

@ -37,9 +37,18 @@ func resourceAwsRDSCluster() *schema.Resource {
"cluster_identifier": {
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsId,
ConflictsWith: []string{"cluster_identifier_prefix"},
ValidateFunc: validateRdsIdentifier,
},
"cluster_identifier_prefix": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsIdentifierPrefix,
},
"cluster_members": {
@ -225,6 +234,19 @@ func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error
conn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var identifier string
if v, ok := d.GetOk("cluster_identifier"); ok {
identifier = v.(string)
} else {
if v, ok := d.GetOk("cluster_identifier_prefix"); ok {
identifier = resource.PrefixedUniqueId(v.(string))
} else {
identifier = resource.PrefixedUniqueId("tf-")
}
d.Set("cluster_identifier", identifier)
}
if _, ok := d.GetOk("snapshot_identifier"); ok {
opts := rds.RestoreDBClusterFromSnapshotInput{
DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),

View File

@ -26,8 +26,17 @@ func resourceAwsRDSClusterInstance() *schema.Resource {
"identifier": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsId,
ConflictsWith: []string{"identifier_prefix"},
ValidateFunc: validateRdsIdentifier,
},
"identifier_prefix": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsIdentifierPrefix,
},
"db_subnet_group_name": {
@ -162,10 +171,14 @@ func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{
createOpts.DBParameterGroupName = aws.String(attr.(string))
}
if v := d.Get("identifier").(string); v != "" {
createOpts.DBInstanceIdentifier = aws.String(v)
if v, ok := d.GetOk("identifier"); ok {
createOpts.DBInstanceIdentifier = aws.String(v.(string))
} else {
createOpts.DBInstanceIdentifier = aws.String(resource.UniqueId())
if v, ok := d.GetOk("identifier_prefix"); ok {
createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId(v.(string)))
} else {
createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId("tf-"))
}
}
if attr, ok := d.GetOk("db_subnet_group_name"); ok {

View File

@ -46,6 +46,48 @@ func TestAccAWSRDSClusterInstance_basic(t *testing.T) {
})
}
func TestAccAWSRDSClusterInstance_namePrefix(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterInstanceConfig_namePrefix(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.test", &v),
testAccCheckAWSDBClusterInstanceAttributes(&v),
resource.TestMatchResourceAttr(
"aws_rds_cluster_instance.test", "identifier", regexp.MustCompile("^tf-cluster-instance-")),
),
},
},
})
}
func TestAccAWSRDSClusterInstance_generatedName(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterInstanceConfig_generatedName(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.test", &v),
testAccCheckAWSDBClusterInstanceAttributes(&v),
resource.TestMatchResourceAttr(
"aws_rds_cluster_instance.test", "identifier", regexp.MustCompile("^tf-")),
),
},
},
})
}
func TestAccAWSRDSClusterInstance_kmsKey(t *testing.T) {
var v rds.DBInstance
keyRegex := regexp.MustCompile("^arn:aws:kms:")
@ -256,6 +298,83 @@ resource "aws_db_parameter_group" "bar" {
`, n, n, n)
}
func testAccAWSClusterInstanceConfig_namePrefix(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster_instance" "test" {
identifier_prefix = "tf-cluster-instance-"
cluster_identifier = "${aws_rds_cluster.test.id}"
instance_class = "db.r3.large"
}
resource "aws_rds_cluster" "test" {
cluster_identifier = "tf-aurora-cluster-%d"
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n, n)
}
func testAccAWSClusterInstanceConfig_generatedName(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster_instance" "test" {
cluster_identifier = "${aws_rds_cluster.test.id}"
instance_class = "db.r3.large"
}
resource "aws_rds_cluster" "test" {
cluster_identifier = "tf-aurora-cluster-%d"
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n, n)
}
func testAccAWSClusterInstanceConfigKmsKey(n int) string {
return fmt.Sprintf(`

View File

@ -30,10 +30,19 @@ func resourceAwsRDSClusterParameterGroup() *schema.Resource {
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbParamGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateDbParamGroupNamePrefix,
},
"family": &schema.Schema{
Type: schema.TypeString,
Required: true,
@ -86,8 +95,17 @@ func resourceAwsRDSClusterParameterGroupCreate(d *schema.ResourceData, meta inte
rdsconn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := rds.CreateDBClusterParameterGroupInput{
DBClusterParameterGroupName: aws.String(d.Get("name").(string)),
DBClusterParameterGroupName: aws.String(groupName),
DBParameterGroupFamily: aws.String(d.Get("family").(string)),
Description: aws.String(d.Get("description").(string)),
Tags: tags,

View File

@ -3,6 +3,7 @@ package aws
import (
"errors"
"fmt"
"regexp"
"testing"
"time"
@ -90,6 +91,44 @@ func TestAccAWSDBClusterParameterGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBClusterParameterGroup_namePrefix(t *testing.T) {
var v rds.DBClusterParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBClusterParameterGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBClusterParameterGroupConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBClusterParameterGroupExists("aws_rds_cluster_parameter_group.test", &v),
resource.TestMatchResourceAttr(
"aws_rds_cluster_parameter_group.test", "name", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBClusterParameterGroup_generatedName(t *testing.T) {
var v rds.DBClusterParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBClusterParameterGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBClusterParameterGroupConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBClusterParameterGroupExists("aws_rds_cluster_parameter_group.test", &v),
),
},
},
})
}
func TestAccAWSDBClusterParameterGroup_disappears(t *testing.T) {
var v rds.DBClusterParameterGroup
@ -365,3 +404,15 @@ func testAccAWSDBClusterParameterGroupOnlyConfig(name string) string {
family = "aurora5.6"
}`, name)
}
const testAccAWSDBClusterParameterGroupConfig_namePrefix = `
resource "aws_rds_cluster_parameter_group" "test" {
name_prefix = "tf-test-"
family = "aurora5.6"
}
`
const testAccAWSDBClusterParameterGroupConfig_generatedName = `
resource "aws_rds_cluster_parameter_group" "test" {
family = "aurora5.6"
}
`

View File

@ -40,6 +40,46 @@ func TestAccAWSRDSCluster_basic(t *testing.T) {
})
}
func TestAccAWSRDSCluster_namePrefix(t *testing.T) {
var v rds.DBCluster
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterConfig_namePrefix(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterExists("aws_rds_cluster.test", &v),
resource.TestMatchResourceAttr(
"aws_rds_cluster.test", "cluster_identifier", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSRDSCluster_generatedName(t *testing.T) {
var v rds.DBCluster
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterConfig_generatedName(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterExists("aws_rds_cluster.test", &v),
resource.TestMatchResourceAttr(
"aws_rds_cluster.test", "cluster_identifier", regexp.MustCompile("^tf-")),
),
},
},
})
}
func TestAccAWSRDSCluster_takeFinalSnapshot(t *testing.T) {
var v rds.DBCluster
rInt := acctest.RandInt()
@ -322,6 +362,71 @@ resource "aws_rds_cluster" "default" {
}`, n)
}
func testAccAWSClusterConfig_namePrefix(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "test" {
cluster_identifier_prefix = "tf-test-"
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n)
}
func testAccAWSClusterConfig_generatedName(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "test" {
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n)
}
func testAccAWSClusterConfigWithFinalSnapshot(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "default" {

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform/helper/schema"
)
func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
func validateRdsIdentifier(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
@ -33,6 +33,23 @@ func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
return
}
func validateRdsIdentifierPrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
}
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
return
}
func validateElastiCacheClusterId(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if (len(value) < 1) || (len(value) > 20) {
@ -103,7 +120,27 @@ func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []er
"%q cannot be greater than 255 characters", k))
}
return
}
func validateDbParamGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
}
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 226 characters", k))
}
return
}
func validateStreamViewType(v interface{}, k string) (ws []string, errors []error) {
@ -1041,3 +1078,79 @@ func validateApiGatewayUsagePlanQuotaSettings(v map[string]interface{}) (errors
return
}
func validateDbSubnetGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 255 characters", k))
}
if regexp.MustCompile(`(?i)^default$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q is not allowed as %q", "Default", k))
}
return
}
func validateDbSubnetGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k))
}
if len(value) > 229 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 229 characters", k))
}
return
}
func validateDbOptionGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters and hyphens allowed in %q", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if regexp.MustCompile(`-$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot end with a hyphen", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 255 characters", k))
}
return
}
func validateDbOptionGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters and hyphens allowed in %q", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if len(value) > 229 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 229 characters", k))
}
return
}

View File

@ -1785,3 +1785,131 @@ func TestValidateElbNamePrefix(t *testing.T) {
}
}
}
func TestValidateDbSubnetGroupName(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "tEsting",
ErrCount: 1,
},
{
Value: "testing?",
ErrCount: 1,
},
{
Value: "default",
ErrCount: 1,
},
{
Value: randomString(300),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbSubnetGroupName(tc.Value, "aws_db_subnet_group")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Subnet Group name to trigger a validation error")
}
}
}
func TestValidateDbSubnetGroupNamePrefix(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "tEsting",
ErrCount: 1,
},
{
Value: "testing?",
ErrCount: 1,
},
{
Value: randomString(230),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbSubnetGroupNamePrefix(tc.Value, "aws_db_subnet_group")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Subnet Group name prefix to trigger a validation error")
}
}
}
func TestValidateDbOptionGroupName(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "testing123!",
ErrCount: 1,
},
{
Value: "1testing123",
ErrCount: 1,
},
{
Value: "testing--123",
ErrCount: 1,
},
{
Value: "testing123-",
ErrCount: 1,
},
{
Value: randomString(256),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbOptionGroupName(tc.Value, "aws_db_option_group_name")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Option Group Name to trigger a validation error")
}
}
}
func TestValidateDbOptionGroupNamePrefix(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "testing123!",
ErrCount: 1,
},
{
Value: "1testing123",
ErrCount: 1,
},
{
Value: "testing--123",
ErrCount: 1,
},
{
Value: randomString(230),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbOptionGroupNamePrefix(tc.Value, "aws_db_option_group_name")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Option Group name prefix to trigger a validation error")
}
}
}

View File

@ -55,7 +55,8 @@ The following arguments are supported:
* `allocated_storage` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The allocated storage in gigabytes.
* `engine` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The database engine to use.
* `engine_version` - (Optional) The engine version to use.
* `identifier` - (Optional) The name of the RDS instance, if omitted, Terraform will assign a random, unique name
* `identifier` - (Optional, Forces new resource) The name of the RDS instance, if omitted, Terraform will assign a random, unique identifier.
* `identifier_prefix` - (Optional, Forces new resource) Creates a unique identifier beginning with the specified prefix. Conflicts with `identifer`.
* `instance_class` - (Required) The instance type of the RDS instance.
* `storage_type` - (Optional) One of "standard" (magnetic), "gp2" (general
purpose SSD), or "io1" (provisioned IOPS SSD). The default is "io1" if

View File

@ -38,8 +38,9 @@ resource "aws_db_option_group" "bar" {
The following arguments are supported:
* `name` - (Required) The name of the Option group to be created.
* `option_group_description` - (Required) The description of the option group.
* `name` - (Optional, Forces new resource) The name of the option group. If omitted, Terraform will assign a random, unique name.
* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`.
* `option_group_description` - (Optional) The description of the option group. Defaults to "Managed by Terraform".
* `engine_name` - (Required) Specifies the name of the engine that this option group should be associated with..
* `major_engine_version` - (Required) Specifies the major version of the engine that this option group should be associated with.
* `option` - (Optional) A list of Options to apply.

View File

@ -31,7 +31,8 @@ resource "aws_db_parameter_group" "default" {
The following arguments are supported:
* `name` - (Required) The name of the DB parameter group.
* `name` - (Optional, Forces new resource) The name of the DB parameter group. If omitted, Terraform will assign a random, unique name.
* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`.
* `family` - (Required) The family of the DB parameter group.
* `description` - (Optional) The description of the DB parameter group. Defaults to "Managed by Terraform".
* `parameter` - (Optional) A list of DB parameters to apply.

View File

@ -27,7 +27,8 @@ resource "aws_db_subnet_group" "default" {
The following arguments are supported:
* `name` - (Required) The name of the DB subnet group.
* `name` - (Optional, Forces new resource) The name of the DB subnet group. If omitted, Terraform will assign a random, unique name.
* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`.
* `description` - (Optional) The description of the DB subnet group. Defaults to "Managed by Terraform".
* `subnet_ids` - (Required) A list of VPC subnet IDs.
* `tags` - (Optional) A mapping of tags to assign to the resource.

View File

@ -53,8 +53,8 @@ the [AWS official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/Co
The following arguments are supported:
* `cluster_identifier` - (Required) The Cluster Identifier. Must be a lower case
string.
* `cluster_identifier` - (Optional, Forces new resources) The cluster identifier. If omitted, Terraform will assign a random, unique identifier.
* `cluster_identifier_prefix` - (Optional, Forces new resource) Creates a unique cluster identifier beginning with the specified prefix. Conflicts with `cluster_identifer`.
* `database_name` - (Optional) The name for your database of up to 8 alpha-numeric
characters. If you do not provide a name, Amazon RDS will not create a
database in the DB cluster you are creating

View File

@ -47,8 +47,8 @@ the [AWS official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/Co
The following arguments are supported:
* `identifier` - (Optional) The Instance Identifier. Must be a lower case
string. If omitted, a unique identifier will be generated.
* `identifier` - (Optional, Forces new resource) The indentifier for the RDS instance, if omitted, Terraform will assign a random, unique identifier.
* `identifier_prefix` - (Optional, Forces new resource) Creates a unique identifier beginning with the specified prefix. Conflicts with `identifer`.
* `cluster_identifier` - (Required) The identifier of the [`aws_rds_cluster`](/docs/providers/aws/r/rds_cluster.html) in which to launch this instance.
* `instance_class` - (Required) The instance class to use. For details on CPU
and memory, see [Scaling Aurora DB Instances][4]. Aurora currently

View File

@ -32,9 +32,10 @@ resource "aws_rds_cluster_parameter_group" "default" {
The following arguments are supported:
* `name` - (Required) The name of the DB cluster parameter group.
* `name` - (Optional, Forces new resource) The name of the DB cluster parameter group. If omitted, Terraform will assign a random, unique name.
* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`.
* `family` - (Required) The family of the DB cluster parameter group.
* `description` - (Required) The description of the DB cluster parameter group.
* `description` - (Optional) The description of the DB cluster parameter group. Defaults to "Managed by Terraform".
* `parameter` - (Optional) A list of DB parameters to apply.
* `tags` - (Optional) A mapping of tags to assign to the resource.