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