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