github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/builtin/providers/aws/resource_aws_rds_cluster.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"regexp"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/aws/awserr"
    12  	"github.com/aws/aws-sdk-go/service/rds"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsRDSCluster() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsRDSClusterCreate,
    20  		Read:   resourceAwsRDSClusterRead,
    21  		Update: resourceAwsRDSClusterUpdate,
    22  		Delete: resourceAwsRDSClusterDelete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: resourceAwsRdsClusterImport,
    25  		},
    26  
    27  		Schema: map[string]*schema.Schema{
    28  
    29  			"availability_zones": {
    30  				Type:     schema.TypeSet,
    31  				Elem:     &schema.Schema{Type: schema.TypeString},
    32  				Optional: true,
    33  				ForceNew: true,
    34  				Computed: true,
    35  				Set:      schema.HashString,
    36  			},
    37  
    38  			"cluster_identifier": {
    39  				Type:          schema.TypeString,
    40  				Optional:      true,
    41  				Computed:      true,
    42  				ForceNew:      true,
    43  				ConflictsWith: []string{"cluster_identifier_prefix"},
    44  				ValidateFunc:  validateRdsIdentifier,
    45  			},
    46  			"cluster_identifier_prefix": {
    47  				Type:         schema.TypeString,
    48  				Optional:     true,
    49  				Computed:     true,
    50  				ForceNew:     true,
    51  				ValidateFunc: validateRdsIdentifierPrefix,
    52  			},
    53  
    54  			"cluster_members": {
    55  				Type:     schema.TypeSet,
    56  				Elem:     &schema.Schema{Type: schema.TypeString},
    57  				Optional: true,
    58  				Computed: true,
    59  				Set:      schema.HashString,
    60  			},
    61  
    62  			"database_name": {
    63  				Type:     schema.TypeString,
    64  				Optional: true,
    65  				Computed: true,
    66  				ForceNew: true,
    67  			},
    68  
    69  			"db_subnet_group_name": {
    70  				Type:     schema.TypeString,
    71  				Optional: true,
    72  				ForceNew: true,
    73  				Computed: true,
    74  			},
    75  
    76  			"db_cluster_parameter_group_name": {
    77  				Type:     schema.TypeString,
    78  				Optional: true,
    79  				Computed: true,
    80  			},
    81  
    82  			"endpoint": {
    83  				Type:     schema.TypeString,
    84  				Computed: true,
    85  			},
    86  
    87  			"reader_endpoint": {
    88  				Type:     schema.TypeString,
    89  				Computed: true,
    90  			},
    91  
    92  			"engine": {
    93  				Type:     schema.TypeString,
    94  				Computed: true,
    95  			},
    96  
    97  			"storage_encrypted": {
    98  				Type:     schema.TypeBool,
    99  				Optional: true,
   100  				Default:  false,
   101  				ForceNew: true,
   102  			},
   103  
   104  			"final_snapshot_identifier": {
   105  				Type:     schema.TypeString,
   106  				Optional: true,
   107  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   108  					value := v.(string)
   109  					if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   110  						es = append(es, fmt.Errorf(
   111  							"only alphanumeric characters and hyphens allowed in %q", k))
   112  					}
   113  					if regexp.MustCompile(`--`).MatchString(value) {
   114  						es = append(es, fmt.Errorf("%q cannot contain two consecutive hyphens", k))
   115  					}
   116  					if regexp.MustCompile(`-$`).MatchString(value) {
   117  						es = append(es, fmt.Errorf("%q cannot end in a hyphen", k))
   118  					}
   119  					return
   120  				},
   121  			},
   122  
   123  			"skip_final_snapshot": {
   124  				Type:     schema.TypeBool,
   125  				Optional: true,
   126  				Default:  false,
   127  			},
   128  
   129  			"master_username": {
   130  				Type:     schema.TypeString,
   131  				Computed: true,
   132  				Optional: true,
   133  				ForceNew: true,
   134  			},
   135  
   136  			"master_password": {
   137  				Type:      schema.TypeString,
   138  				Optional:  true,
   139  				Sensitive: true,
   140  			},
   141  
   142  			"snapshot_identifier": {
   143  				Type:     schema.TypeString,
   144  				Computed: false,
   145  				Optional: true,
   146  				Elem:     &schema.Schema{Type: schema.TypeString},
   147  			},
   148  
   149  			"port": {
   150  				Type:     schema.TypeInt,
   151  				Optional: true,
   152  				Computed: true,
   153  			},
   154  
   155  			// apply_immediately is used to determine when the update modifications
   156  			// take place.
   157  			// See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
   158  			"apply_immediately": {
   159  				Type:     schema.TypeBool,
   160  				Optional: true,
   161  				Computed: true,
   162  			},
   163  
   164  			"vpc_security_group_ids": {
   165  				Type:     schema.TypeSet,
   166  				Optional: true,
   167  				Computed: true,
   168  				Elem:     &schema.Schema{Type: schema.TypeString},
   169  				Set:      schema.HashString,
   170  			},
   171  
   172  			"preferred_backup_window": {
   173  				Type:         schema.TypeString,
   174  				Optional:     true,
   175  				Computed:     true,
   176  				ValidateFunc: validateOnceADayWindowFormat,
   177  			},
   178  
   179  			"preferred_maintenance_window": {
   180  				Type:     schema.TypeString,
   181  				Optional: true,
   182  				Computed: true,
   183  				StateFunc: func(val interface{}) string {
   184  					if val == nil {
   185  						return ""
   186  					}
   187  					return strings.ToLower(val.(string))
   188  				},
   189  				ValidateFunc: validateOnceAWeekWindowFormat,
   190  			},
   191  
   192  			"backup_retention_period": {
   193  				Type:     schema.TypeInt,
   194  				Optional: true,
   195  				Default:  1,
   196  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   197  					value := v.(int)
   198  					if value > 35 {
   199  						es = append(es, fmt.Errorf(
   200  							"backup retention period cannot be more than 35 days"))
   201  					}
   202  					return
   203  				},
   204  			},
   205  
   206  			"kms_key_id": {
   207  				Type:         schema.TypeString,
   208  				Optional:     true,
   209  				Computed:     true,
   210  				ForceNew:     true,
   211  				ValidateFunc: validateArn,
   212  			},
   213  
   214  			"replication_source_identifier": {
   215  				Type:     schema.TypeString,
   216  				Optional: true,
   217  			},
   218  
   219  			"iam_database_authentication_enabled": {
   220  				Type:     schema.TypeBool,
   221  				Optional: true,
   222  			},
   223  
   224  			"tags": tagsSchema(),
   225  		},
   226  	}
   227  }
   228  
   229  func resourceAwsRdsClusterImport(
   230  	d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
   231  	// Neither skip_final_snapshot nor final_snapshot_identifier can be fetched
   232  	// from any API call, so we need to default skip_final_snapshot to true so
   233  	// that final_snapshot_identifier is not required
   234  	d.Set("skip_final_snapshot", true)
   235  	return []*schema.ResourceData{d}, nil
   236  }
   237  
   238  func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error {
   239  	conn := meta.(*AWSClient).rdsconn
   240  	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
   241  
   242  	var identifier string
   243  	if v, ok := d.GetOk("cluster_identifier"); ok {
   244  		identifier = v.(string)
   245  	} else {
   246  		if v, ok := d.GetOk("cluster_identifier_prefix"); ok {
   247  			identifier = resource.PrefixedUniqueId(v.(string))
   248  		} else {
   249  			identifier = resource.PrefixedUniqueId("tf-")
   250  		}
   251  
   252  		d.Set("cluster_identifier", identifier)
   253  	}
   254  
   255  	if _, ok := d.GetOk("snapshot_identifier"); ok {
   256  		opts := rds.RestoreDBClusterFromSnapshotInput{
   257  			DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
   258  			SnapshotIdentifier:  aws.String(d.Get("snapshot_identifier").(string)),
   259  			Engine:              aws.String("aurora"),
   260  			Tags:                tags,
   261  		}
   262  
   263  		if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 {
   264  			opts.AvailabilityZones = expandStringList(attr.List())
   265  		}
   266  
   267  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   268  			opts.DBSubnetGroupName = aws.String(attr.(string))
   269  		}
   270  
   271  		if attr, ok := d.GetOk("database_name"); ok {
   272  			opts.DatabaseName = aws.String(attr.(string))
   273  		}
   274  
   275  		if attr, ok := d.GetOk("option_group_name"); ok {
   276  			opts.OptionGroupName = aws.String(attr.(string))
   277  		}
   278  
   279  		if attr, ok := d.GetOk("port"); ok {
   280  			opts.Port = aws.Int64(int64(attr.(int)))
   281  		}
   282  
   283  		var sgUpdate bool
   284  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   285  			sgUpdate = true
   286  			opts.VpcSecurityGroupIds = expandStringList(attr.List())
   287  		}
   288  
   289  		log.Printf("[DEBUG] RDS Cluster restore from snapshot configuration: %s", opts)
   290  		_, err := conn.RestoreDBClusterFromSnapshot(&opts)
   291  		if err != nil {
   292  			return fmt.Errorf("Error creating RDS Cluster: %s", err)
   293  		}
   294  
   295  		if sgUpdate {
   296  			log.Printf("[INFO] RDS Cluster is restoring from snapshot with default security, but custom security should be set, will now update after snapshot is restored!")
   297  
   298  			d.SetId(d.Get("cluster_identifier").(string))
   299  
   300  			log.Printf("[INFO] RDS Cluster Instance ID: %s", d.Id())
   301  
   302  			log.Println("[INFO] Waiting for RDS Cluster to be available")
   303  
   304  			stateConf := &resource.StateChangeConf{
   305  				Pending:    []string{"creating", "backing-up", "modifying"},
   306  				Target:     []string{"available"},
   307  				Refresh:    resourceAwsRDSClusterStateRefreshFunc(d, meta),
   308  				Timeout:    120 * time.Minute,
   309  				MinTimeout: 3 * time.Second,
   310  				Delay:      30 * time.Second, // Wait 30 secs before starting
   311  			}
   312  
   313  			// Wait, catching any errors
   314  			_, err := stateConf.WaitForState()
   315  			if err != nil {
   316  				return err
   317  			}
   318  
   319  			err = resourceAwsRDSClusterInstanceUpdate(d, meta)
   320  			if err != nil {
   321  				return err
   322  			}
   323  		}
   324  	} else if _, ok := d.GetOk("replication_source_identifier"); ok {
   325  		createOpts := &rds.CreateDBClusterInput{
   326  			DBClusterIdentifier:         aws.String(d.Get("cluster_identifier").(string)),
   327  			Engine:                      aws.String("aurora"),
   328  			StorageEncrypted:            aws.Bool(d.Get("storage_encrypted").(bool)),
   329  			ReplicationSourceIdentifier: aws.String(d.Get("replication_source_identifier").(string)),
   330  			Tags: tags,
   331  		}
   332  
   333  		if attr, ok := d.GetOk("port"); ok {
   334  			createOpts.Port = aws.Int64(int64(attr.(int)))
   335  		}
   336  
   337  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   338  			createOpts.DBSubnetGroupName = aws.String(attr.(string))
   339  		}
   340  
   341  		if attr, ok := d.GetOk("db_cluster_parameter_group_name"); ok {
   342  			createOpts.DBClusterParameterGroupName = aws.String(attr.(string))
   343  		}
   344  
   345  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   346  			createOpts.VpcSecurityGroupIds = expandStringList(attr.List())
   347  		}
   348  
   349  		if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 {
   350  			createOpts.AvailabilityZones = expandStringList(attr.List())
   351  		}
   352  
   353  		if v, ok := d.GetOk("backup_retention_period"); ok {
   354  			createOpts.BackupRetentionPeriod = aws.Int64(int64(v.(int)))
   355  		}
   356  
   357  		if v, ok := d.GetOk("preferred_backup_window"); ok {
   358  			createOpts.PreferredBackupWindow = aws.String(v.(string))
   359  		}
   360  
   361  		if v, ok := d.GetOk("preferred_maintenance_window"); ok {
   362  			createOpts.PreferredMaintenanceWindow = aws.String(v.(string))
   363  		}
   364  
   365  		if attr, ok := d.GetOk("kms_key_id"); ok {
   366  			createOpts.KmsKeyId = aws.String(attr.(string))
   367  		}
   368  
   369  		log.Printf("[DEBUG] Create RDS Cluster as read replica: %s", createOpts)
   370  		resp, err := conn.CreateDBCluster(createOpts)
   371  		if err != nil {
   372  			log.Printf("[ERROR] Error creating RDS Cluster: %s", err)
   373  			return err
   374  		}
   375  
   376  		log.Printf("[DEBUG]: RDS Cluster create response: %s", resp)
   377  
   378  	} else {
   379  		if _, ok := d.GetOk("master_password"); !ok {
   380  			return fmt.Errorf(`provider.aws: aws_rds_cluster: %s: "master_password": required field is not set`, d.Get("database_name").(string))
   381  		}
   382  
   383  		if _, ok := d.GetOk("master_username"); !ok {
   384  			return fmt.Errorf(`provider.aws: aws_rds_cluster: %s: "master_username": required field is not set`, d.Get("database_name").(string))
   385  		}
   386  
   387  		createOpts := &rds.CreateDBClusterInput{
   388  			DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
   389  			Engine:              aws.String("aurora"),
   390  			MasterUserPassword:  aws.String(d.Get("master_password").(string)),
   391  			MasterUsername:      aws.String(d.Get("master_username").(string)),
   392  			StorageEncrypted:    aws.Bool(d.Get("storage_encrypted").(bool)),
   393  			Tags:                tags,
   394  		}
   395  
   396  		if v := d.Get("database_name"); v.(string) != "" {
   397  			createOpts.DatabaseName = aws.String(v.(string))
   398  		}
   399  
   400  		if attr, ok := d.GetOk("port"); ok {
   401  			createOpts.Port = aws.Int64(int64(attr.(int)))
   402  		}
   403  
   404  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   405  			createOpts.DBSubnetGroupName = aws.String(attr.(string))
   406  		}
   407  
   408  		if attr, ok := d.GetOk("db_cluster_parameter_group_name"); ok {
   409  			createOpts.DBClusterParameterGroupName = aws.String(attr.(string))
   410  		}
   411  
   412  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   413  			createOpts.VpcSecurityGroupIds = expandStringList(attr.List())
   414  		}
   415  
   416  		if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 {
   417  			createOpts.AvailabilityZones = expandStringList(attr.List())
   418  		}
   419  
   420  		if v, ok := d.GetOk("backup_retention_period"); ok {
   421  			createOpts.BackupRetentionPeriod = aws.Int64(int64(v.(int)))
   422  		}
   423  
   424  		if v, ok := d.GetOk("preferred_backup_window"); ok {
   425  			createOpts.PreferredBackupWindow = aws.String(v.(string))
   426  		}
   427  
   428  		if v, ok := d.GetOk("preferred_maintenance_window"); ok {
   429  			createOpts.PreferredMaintenanceWindow = aws.String(v.(string))
   430  		}
   431  
   432  		if attr, ok := d.GetOk("kms_key_id"); ok {
   433  			createOpts.KmsKeyId = aws.String(attr.(string))
   434  		}
   435  
   436  		if attr, ok := d.GetOk("iam_database_authentication_enabled"); ok {
   437  			createOpts.EnableIAMDatabaseAuthentication = aws.Bool(attr.(bool))
   438  		}
   439  
   440  		log.Printf("[DEBUG] RDS Cluster create options: %s", createOpts)
   441  		resp, err := conn.CreateDBCluster(createOpts)
   442  		if err != nil {
   443  			log.Printf("[ERROR] Error creating RDS Cluster: %s", err)
   444  			return err
   445  		}
   446  
   447  		log.Printf("[DEBUG]: RDS Cluster create response: %s", resp)
   448  	}
   449  
   450  	d.SetId(d.Get("cluster_identifier").(string))
   451  
   452  	log.Printf("[INFO] RDS Cluster ID: %s", d.Id())
   453  
   454  	log.Println(
   455  		"[INFO] Waiting for RDS Cluster to be available")
   456  
   457  	stateConf := &resource.StateChangeConf{
   458  		Pending:    []string{"creating", "backing-up", "modifying"},
   459  		Target:     []string{"available"},
   460  		Refresh:    resourceAwsRDSClusterStateRefreshFunc(d, meta),
   461  		Timeout:    120 * time.Minute,
   462  		MinTimeout: 3 * time.Second,
   463  	}
   464  
   465  	// Wait, catching any errors
   466  	_, err := stateConf.WaitForState()
   467  	if err != nil {
   468  		return fmt.Errorf("[WARN] Error waiting for RDS Cluster state to be \"available\": %s", err)
   469  	}
   470  
   471  	return resourceAwsRDSClusterRead(d, meta)
   472  }
   473  
   474  func resourceAwsRDSClusterRead(d *schema.ResourceData, meta interface{}) error {
   475  	conn := meta.(*AWSClient).rdsconn
   476  
   477  	resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
   478  		DBClusterIdentifier: aws.String(d.Id()),
   479  	})
   480  
   481  	if err != nil {
   482  		if awsErr, ok := err.(awserr.Error); ok {
   483  			if "DBClusterNotFoundFault" == awsErr.Code() {
   484  				d.SetId("")
   485  				log.Printf("[DEBUG] RDS Cluster (%s) not found", d.Id())
   486  				return nil
   487  			}
   488  		}
   489  		log.Printf("[DEBUG] Error describing RDS Cluster (%s)", d.Id())
   490  		return err
   491  	}
   492  
   493  	var dbc *rds.DBCluster
   494  	for _, c := range resp.DBClusters {
   495  		if *c.DBClusterIdentifier == d.Id() {
   496  			dbc = c
   497  		}
   498  	}
   499  
   500  	if dbc == nil {
   501  		log.Printf("[WARN] RDS Cluster (%s) not found", d.Id())
   502  		d.SetId("")
   503  		return nil
   504  	}
   505  
   506  	if err := d.Set("availability_zones", aws.StringValueSlice(dbc.AvailabilityZones)); err != nil {
   507  		return fmt.Errorf("[DEBUG] Error saving AvailabilityZones to state for RDS Cluster (%s): %s", d.Id(), err)
   508  	}
   509  
   510  	// Only set the DatabaseName if it is not nil. There is a known API bug where
   511  	// RDS accepts a DatabaseName but does not return it, causing a perpetual
   512  	// diff.
   513  	//	See https://github.com/hashicorp/terraform/issues/4671 for backstory
   514  	if dbc.DatabaseName != nil {
   515  		d.Set("database_name", dbc.DatabaseName)
   516  	}
   517  
   518  	d.Set("cluster_identifier", dbc.DBClusterIdentifier)
   519  	d.Set("db_subnet_group_name", dbc.DBSubnetGroup)
   520  	d.Set("db_cluster_parameter_group_name", dbc.DBClusterParameterGroup)
   521  	d.Set("endpoint", dbc.Endpoint)
   522  	d.Set("engine", dbc.Engine)
   523  	d.Set("master_username", dbc.MasterUsername)
   524  	d.Set("port", dbc.Port)
   525  	d.Set("storage_encrypted", dbc.StorageEncrypted)
   526  	d.Set("backup_retention_period", dbc.BackupRetentionPeriod)
   527  	d.Set("preferred_backup_window", dbc.PreferredBackupWindow)
   528  	d.Set("preferred_maintenance_window", dbc.PreferredMaintenanceWindow)
   529  	d.Set("kms_key_id", dbc.KmsKeyId)
   530  	d.Set("reader_endpoint", dbc.ReaderEndpoint)
   531  	d.Set("replication_source_identifier", dbc.ReplicationSourceIdentifier)
   532  	d.Set("iam_database_authentication_enabled", dbc.IAMDatabaseAuthenticationEnabled)
   533  
   534  	var vpcg []string
   535  	for _, g := range dbc.VpcSecurityGroups {
   536  		vpcg = append(vpcg, *g.VpcSecurityGroupId)
   537  	}
   538  	if err := d.Set("vpc_security_group_ids", vpcg); err != nil {
   539  		return fmt.Errorf("[DEBUG] Error saving VPC Security Group IDs to state for RDS Cluster (%s): %s", d.Id(), err)
   540  	}
   541  
   542  	var cm []string
   543  	for _, m := range dbc.DBClusterMembers {
   544  		cm = append(cm, *m.DBInstanceIdentifier)
   545  	}
   546  	if err := d.Set("cluster_members", cm); err != nil {
   547  		return fmt.Errorf("[DEBUG] Error saving RDS Cluster Members to state for RDS Cluster (%s): %s", d.Id(), err)
   548  	}
   549  
   550  	// Fetch and save tags
   551  	arn, err := buildRDSClusterARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region)
   552  	if err != nil {
   553  		log.Printf("[DEBUG] Error building ARN for RDS Cluster (%s), not setting Tags", *dbc.DBClusterIdentifier)
   554  	} else {
   555  		if err := saveTagsRDS(conn, d, arn); err != nil {
   556  			log.Printf("[WARN] Failed to save tags for RDS Cluster (%s): %s", *dbc.DBClusterIdentifier, err)
   557  		}
   558  	}
   559  
   560  	return nil
   561  }
   562  
   563  func resourceAwsRDSClusterUpdate(d *schema.ResourceData, meta interface{}) error {
   564  	conn := meta.(*AWSClient).rdsconn
   565  	requestUpdate := false
   566  
   567  	req := &rds.ModifyDBClusterInput{
   568  		ApplyImmediately:    aws.Bool(d.Get("apply_immediately").(bool)),
   569  		DBClusterIdentifier: aws.String(d.Id()),
   570  	}
   571  
   572  	if d.HasChange("master_password") {
   573  		req.MasterUserPassword = aws.String(d.Get("master_password").(string))
   574  		requestUpdate = true
   575  	}
   576  
   577  	if d.HasChange("vpc_security_group_ids") {
   578  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   579  			req.VpcSecurityGroupIds = expandStringList(attr.List())
   580  		} else {
   581  			req.VpcSecurityGroupIds = []*string{}
   582  		}
   583  		requestUpdate = true
   584  	}
   585  
   586  	if d.HasChange("preferred_backup_window") {
   587  		req.PreferredBackupWindow = aws.String(d.Get("preferred_backup_window").(string))
   588  		requestUpdate = true
   589  	}
   590  
   591  	if d.HasChange("preferred_maintenance_window") {
   592  		req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string))
   593  		requestUpdate = true
   594  	}
   595  
   596  	if d.HasChange("backup_retention_period") {
   597  		req.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_period").(int)))
   598  		requestUpdate = true
   599  	}
   600  
   601  	if d.HasChange("db_cluster_parameter_group_name") {
   602  		d.SetPartial("db_cluster_parameter_group_name")
   603  		req.DBClusterParameterGroupName = aws.String(d.Get("db_cluster_parameter_group_name").(string))
   604  		requestUpdate = true
   605  	}
   606  
   607  	if d.HasChange("iam_database_authentication_enabled") {
   608  		req.EnableIAMDatabaseAuthentication = aws.Bool(d.Get("iam_database_authentication_enabled").(bool))
   609  		requestUpdate = true
   610  	}
   611  
   612  	if requestUpdate {
   613  		_, err := conn.ModifyDBCluster(req)
   614  		if err != nil {
   615  			return fmt.Errorf("[WARN] Error modifying RDS Cluster (%s): %s", d.Id(), err)
   616  		}
   617  	}
   618  
   619  	if arn, err := buildRDSClusterARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region); err == nil {
   620  		if err := setTagsRDS(conn, d, arn); err != nil {
   621  			return err
   622  		} else {
   623  			d.SetPartial("tags")
   624  		}
   625  	}
   626  
   627  	return resourceAwsRDSClusterRead(d, meta)
   628  }
   629  
   630  func resourceAwsRDSClusterDelete(d *schema.ResourceData, meta interface{}) error {
   631  	conn := meta.(*AWSClient).rdsconn
   632  	log.Printf("[DEBUG] Destroying RDS Cluster (%s)", d.Id())
   633  
   634  	deleteOpts := rds.DeleteDBClusterInput{
   635  		DBClusterIdentifier: aws.String(d.Id()),
   636  	}
   637  
   638  	skipFinalSnapshot := d.Get("skip_final_snapshot").(bool)
   639  	deleteOpts.SkipFinalSnapshot = aws.Bool(skipFinalSnapshot)
   640  
   641  	if skipFinalSnapshot == false {
   642  		if name, present := d.GetOk("final_snapshot_identifier"); present {
   643  			deleteOpts.FinalDBSnapshotIdentifier = aws.String(name.(string))
   644  		} else {
   645  			return fmt.Errorf("RDS Cluster FinalSnapshotIdentifier is required when a final snapshot is required")
   646  		}
   647  	}
   648  
   649  	log.Printf("[DEBUG] RDS Cluster delete options: %s", deleteOpts)
   650  	_, err := conn.DeleteDBCluster(&deleteOpts)
   651  	if err != nil {
   652  		if awsErr, ok := err.(awserr.Error); ok {
   653  			if "InvalidDBClusterStateFault" == awsErr.Code() {
   654  				return fmt.Errorf("RDS Cluster cannot be deleted: %s", awsErr.Message())
   655  			}
   656  		}
   657  	}
   658  
   659  	stateConf := &resource.StateChangeConf{
   660  		Pending:    []string{"available", "deleting", "backing-up", "modifying"},
   661  		Target:     []string{"destroyed"},
   662  		Refresh:    resourceAwsRDSClusterStateRefreshFunc(d, meta),
   663  		Timeout:    15 * time.Minute,
   664  		MinTimeout: 3 * time.Second,
   665  	}
   666  
   667  	// Wait, catching any errors
   668  	_, err = stateConf.WaitForState()
   669  	if err != nil {
   670  		return fmt.Errorf("[WARN] Error deleting RDS Cluster (%s): %s", d.Id(), err)
   671  	}
   672  
   673  	return nil
   674  }
   675  
   676  func resourceAwsRDSClusterStateRefreshFunc(
   677  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   678  	return func() (interface{}, string, error) {
   679  		conn := meta.(*AWSClient).rdsconn
   680  
   681  		resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
   682  			DBClusterIdentifier: aws.String(d.Id()),
   683  		})
   684  
   685  		if err != nil {
   686  			if awsErr, ok := err.(awserr.Error); ok {
   687  				if "DBClusterNotFoundFault" == awsErr.Code() {
   688  					return 42, "destroyed", nil
   689  				}
   690  			}
   691  			log.Printf("[WARN] Error on retrieving DB Cluster (%s) when waiting: %s", d.Id(), err)
   692  			return nil, "", err
   693  		}
   694  
   695  		var dbc *rds.DBCluster
   696  
   697  		for _, c := range resp.DBClusters {
   698  			if *c.DBClusterIdentifier == d.Id() {
   699  				dbc = c
   700  			}
   701  		}
   702  
   703  		if dbc == nil {
   704  			return 42, "destroyed", nil
   705  		}
   706  
   707  		if dbc.Status != nil {
   708  			log.Printf("[DEBUG] DB Cluster status (%s): %s", d.Id(), *dbc.Status)
   709  		}
   710  
   711  		return dbc, *dbc.Status, nil
   712  	}
   713  }
   714  
   715  func buildRDSClusterARN(identifier, partition, accountid, region string) (string, error) {
   716  	if partition == "" {
   717  		return "", fmt.Errorf("Unable to construct RDS Cluster ARN because of missing AWS partition")
   718  	}
   719  	if accountid == "" {
   720  		return "", fmt.Errorf("Unable to construct RDS Cluster ARN because of missing AWS Account ID")
   721  	}
   722  
   723  	arn := fmt.Sprintf("arn:%s:rds:%s:%s:cluster:%s", partition, region, accountid, identifier)
   724  	return arn, nil
   725  
   726  }