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 }