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