github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/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  			"cluster_resource_id": {
   231  				Type:     schema.TypeString,
   232  				Computed: true,
   233  			},
   234  
   235  			"tags": tagsSchema(),
   236  		},
   237  	}
   238  }
   239  
   240  func resourceAwsRdsClusterImport(
   241  	d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
   242  	// Neither skip_final_snapshot nor final_snapshot_identifier can be fetched
   243  	// from any API call, so we need to default skip_final_snapshot to true so
   244  	// that final_snapshot_identifier is not required
   245  	d.Set("skip_final_snapshot", true)
   246  	return []*schema.ResourceData{d}, nil
   247  }
   248  
   249  func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error {
   250  	conn := meta.(*AWSClient).rdsconn
   251  	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
   252  
   253  	var identifier string
   254  	if v, ok := d.GetOk("cluster_identifier"); ok {
   255  		identifier = v.(string)
   256  	} else {
   257  		if v, ok := d.GetOk("cluster_identifier_prefix"); ok {
   258  			identifier = resource.PrefixedUniqueId(v.(string))
   259  		} else {
   260  			identifier = resource.PrefixedUniqueId("tf-")
   261  		}
   262  
   263  		d.Set("cluster_identifier", identifier)
   264  	}
   265  
   266  	if _, ok := d.GetOk("snapshot_identifier"); ok {
   267  		opts := rds.RestoreDBClusterFromSnapshotInput{
   268  			DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
   269  			SnapshotIdentifier:  aws.String(d.Get("snapshot_identifier").(string)),
   270  			Engine:              aws.String("aurora"),
   271  			Tags:                tags,
   272  		}
   273  
   274  		if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 {
   275  			opts.AvailabilityZones = expandStringList(attr.List())
   276  		}
   277  
   278  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   279  			opts.DBSubnetGroupName = aws.String(attr.(string))
   280  		}
   281  
   282  		if attr, ok := d.GetOk("database_name"); ok {
   283  			opts.DatabaseName = aws.String(attr.(string))
   284  		}
   285  
   286  		if attr, ok := d.GetOk("option_group_name"); ok {
   287  			opts.OptionGroupName = aws.String(attr.(string))
   288  		}
   289  
   290  		if attr, ok := d.GetOk("port"); ok {
   291  			opts.Port = aws.Int64(int64(attr.(int)))
   292  		}
   293  
   294  		var sgUpdate bool
   295  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   296  			sgUpdate = true
   297  			opts.VpcSecurityGroupIds = expandStringList(attr.List())
   298  		}
   299  
   300  		log.Printf("[DEBUG] RDS Cluster restore from snapshot configuration: %s", opts)
   301  		_, err := conn.RestoreDBClusterFromSnapshot(&opts)
   302  		if err != nil {
   303  			return fmt.Errorf("Error creating RDS Cluster: %s", err)
   304  		}
   305  
   306  		if sgUpdate {
   307  			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!")
   308  
   309  			d.SetId(d.Get("cluster_identifier").(string))
   310  
   311  			log.Printf("[INFO] RDS Cluster Instance ID: %s", d.Id())
   312  
   313  			log.Println("[INFO] Waiting for RDS Cluster to be available")
   314  
   315  			stateConf := &resource.StateChangeConf{
   316  				Pending:    []string{"creating", "backing-up", "modifying", "preparing-data-migration", "migrating"},
   317  				Target:     []string{"available"},
   318  				Refresh:    resourceAwsRDSClusterStateRefreshFunc(d, meta),
   319  				Timeout:    d.Timeout(schema.TimeoutCreate),
   320  				MinTimeout: 10 * time.Second,
   321  				Delay:      30 * time.Second,
   322  			}
   323  
   324  			// Wait, catching any errors
   325  			_, err := stateConf.WaitForState()
   326  			if err != nil {
   327  				return err
   328  			}
   329  
   330  			err = resourceAwsRDSClusterInstanceUpdate(d, meta)
   331  			if err != nil {
   332  				return err
   333  			}
   334  		}
   335  	} else if _, ok := d.GetOk("replication_source_identifier"); ok {
   336  		createOpts := &rds.CreateDBClusterInput{
   337  			DBClusterIdentifier:         aws.String(d.Get("cluster_identifier").(string)),
   338  			Engine:                      aws.String("aurora"),
   339  			StorageEncrypted:            aws.Bool(d.Get("storage_encrypted").(bool)),
   340  			ReplicationSourceIdentifier: aws.String(d.Get("replication_source_identifier").(string)),
   341  			Tags: tags,
   342  		}
   343  
   344  		if attr, ok := d.GetOk("port"); ok {
   345  			createOpts.Port = aws.Int64(int64(attr.(int)))
   346  		}
   347  
   348  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   349  			createOpts.DBSubnetGroupName = aws.String(attr.(string))
   350  		}
   351  
   352  		if attr, ok := d.GetOk("db_cluster_parameter_group_name"); ok {
   353  			createOpts.DBClusterParameterGroupName = aws.String(attr.(string))
   354  		}
   355  
   356  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   357  			createOpts.VpcSecurityGroupIds = expandStringList(attr.List())
   358  		}
   359  
   360  		if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 {
   361  			createOpts.AvailabilityZones = expandStringList(attr.List())
   362  		}
   363  
   364  		if v, ok := d.GetOk("backup_retention_period"); ok {
   365  			createOpts.BackupRetentionPeriod = aws.Int64(int64(v.(int)))
   366  		}
   367  
   368  		if v, ok := d.GetOk("preferred_backup_window"); ok {
   369  			createOpts.PreferredBackupWindow = aws.String(v.(string))
   370  		}
   371  
   372  		if v, ok := d.GetOk("preferred_maintenance_window"); ok {
   373  			createOpts.PreferredMaintenanceWindow = aws.String(v.(string))
   374  		}
   375  
   376  		if attr, ok := d.GetOk("kms_key_id"); ok {
   377  			createOpts.KmsKeyId = aws.String(attr.(string))
   378  		}
   379  
   380  		log.Printf("[DEBUG] Create RDS Cluster as read replica: %s", createOpts)
   381  		resp, err := conn.CreateDBCluster(createOpts)
   382  		if err != nil {
   383  			log.Printf("[ERROR] Error creating RDS Cluster: %s", err)
   384  			return err
   385  		}
   386  
   387  		log.Printf("[DEBUG]: RDS Cluster create response: %s", resp)
   388  
   389  	} else {
   390  		if _, ok := d.GetOk("master_password"); !ok {
   391  			return fmt.Errorf(`provider.aws: aws_rds_cluster: %s: "master_password": required field is not set`, d.Get("database_name").(string))
   392  		}
   393  
   394  		if _, ok := d.GetOk("master_username"); !ok {
   395  			return fmt.Errorf(`provider.aws: aws_rds_cluster: %s: "master_username": required field is not set`, d.Get("database_name").(string))
   396  		}
   397  
   398  		createOpts := &rds.CreateDBClusterInput{
   399  			DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
   400  			Engine:              aws.String("aurora"),
   401  			MasterUserPassword:  aws.String(d.Get("master_password").(string)),
   402  			MasterUsername:      aws.String(d.Get("master_username").(string)),
   403  			StorageEncrypted:    aws.Bool(d.Get("storage_encrypted").(bool)),
   404  			Tags:                tags,
   405  		}
   406  
   407  		if v := d.Get("database_name"); v.(string) != "" {
   408  			createOpts.DatabaseName = aws.String(v.(string))
   409  		}
   410  
   411  		if attr, ok := d.GetOk("port"); ok {
   412  			createOpts.Port = aws.Int64(int64(attr.(int)))
   413  		}
   414  
   415  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   416  			createOpts.DBSubnetGroupName = aws.String(attr.(string))
   417  		}
   418  
   419  		if attr, ok := d.GetOk("db_cluster_parameter_group_name"); ok {
   420  			createOpts.DBClusterParameterGroupName = aws.String(attr.(string))
   421  		}
   422  
   423  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   424  			createOpts.VpcSecurityGroupIds = expandStringList(attr.List())
   425  		}
   426  
   427  		if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 {
   428  			createOpts.AvailabilityZones = expandStringList(attr.List())
   429  		}
   430  
   431  		if v, ok := d.GetOk("backup_retention_period"); ok {
   432  			createOpts.BackupRetentionPeriod = aws.Int64(int64(v.(int)))
   433  		}
   434  
   435  		if v, ok := d.GetOk("preferred_backup_window"); ok {
   436  			createOpts.PreferredBackupWindow = aws.String(v.(string))
   437  		}
   438  
   439  		if v, ok := d.GetOk("preferred_maintenance_window"); ok {
   440  			createOpts.PreferredMaintenanceWindow = aws.String(v.(string))
   441  		}
   442  
   443  		if attr, ok := d.GetOk("kms_key_id"); ok {
   444  			createOpts.KmsKeyId = aws.String(attr.(string))
   445  		}
   446  
   447  		if attr, ok := d.GetOk("iam_database_authentication_enabled"); ok {
   448  			createOpts.EnableIAMDatabaseAuthentication = aws.Bool(attr.(bool))
   449  		}
   450  
   451  		log.Printf("[DEBUG] RDS Cluster create options: %s", createOpts)
   452  		resp, err := conn.CreateDBCluster(createOpts)
   453  		if err != nil {
   454  			log.Printf("[ERROR] Error creating RDS Cluster: %s", err)
   455  			return err
   456  		}
   457  
   458  		log.Printf("[DEBUG]: RDS Cluster create response: %s", resp)
   459  	}
   460  
   461  	d.SetId(d.Get("cluster_identifier").(string))
   462  
   463  	log.Printf("[INFO] RDS Cluster ID: %s", d.Id())
   464  
   465  	log.Println(
   466  		"[INFO] Waiting for RDS Cluster to be available")
   467  
   468  	stateConf := &resource.StateChangeConf{
   469  		Pending:    []string{"creating", "backing-up", "modifying"},
   470  		Target:     []string{"available"},
   471  		Refresh:    resourceAwsRDSClusterStateRefreshFunc(d, meta),
   472  		Timeout:    d.Timeout(schema.TimeoutCreate),
   473  		MinTimeout: 10 * time.Second,
   474  		Delay:      30 * time.Second,
   475  	}
   476  
   477  	// Wait, catching any errors
   478  	_, err := stateConf.WaitForState()
   479  	if err != nil {
   480  		return fmt.Errorf("[WARN] Error waiting for RDS Cluster state to be \"available\": %s", err)
   481  	}
   482  
   483  	return resourceAwsRDSClusterRead(d, meta)
   484  }
   485  
   486  func resourceAwsRDSClusterRead(d *schema.ResourceData, meta interface{}) error {
   487  	conn := meta.(*AWSClient).rdsconn
   488  
   489  	resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
   490  		DBClusterIdentifier: aws.String(d.Id()),
   491  	})
   492  
   493  	if err != nil {
   494  		if awsErr, ok := err.(awserr.Error); ok {
   495  			if "DBClusterNotFoundFault" == awsErr.Code() {
   496  				d.SetId("")
   497  				log.Printf("[DEBUG] RDS Cluster (%s) not found", d.Id())
   498  				return nil
   499  			}
   500  		}
   501  		log.Printf("[DEBUG] Error describing RDS Cluster (%s)", d.Id())
   502  		return err
   503  	}
   504  
   505  	var dbc *rds.DBCluster
   506  	for _, c := range resp.DBClusters {
   507  		if *c.DBClusterIdentifier == d.Id() {
   508  			dbc = c
   509  		}
   510  	}
   511  
   512  	if dbc == nil {
   513  		log.Printf("[WARN] RDS Cluster (%s) not found", d.Id())
   514  		d.SetId("")
   515  		return nil
   516  	}
   517  
   518  	if err := d.Set("availability_zones", aws.StringValueSlice(dbc.AvailabilityZones)); err != nil {
   519  		return fmt.Errorf("[DEBUG] Error saving AvailabilityZones to state for RDS Cluster (%s): %s", d.Id(), err)
   520  	}
   521  
   522  	// Only set the DatabaseName if it is not nil. There is a known API bug where
   523  	// RDS accepts a DatabaseName but does not return it, causing a perpetual
   524  	// diff.
   525  	//	See https://github.com/hashicorp/terraform/issues/4671 for backstory
   526  	if dbc.DatabaseName != nil {
   527  		d.Set("database_name", dbc.DatabaseName)
   528  	}
   529  
   530  	d.Set("cluster_identifier", dbc.DBClusterIdentifier)
   531  	d.Set("cluster_resource_id", dbc.DbClusterResourceId)
   532  	d.Set("db_subnet_group_name", dbc.DBSubnetGroup)
   533  	d.Set("db_cluster_parameter_group_name", dbc.DBClusterParameterGroup)
   534  	d.Set("endpoint", dbc.Endpoint)
   535  	d.Set("engine", dbc.Engine)
   536  	d.Set("master_username", dbc.MasterUsername)
   537  	d.Set("port", dbc.Port)
   538  	d.Set("storage_encrypted", dbc.StorageEncrypted)
   539  	d.Set("backup_retention_period", dbc.BackupRetentionPeriod)
   540  	d.Set("preferred_backup_window", dbc.PreferredBackupWindow)
   541  	d.Set("preferred_maintenance_window", dbc.PreferredMaintenanceWindow)
   542  	d.Set("kms_key_id", dbc.KmsKeyId)
   543  	d.Set("reader_endpoint", dbc.ReaderEndpoint)
   544  	d.Set("replication_source_identifier", dbc.ReplicationSourceIdentifier)
   545  	d.Set("iam_database_authentication_enabled", dbc.IAMDatabaseAuthenticationEnabled)
   546  
   547  	var vpcg []string
   548  	for _, g := range dbc.VpcSecurityGroups {
   549  		vpcg = append(vpcg, *g.VpcSecurityGroupId)
   550  	}
   551  	if err := d.Set("vpc_security_group_ids", vpcg); err != nil {
   552  		return fmt.Errorf("[DEBUG] Error saving VPC Security Group IDs to state for RDS Cluster (%s): %s", d.Id(), err)
   553  	}
   554  
   555  	var cm []string
   556  	for _, m := range dbc.DBClusterMembers {
   557  		cm = append(cm, *m.DBInstanceIdentifier)
   558  	}
   559  	if err := d.Set("cluster_members", cm); err != nil {
   560  		return fmt.Errorf("[DEBUG] Error saving RDS Cluster Members to state for RDS Cluster (%s): %s", d.Id(), err)
   561  	}
   562  
   563  	// Fetch and save tags
   564  	arn, err := buildRDSClusterARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region)
   565  	if err != nil {
   566  		log.Printf("[DEBUG] Error building ARN for RDS Cluster (%s), not setting Tags", *dbc.DBClusterIdentifier)
   567  	} else {
   568  		if err := saveTagsRDS(conn, d, arn); err != nil {
   569  			log.Printf("[WARN] Failed to save tags for RDS Cluster (%s): %s", *dbc.DBClusterIdentifier, err)
   570  		}
   571  	}
   572  
   573  	return nil
   574  }
   575  
   576  func resourceAwsRDSClusterUpdate(d *schema.ResourceData, meta interface{}) error {
   577  	conn := meta.(*AWSClient).rdsconn
   578  	requestUpdate := false
   579  
   580  	req := &rds.ModifyDBClusterInput{
   581  		ApplyImmediately:    aws.Bool(d.Get("apply_immediately").(bool)),
   582  		DBClusterIdentifier: aws.String(d.Id()),
   583  	}
   584  
   585  	if d.HasChange("master_password") {
   586  		req.MasterUserPassword = aws.String(d.Get("master_password").(string))
   587  		requestUpdate = true
   588  	}
   589  
   590  	if d.HasChange("vpc_security_group_ids") {
   591  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   592  			req.VpcSecurityGroupIds = expandStringList(attr.List())
   593  		} else {
   594  			req.VpcSecurityGroupIds = []*string{}
   595  		}
   596  		requestUpdate = true
   597  	}
   598  
   599  	if d.HasChange("preferred_backup_window") {
   600  		req.PreferredBackupWindow = aws.String(d.Get("preferred_backup_window").(string))
   601  		requestUpdate = true
   602  	}
   603  
   604  	if d.HasChange("preferred_maintenance_window") {
   605  		req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string))
   606  		requestUpdate = true
   607  	}
   608  
   609  	if d.HasChange("backup_retention_period") {
   610  		req.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_period").(int)))
   611  		requestUpdate = true
   612  	}
   613  
   614  	if d.HasChange("db_cluster_parameter_group_name") {
   615  		d.SetPartial("db_cluster_parameter_group_name")
   616  		req.DBClusterParameterGroupName = aws.String(d.Get("db_cluster_parameter_group_name").(string))
   617  		requestUpdate = true
   618  	}
   619  
   620  	if d.HasChange("iam_database_authentication_enabled") {
   621  		req.EnableIAMDatabaseAuthentication = aws.Bool(d.Get("iam_database_authentication_enabled").(bool))
   622  		requestUpdate = true
   623  	}
   624  
   625  	if requestUpdate {
   626  		_, err := conn.ModifyDBCluster(req)
   627  		if err != nil {
   628  			return fmt.Errorf("[WARN] Error modifying RDS Cluster (%s): %s", d.Id(), err)
   629  		}
   630  	}
   631  
   632  	if arn, err := buildRDSClusterARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region); err == nil {
   633  		if err := setTagsRDS(conn, d, arn); err != nil {
   634  			return err
   635  		} else {
   636  			d.SetPartial("tags")
   637  		}
   638  	}
   639  
   640  	return resourceAwsRDSClusterRead(d, meta)
   641  }
   642  
   643  func resourceAwsRDSClusterDelete(d *schema.ResourceData, meta interface{}) error {
   644  	conn := meta.(*AWSClient).rdsconn
   645  	log.Printf("[DEBUG] Destroying RDS Cluster (%s)", d.Id())
   646  
   647  	deleteOpts := rds.DeleteDBClusterInput{
   648  		DBClusterIdentifier: aws.String(d.Id()),
   649  	}
   650  
   651  	skipFinalSnapshot := d.Get("skip_final_snapshot").(bool)
   652  	deleteOpts.SkipFinalSnapshot = aws.Bool(skipFinalSnapshot)
   653  
   654  	if skipFinalSnapshot == false {
   655  		if name, present := d.GetOk("final_snapshot_identifier"); present {
   656  			deleteOpts.FinalDBSnapshotIdentifier = aws.String(name.(string))
   657  		} else {
   658  			return fmt.Errorf("RDS Cluster FinalSnapshotIdentifier is required when a final snapshot is required")
   659  		}
   660  	}
   661  
   662  	log.Printf("[DEBUG] RDS Cluster delete options: %s", deleteOpts)
   663  	_, err := conn.DeleteDBCluster(&deleteOpts)
   664  	if err != nil {
   665  		if awsErr, ok := err.(awserr.Error); ok {
   666  			if "InvalidDBClusterStateFault" == awsErr.Code() {
   667  				return fmt.Errorf("RDS Cluster cannot be deleted: %s", awsErr.Message())
   668  			}
   669  		}
   670  	}
   671  
   672  	stateConf := &resource.StateChangeConf{
   673  		Pending:    []string{"available", "deleting", "backing-up", "modifying"},
   674  		Target:     []string{"destroyed"},
   675  		Refresh:    resourceAwsRDSClusterStateRefreshFunc(d, meta),
   676  		Timeout:    d.Timeout(schema.TimeoutDelete),
   677  		MinTimeout: 10 * time.Second,
   678  		Delay:      30 * time.Second,
   679  	}
   680  
   681  	// Wait, catching any errors
   682  	_, err = stateConf.WaitForState()
   683  	if err != nil {
   684  		return fmt.Errorf("[WARN] Error deleting RDS Cluster (%s): %s", d.Id(), err)
   685  	}
   686  
   687  	return nil
   688  }
   689  
   690  func resourceAwsRDSClusterStateRefreshFunc(
   691  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   692  	return func() (interface{}, string, error) {
   693  		conn := meta.(*AWSClient).rdsconn
   694  
   695  		resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
   696  			DBClusterIdentifier: aws.String(d.Id()),
   697  		})
   698  
   699  		if err != nil {
   700  			if awsErr, ok := err.(awserr.Error); ok {
   701  				if "DBClusterNotFoundFault" == awsErr.Code() {
   702  					return 42, "destroyed", nil
   703  				}
   704  			}
   705  			log.Printf("[WARN] Error on retrieving DB Cluster (%s) when waiting: %s", d.Id(), err)
   706  			return nil, "", err
   707  		}
   708  
   709  		var dbc *rds.DBCluster
   710  
   711  		for _, c := range resp.DBClusters {
   712  			if *c.DBClusterIdentifier == d.Id() {
   713  				dbc = c
   714  			}
   715  		}
   716  
   717  		if dbc == nil {
   718  			return 42, "destroyed", nil
   719  		}
   720  
   721  		if dbc.Status != nil {
   722  			log.Printf("[DEBUG] DB Cluster status (%s): %s", d.Id(), *dbc.Status)
   723  		}
   724  
   725  		return dbc, *dbc.Status, nil
   726  	}
   727  }
   728  
   729  func buildRDSClusterARN(identifier, partition, accountid, region string) (string, error) {
   730  	if partition == "" {
   731  		return "", fmt.Errorf("Unable to construct RDS Cluster ARN because of missing AWS partition")
   732  	}
   733  	if accountid == "" {
   734  		return "", fmt.Errorf("Unable to construct RDS Cluster ARN because of missing AWS Account ID")
   735  	}
   736  
   737  	arn := fmt.Sprintf("arn:%s:rds:%s:%s:cluster:%s", partition, region, accountid, identifier)
   738  	return arn, nil
   739  
   740  }