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 }