github.com/jdextraze/terraform@v0.6.17-0.20160511153921-e33847c8a8af/builtin/providers/aws/resource_aws_db_instance.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/iam" 13 "github.com/aws/aws-sdk-go/service/rds" 14 15 "github.com/hashicorp/terraform/helper/resource" 16 "github.com/hashicorp/terraform/helper/schema" 17 ) 18 19 func resourceAwsDbInstance() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceAwsDbInstanceCreate, 22 Read: resourceAwsDbInstanceRead, 23 Update: resourceAwsDbInstanceUpdate, 24 Delete: resourceAwsDbInstanceDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "name": &schema.Schema{ 28 Type: schema.TypeString, 29 Optional: true, 30 Computed: true, 31 ForceNew: true, 32 }, 33 34 "arn": &schema.Schema{ 35 Type: schema.TypeString, 36 Computed: true, 37 }, 38 39 "username": &schema.Schema{ 40 Type: schema.TypeString, 41 Optional: true, 42 Computed: true, 43 ForceNew: true, 44 }, 45 46 "password": &schema.Schema{ 47 Type: schema.TypeString, 48 Optional: true, 49 }, 50 51 "engine": &schema.Schema{ 52 Type: schema.TypeString, 53 Optional: true, 54 Computed: true, 55 ForceNew: true, 56 StateFunc: func(v interface{}) string { 57 value := v.(string) 58 return strings.ToLower(value) 59 }, 60 }, 61 62 "engine_version": &schema.Schema{ 63 Type: schema.TypeString, 64 Optional: true, 65 Computed: true, 66 }, 67 68 "storage_encrypted": &schema.Schema{ 69 Type: schema.TypeBool, 70 Optional: true, 71 ForceNew: true, 72 }, 73 74 "allocated_storage": &schema.Schema{ 75 Type: schema.TypeInt, 76 Optional: true, 77 Computed: true, 78 }, 79 80 "storage_type": &schema.Schema{ 81 Type: schema.TypeString, 82 Optional: true, 83 Computed: true, 84 }, 85 86 "identifier": &schema.Schema{ 87 Type: schema.TypeString, 88 Optional: true, 89 Computed: true, 90 ForceNew: true, 91 ValidateFunc: validateRdsId, 92 }, 93 94 "instance_class": &schema.Schema{ 95 Type: schema.TypeString, 96 Required: true, 97 }, 98 99 "availability_zone": &schema.Schema{ 100 Type: schema.TypeString, 101 Optional: true, 102 Computed: true, 103 ForceNew: true, 104 }, 105 106 "backup_retention_period": &schema.Schema{ 107 Type: schema.TypeInt, 108 Optional: true, 109 Computed: true, 110 }, 111 112 "backup_window": &schema.Schema{ 113 Type: schema.TypeString, 114 Optional: true, 115 Computed: true, 116 }, 117 118 "iops": &schema.Schema{ 119 Type: schema.TypeInt, 120 Optional: true, 121 }, 122 123 "license_model": &schema.Schema{ 124 Type: schema.TypeString, 125 Optional: true, 126 Computed: true, 127 }, 128 129 "maintenance_window": &schema.Schema{ 130 Type: schema.TypeString, 131 Optional: true, 132 Computed: true, 133 StateFunc: func(v interface{}) string { 134 if v != nil { 135 value := v.(string) 136 return strings.ToLower(value) 137 } 138 return "" 139 }, 140 }, 141 142 "multi_az": &schema.Schema{ 143 Type: schema.TypeBool, 144 Optional: true, 145 Computed: true, 146 }, 147 148 "port": &schema.Schema{ 149 Type: schema.TypeInt, 150 Optional: true, 151 Computed: true, 152 ForceNew: true, 153 }, 154 155 "publicly_accessible": &schema.Schema{ 156 Type: schema.TypeBool, 157 Optional: true, 158 Computed: true, 159 }, 160 161 "vpc_security_group_ids": &schema.Schema{ 162 Type: schema.TypeSet, 163 Optional: true, 164 Computed: true, 165 Elem: &schema.Schema{Type: schema.TypeString}, 166 Set: schema.HashString, 167 }, 168 169 "security_group_names": &schema.Schema{ 170 Type: schema.TypeSet, 171 Optional: true, 172 Elem: &schema.Schema{Type: schema.TypeString}, 173 Set: schema.HashString, 174 }, 175 176 "final_snapshot_identifier": &schema.Schema{ 177 Type: schema.TypeString, 178 Optional: true, 179 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 180 value := v.(string) 181 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 182 es = append(es, fmt.Errorf( 183 "only alphanumeric characters and hyphens allowed in %q", k)) 184 } 185 if regexp.MustCompile(`--`).MatchString(value) { 186 es = append(es, fmt.Errorf("%q cannot contain two consecutive hyphens", k)) 187 } 188 if regexp.MustCompile(`-$`).MatchString(value) { 189 es = append(es, fmt.Errorf("%q cannot end in a hyphen", k)) 190 } 191 return 192 }, 193 }, 194 195 "skip_final_snapshot": &schema.Schema{ 196 Type: schema.TypeBool, 197 Optional: true, 198 Default: true, 199 }, 200 201 "copy_tags_to_snapshot": &schema.Schema{ 202 Type: schema.TypeBool, 203 Optional: true, 204 Default: false, 205 }, 206 207 "db_subnet_group_name": &schema.Schema{ 208 Type: schema.TypeString, 209 Optional: true, 210 ForceNew: true, 211 Computed: true, 212 }, 213 214 "parameter_group_name": &schema.Schema{ 215 Type: schema.TypeString, 216 Optional: true, 217 Computed: true, 218 }, 219 220 "address": &schema.Schema{ 221 Type: schema.TypeString, 222 Computed: true, 223 }, 224 225 "endpoint": &schema.Schema{ 226 Type: schema.TypeString, 227 Computed: true, 228 }, 229 230 "status": &schema.Schema{ 231 Type: schema.TypeString, 232 Computed: true, 233 }, 234 235 // apply_immediately is used to determine when the update modifications 236 // take place. 237 // See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html 238 "apply_immediately": &schema.Schema{ 239 Type: schema.TypeBool, 240 Optional: true, 241 Computed: true, 242 }, 243 244 "replicate_source_db": &schema.Schema{ 245 Type: schema.TypeString, 246 Optional: true, 247 }, 248 249 "replicas": &schema.Schema{ 250 Type: schema.TypeList, 251 Computed: true, 252 Elem: &schema.Schema{Type: schema.TypeString}, 253 }, 254 255 "snapshot_identifier": &schema.Schema{ 256 Type: schema.TypeString, 257 Computed: false, 258 Optional: true, 259 Elem: &schema.Schema{Type: schema.TypeString}, 260 }, 261 262 "auto_minor_version_upgrade": &schema.Schema{ 263 Type: schema.TypeBool, 264 Optional: true, 265 Default: true, 266 }, 267 268 "allow_major_version_upgrade": &schema.Schema{ 269 Type: schema.TypeBool, 270 Computed: false, 271 Optional: true, 272 }, 273 274 "monitoring_role_arn": &schema.Schema{ 275 Type: schema.TypeString, 276 Optional: true, 277 Computed: true, 278 }, 279 280 "monitoring_interval": &schema.Schema{ 281 Type: schema.TypeInt, 282 Optional: true, 283 Default: 0, 284 }, 285 286 "option_group_name": &schema.Schema{ 287 Type: schema.TypeString, 288 Optional: true, 289 Computed: true, 290 }, 291 292 "tags": tagsSchema(), 293 }, 294 } 295 } 296 297 func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error { 298 conn := meta.(*AWSClient).rdsconn 299 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 300 301 identifier := d.Get("identifier").(string) 302 // Generate a unique ID for the user 303 if identifier == "" { 304 identifier = resource.PrefixedUniqueId("tf-") 305 // SQL Server identifier size is max 15 chars, so truncate 306 if engine := d.Get("engine").(string); engine != "" { 307 if strings.Contains(strings.ToLower(engine), "sqlserver") { 308 identifier = identifier[:15] 309 } 310 } 311 d.Set("identifier", identifier) 312 } 313 314 if v, ok := d.GetOk("replicate_source_db"); ok { 315 opts := rds.CreateDBInstanceReadReplicaInput{ 316 SourceDBInstanceIdentifier: aws.String(v.(string)), 317 CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), 318 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 319 DBInstanceIdentifier: aws.String(identifier), 320 Tags: tags, 321 } 322 if attr, ok := d.GetOk("iops"); ok { 323 opts.Iops = aws.Int64(int64(attr.(int))) 324 } 325 326 if attr, ok := d.GetOk("port"); ok { 327 opts.Port = aws.Int64(int64(attr.(int))) 328 } 329 330 if attr, ok := d.GetOk("availability_zone"); ok { 331 opts.AvailabilityZone = aws.String(attr.(string)) 332 } 333 334 if attr, ok := d.GetOk("storage_type"); ok { 335 opts.StorageType = aws.String(attr.(string)) 336 } 337 338 if attr, ok := d.GetOk("publicly_accessible"); ok { 339 opts.PubliclyAccessible = aws.Bool(attr.(bool)) 340 } 341 342 if attr, ok := d.GetOk("db_subnet_group_name"); ok { 343 opts.DBSubnetGroupName = aws.String(attr.(string)) 344 } 345 346 if attr, ok := d.GetOk("monitoring_role_arn"); ok { 347 opts.MonitoringRoleArn = aws.String(attr.(string)) 348 } 349 350 if attr, ok := d.GetOk("monitoring_interval"); ok { 351 opts.MonitoringInterval = aws.Int64(int64(attr.(int))) 352 } 353 354 if attr, ok := d.GetOk("option_group_name"); ok { 355 opts.OptionGroupName = aws.String(attr.(string)) 356 } 357 358 log.Printf("[DEBUG] DB Instance Replica create configuration: %#v", opts) 359 _, err := conn.CreateDBInstanceReadReplica(&opts) 360 if err != nil { 361 return fmt.Errorf("Error creating DB Instance: %s", err) 362 } 363 } else if _, ok := d.GetOk("snapshot_identifier"); ok { 364 opts := rds.RestoreDBInstanceFromDBSnapshotInput{ 365 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 366 DBInstanceIdentifier: aws.String(d.Get("identifier").(string)), 367 DBSnapshotIdentifier: aws.String(d.Get("snapshot_identifier").(string)), 368 AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)), 369 Tags: tags, 370 CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), 371 } 372 373 if attr, ok := d.GetOk("availability_zone"); ok { 374 opts.AvailabilityZone = aws.String(attr.(string)) 375 } 376 377 if attr, ok := d.GetOk("db_subnet_group_name"); ok { 378 opts.DBSubnetGroupName = aws.String(attr.(string)) 379 } 380 381 if attr, ok := d.GetOk("engine"); ok { 382 opts.Engine = aws.String(attr.(string)) 383 } 384 385 if attr, ok := d.GetOk("iops"); ok { 386 opts.Iops = aws.Int64(int64(attr.(int))) 387 } 388 389 if attr, ok := d.GetOk("license_model"); ok { 390 opts.LicenseModel = aws.String(attr.(string)) 391 } 392 393 if attr, ok := d.GetOk("multi_az"); ok { 394 opts.MultiAZ = aws.Bool(attr.(bool)) 395 } 396 397 if attr, ok := d.GetOk("option_group_name"); ok { 398 opts.OptionGroupName = aws.String(attr.(string)) 399 } 400 401 if attr, ok := d.GetOk("port"); ok { 402 opts.Port = aws.Int64(int64(attr.(int))) 403 } 404 405 if attr, ok := d.GetOk("publicly_accessible"); ok { 406 opts.PubliclyAccessible = aws.Bool(attr.(bool)) 407 } 408 409 if attr, ok := d.GetOk("tde_credential_arn"); ok { 410 opts.TdeCredentialArn = aws.String(attr.(string)) 411 } 412 413 if attr, ok := d.GetOk("storage_type"); ok { 414 opts.StorageType = aws.String(attr.(string)) 415 } 416 417 log.Printf("[DEBUG] DB Instance restore from snapshot configuration: %s", opts) 418 _, err := conn.RestoreDBInstanceFromDBSnapshot(&opts) 419 if err != nil { 420 return fmt.Errorf("Error creating DB Instance: %s", err) 421 } 422 423 var sgUpdate bool 424 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 425 sgUpdate = true 426 } 427 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 428 sgUpdate = true 429 } 430 if sgUpdate { 431 log.Printf("[INFO] DB is restoring from snapshot with default security, but custom security should be set, will now update after snapshot is restored!") 432 433 // wait for instance to get up and then modify security 434 d.SetId(d.Get("identifier").(string)) 435 436 log.Printf("[INFO] DB Instance ID: %s", d.Id()) 437 438 log.Println( 439 "[INFO] Waiting for DB Instance to be available") 440 441 stateConf := &resource.StateChangeConf{ 442 Pending: []string{"creating", "backing-up", "modifying", "resetting-master-credentials", 443 "maintenance", "renaming", "rebooting", "upgrading"}, 444 Target: []string{"available"}, 445 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 446 Timeout: 40 * time.Minute, 447 MinTimeout: 10 * time.Second, 448 Delay: 30 * time.Second, // Wait 30 secs before starting 449 } 450 451 // Wait, catching any errors 452 _, err := stateConf.WaitForState() 453 if err != nil { 454 return err 455 } 456 457 err = resourceAwsDbInstanceUpdate(d, meta) 458 if err != nil { 459 return err 460 } 461 462 } 463 } else { 464 if _, ok := d.GetOk("allocated_storage"); !ok { 465 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "allocated_storage": required field is not set`, d.Get("name").(string)) 466 } 467 if _, ok := d.GetOk("engine"); !ok { 468 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "engine": required field is not set`, d.Get("name").(string)) 469 } 470 if _, ok := d.GetOk("password"); !ok { 471 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "password": required field is not set`, d.Get("name").(string)) 472 } 473 if _, ok := d.GetOk("username"); !ok { 474 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "username": required field is not set`, d.Get("name").(string)) 475 } 476 opts := rds.CreateDBInstanceInput{ 477 AllocatedStorage: aws.Int64(int64(d.Get("allocated_storage").(int))), 478 DBName: aws.String(d.Get("name").(string)), 479 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 480 DBInstanceIdentifier: aws.String(d.Get("identifier").(string)), 481 MasterUsername: aws.String(d.Get("username").(string)), 482 MasterUserPassword: aws.String(d.Get("password").(string)), 483 Engine: aws.String(d.Get("engine").(string)), 484 EngineVersion: aws.String(d.Get("engine_version").(string)), 485 StorageEncrypted: aws.Bool(d.Get("storage_encrypted").(bool)), 486 AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)), 487 Tags: tags, 488 CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), 489 } 490 491 attr := d.Get("backup_retention_period") 492 opts.BackupRetentionPeriod = aws.Int64(int64(attr.(int))) 493 if attr, ok := d.GetOk("multi_az"); ok { 494 opts.MultiAZ = aws.Bool(attr.(bool)) 495 } 496 497 if attr, ok := d.GetOk("maintenance_window"); ok { 498 opts.PreferredMaintenanceWindow = aws.String(attr.(string)) 499 } 500 501 if attr, ok := d.GetOk("backup_window"); ok { 502 opts.PreferredBackupWindow = aws.String(attr.(string)) 503 } 504 505 if attr, ok := d.GetOk("license_model"); ok { 506 opts.LicenseModel = aws.String(attr.(string)) 507 } 508 if attr, ok := d.GetOk("parameter_group_name"); ok { 509 opts.DBParameterGroupName = aws.String(attr.(string)) 510 } 511 512 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 513 var s []*string 514 for _, v := range attr.List() { 515 s = append(s, aws.String(v.(string))) 516 } 517 opts.VpcSecurityGroupIds = s 518 } 519 520 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 521 var s []*string 522 for _, v := range attr.List() { 523 s = append(s, aws.String(v.(string))) 524 } 525 opts.DBSecurityGroups = s 526 } 527 if attr, ok := d.GetOk("storage_type"); ok { 528 opts.StorageType = aws.String(attr.(string)) 529 } 530 531 if attr, ok := d.GetOk("db_subnet_group_name"); ok { 532 opts.DBSubnetGroupName = aws.String(attr.(string)) 533 } 534 535 if attr, ok := d.GetOk("iops"); ok { 536 opts.Iops = aws.Int64(int64(attr.(int))) 537 } 538 539 if attr, ok := d.GetOk("port"); ok { 540 opts.Port = aws.Int64(int64(attr.(int))) 541 } 542 543 if attr, ok := d.GetOk("availability_zone"); ok { 544 opts.AvailabilityZone = aws.String(attr.(string)) 545 } 546 547 if attr, ok := d.GetOk("publicly_accessible"); ok { 548 opts.PubliclyAccessible = aws.Bool(attr.(bool)) 549 } 550 551 if attr, ok := d.GetOk("monitoring_role_arn"); ok { 552 opts.MonitoringRoleArn = aws.String(attr.(string)) 553 } 554 555 if attr, ok := d.GetOk("monitoring_interval"); ok { 556 opts.MonitoringInterval = aws.Int64(int64(attr.(int))) 557 } 558 559 if attr, ok := d.GetOk("option_group_name"); ok { 560 opts.OptionGroupName = aws.String(attr.(string)) 561 } 562 563 log.Printf("[DEBUG] DB Instance create configuration: %#v", opts) 564 var err error 565 err = resource.Retry(5*time.Minute, func() *resource.RetryError { 566 _, err = conn.CreateDBInstance(&opts) 567 if err != nil { 568 if awsErr, ok := err.(awserr.Error); ok { 569 if awsErr.Code() == "InvalidParameterValue" && strings.Contains(awsErr.Message(), "ENHANCED_MONITORING") { 570 return resource.RetryableError(awsErr) 571 } 572 } 573 return resource.NonRetryableError(err) 574 } 575 return nil 576 }) 577 if err != nil { 578 return fmt.Errorf("Error creating DB Instance: %s", err) 579 } 580 } 581 582 d.SetId(d.Get("identifier").(string)) 583 584 log.Printf("[INFO] DB Instance ID: %s", d.Id()) 585 586 log.Println( 587 "[INFO] Waiting for DB Instance to be available") 588 589 stateConf := &resource.StateChangeConf{ 590 Pending: []string{"creating", "backing-up", "modifying", "resetting-master-credentials", 591 "maintenance", "renaming", "rebooting", "upgrading"}, 592 Target: []string{"available"}, 593 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 594 Timeout: 40 * time.Minute, 595 MinTimeout: 10 * time.Second, 596 Delay: 30 * time.Second, // Wait 30 secs before starting 597 } 598 599 // Wait, catching any errors 600 _, err := stateConf.WaitForState() 601 if err != nil { 602 return err 603 } 604 605 return resourceAwsDbInstanceRead(d, meta) 606 } 607 608 func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { 609 v, err := resourceAwsDbInstanceRetrieve(d, meta) 610 611 if err != nil { 612 return err 613 } 614 if v == nil { 615 d.SetId("") 616 return nil 617 } 618 619 d.Set("name", v.DBName) 620 d.Set("username", v.MasterUsername) 621 d.Set("engine", v.Engine) 622 d.Set("engine_version", v.EngineVersion) 623 d.Set("allocated_storage", v.AllocatedStorage) 624 d.Set("copy_tags_to_snapshot", v.CopyTagsToSnapshot) 625 d.Set("auto_minor_version_upgrade", v.AutoMinorVersionUpgrade) 626 d.Set("storage_type", v.StorageType) 627 d.Set("instance_class", v.DBInstanceClass) 628 d.Set("availability_zone", v.AvailabilityZone) 629 d.Set("backup_retention_period", v.BackupRetentionPeriod) 630 d.Set("backup_window", v.PreferredBackupWindow) 631 d.Set("license_model", v.LicenseModel) 632 d.Set("maintenance_window", v.PreferredMaintenanceWindow) 633 d.Set("publicly_accessible", v.PubliclyAccessible) 634 d.Set("multi_az", v.MultiAZ) 635 if v.DBSubnetGroup != nil { 636 d.Set("db_subnet_group_name", v.DBSubnetGroup.DBSubnetGroupName) 637 } 638 639 if len(v.DBParameterGroups) > 0 { 640 d.Set("parameter_group_name", v.DBParameterGroups[0].DBParameterGroupName) 641 } 642 643 if v.Endpoint != nil { 644 d.Set("port", v.Endpoint.Port) 645 d.Set("address", v.Endpoint.Address) 646 647 if v.Endpoint.Address != nil && v.Endpoint.Port != nil { 648 d.Set("endpoint", 649 fmt.Sprintf("%s:%d", *v.Endpoint.Address, *v.Endpoint.Port)) 650 } 651 } 652 653 d.Set("status", v.DBInstanceStatus) 654 d.Set("storage_encrypted", v.StorageEncrypted) 655 if v.OptionGroupMemberships != nil { 656 d.Set("option_group_name", v.OptionGroupMemberships[0].OptionGroupName) 657 } 658 659 if v.MonitoringInterval != nil { 660 d.Set("monitoring_interval", v.MonitoringInterval) 661 } 662 663 if v.MonitoringRoleArn != nil { 664 d.Set("monitoring_role_arn", v.MonitoringRoleArn) 665 } 666 667 // list tags for resource 668 // set tags 669 conn := meta.(*AWSClient).rdsconn 670 arn, err := buildRDSARN(d.Id(), meta) 671 if err != nil { 672 name := "<empty>" 673 if v.DBName != nil && *v.DBName != "" { 674 name = *v.DBName 675 } 676 log.Printf("[DEBUG] Error building ARN for DB Instance, not setting Tags for DB %s", name) 677 } else { 678 d.Set("arn", arn) 679 resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{ 680 ResourceName: aws.String(arn), 681 }) 682 683 if err != nil { 684 log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn) 685 } 686 687 var dt []*rds.Tag 688 if len(resp.TagList) > 0 { 689 dt = resp.TagList 690 } 691 d.Set("tags", tagsToMapRDS(dt)) 692 } 693 694 // Create an empty schema.Set to hold all vpc security group ids 695 ids := &schema.Set{ 696 F: schema.HashString, 697 } 698 for _, v := range v.VpcSecurityGroups { 699 ids.Add(*v.VpcSecurityGroupId) 700 } 701 d.Set("vpc_security_group_ids", ids) 702 703 // Create an empty schema.Set to hold all security group names 704 sgn := &schema.Set{ 705 F: schema.HashString, 706 } 707 for _, v := range v.DBSecurityGroups { 708 sgn.Add(*v.DBSecurityGroupName) 709 } 710 d.Set("security_group_names", sgn) 711 712 // replica things 713 714 var replicas []string 715 for _, v := range v.ReadReplicaDBInstanceIdentifiers { 716 replicas = append(replicas, *v) 717 } 718 if err := d.Set("replicas", replicas); err != nil { 719 return fmt.Errorf("[DEBUG] Error setting replicas attribute: %#v, error: %#v", replicas, err) 720 } 721 722 d.Set("replicate_source_db", v.ReadReplicaSourceDBInstanceIdentifier) 723 724 return nil 725 } 726 727 func resourceAwsDbInstanceDelete(d *schema.ResourceData, meta interface{}) error { 728 conn := meta.(*AWSClient).rdsconn 729 730 log.Printf("[DEBUG] DB Instance destroy: %v", d.Id()) 731 732 opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())} 733 734 skipFinalSnapshot := d.Get("skip_final_snapshot").(bool) 735 opts.SkipFinalSnapshot = aws.Bool(skipFinalSnapshot) 736 737 if !skipFinalSnapshot { 738 if name, present := d.GetOk("final_snapshot_identifier"); present { 739 opts.FinalDBSnapshotIdentifier = aws.String(name.(string)) 740 } else { 741 return fmt.Errorf("DB Instance FinalSnapshotIdentifier is required when a final snapshot is required") 742 } 743 } 744 745 log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts) 746 if _, err := conn.DeleteDBInstance(&opts); err != nil { 747 return err 748 } 749 750 log.Println( 751 "[INFO] Waiting for DB Instance to be destroyed") 752 stateConf := &resource.StateChangeConf{ 753 Pending: []string{"creating", "backing-up", 754 "modifying", "deleting", "available"}, 755 Target: []string{}, 756 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 757 Timeout: 40 * time.Minute, 758 MinTimeout: 10 * time.Second, 759 Delay: 30 * time.Second, // Wait 30 secs before starting 760 } 761 if _, err := stateConf.WaitForState(); err != nil { 762 return err 763 } 764 765 return nil 766 } 767 768 func resourceAwsDbInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 769 conn := meta.(*AWSClient).rdsconn 770 771 d.Partial(true) 772 773 req := &rds.ModifyDBInstanceInput{ 774 ApplyImmediately: aws.Bool(d.Get("apply_immediately").(bool)), 775 DBInstanceIdentifier: aws.String(d.Id()), 776 } 777 d.SetPartial("apply_immediately") 778 779 requestUpdate := false 780 if d.HasChange("allocated_storage") { 781 d.SetPartial("allocated_storage") 782 req.AllocatedStorage = aws.Int64(int64(d.Get("allocated_storage").(int))) 783 requestUpdate = true 784 } 785 if d.HasChange("allow_major_version_upgrade") { 786 d.SetPartial("allow_major_version_upgrade") 787 req.AllowMajorVersionUpgrade = aws.Bool(d.Get("allow_major_version_upgrade").(bool)) 788 requestUpdate = true 789 } 790 if d.HasChange("backup_retention_period") { 791 d.SetPartial("backup_retention_period") 792 req.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_period").(int))) 793 requestUpdate = true 794 } 795 if d.HasChange("copy_tags_to_snapshot") { 796 d.SetPartial("copy_tags_to_snapshot") 797 req.CopyTagsToSnapshot = aws.Bool(d.Get("copy_tags_to_snapshot").(bool)) 798 requestUpdate = true 799 } 800 if d.HasChange("instance_class") { 801 d.SetPartial("instance_class") 802 req.DBInstanceClass = aws.String(d.Get("instance_class").(string)) 803 requestUpdate = true 804 } 805 if d.HasChange("parameter_group_name") { 806 d.SetPartial("parameter_group_name") 807 req.DBParameterGroupName = aws.String(d.Get("parameter_group_name").(string)) 808 requestUpdate = true 809 } 810 if d.HasChange("engine_version") { 811 d.SetPartial("engine_version") 812 req.EngineVersion = aws.String(d.Get("engine_version").(string)) 813 requestUpdate = true 814 } 815 if d.HasChange("iops") { 816 d.SetPartial("iops") 817 req.Iops = aws.Int64(int64(d.Get("iops").(int))) 818 requestUpdate = true 819 } 820 if d.HasChange("backup_window") { 821 d.SetPartial("backup_window") 822 req.PreferredBackupWindow = aws.String(d.Get("backup_window").(string)) 823 requestUpdate = true 824 } 825 if d.HasChange("maintenance_window") { 826 d.SetPartial("maintenance_window") 827 req.PreferredMaintenanceWindow = aws.String(d.Get("maintenance_window").(string)) 828 requestUpdate = true 829 } 830 if d.HasChange("password") { 831 d.SetPartial("password") 832 req.MasterUserPassword = aws.String(d.Get("password").(string)) 833 requestUpdate = true 834 } 835 if d.HasChange("multi_az") { 836 d.SetPartial("multi_az") 837 req.MultiAZ = aws.Bool(d.Get("multi_az").(bool)) 838 requestUpdate = true 839 } 840 if d.HasChange("publicly_accessible") { 841 d.SetPartial("publicly_accessible") 842 req.PubliclyAccessible = aws.Bool(d.Get("publicly_accessible").(bool)) 843 requestUpdate = true 844 } 845 if d.HasChange("storage_type") { 846 d.SetPartial("storage_type") 847 req.StorageType = aws.String(d.Get("storage_type").(string)) 848 requestUpdate = true 849 850 if *req.StorageType == "io1" { 851 req.Iops = aws.Int64(int64(d.Get("iops").(int))) 852 } 853 } 854 if d.HasChange("auto_minor_version_upgrade") { 855 d.SetPartial("auto_minor_version_upgrade") 856 req.AutoMinorVersionUpgrade = aws.Bool(d.Get("auto_minor_version_upgrade").(bool)) 857 requestUpdate = true 858 } 859 860 if d.HasChange("monitoring_role_arn") { 861 d.SetPartial("monitoring_role_arn") 862 req.MonitoringRoleArn = aws.String(d.Get("monitoring_role_arn").(string)) 863 requestUpdate = true 864 } 865 866 if d.HasChange("monitoring_interval") { 867 d.SetPartial("monitoring_interval") 868 req.MonitoringInterval = aws.Int64(int64(d.Get("monitoring_interval").(int))) 869 requestUpdate = true 870 } 871 872 if d.HasChange("vpc_security_group_ids") { 873 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 874 var s []*string 875 for _, v := range attr.List() { 876 s = append(s, aws.String(v.(string))) 877 } 878 req.VpcSecurityGroupIds = s 879 } 880 requestUpdate = true 881 } 882 883 if d.HasChange("security_group_names") { 884 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 885 var s []*string 886 for _, v := range attr.List() { 887 s = append(s, aws.String(v.(string))) 888 } 889 req.DBSecurityGroups = s 890 } 891 requestUpdate = true 892 } 893 894 if d.HasChange("option_group_name") { 895 d.SetPartial("option_group_name") 896 req.OptionGroupName = aws.String(d.Get("option_group_name").(string)) 897 requestUpdate = true 898 } 899 900 log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate) 901 if requestUpdate { 902 log.Printf("[DEBUG] DB Instance Modification request: %#v", req) 903 _, err := conn.ModifyDBInstance(req) 904 if err != nil { 905 return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err) 906 } 907 } 908 909 // separate request to promote a database 910 if d.HasChange("replicate_source_db") { 911 if d.Get("replicate_source_db").(string) == "" { 912 // promote 913 opts := rds.PromoteReadReplicaInput{ 914 DBInstanceIdentifier: aws.String(d.Id()), 915 } 916 attr := d.Get("backup_retention_period") 917 opts.BackupRetentionPeriod = aws.Int64(int64(attr.(int))) 918 if attr, ok := d.GetOk("backup_window"); ok { 919 opts.PreferredBackupWindow = aws.String(attr.(string)) 920 } 921 _, err := conn.PromoteReadReplica(&opts) 922 if err != nil { 923 return fmt.Errorf("Error promoting database: %#v", err) 924 } 925 d.Set("replicate_source_db", "") 926 } else { 927 return fmt.Errorf("cannot elect new source database for replication") 928 } 929 } 930 931 if arn, err := buildRDSARN(d.Id(), meta); err == nil { 932 if err := setTagsRDS(conn, d, arn); err != nil { 933 return err 934 } else { 935 d.SetPartial("tags") 936 } 937 } 938 d.Partial(false) 939 940 return resourceAwsDbInstanceRead(d, meta) 941 } 942 943 // resourceAwsDbInstanceRetrieve fetches DBInstance information from the AWS 944 // API. It returns an error if there is a communication problem or unexpected 945 // error with AWS. When the DBInstance is not found, it returns no error and a 946 // nil pointer. 947 func resourceAwsDbInstanceRetrieve( 948 d *schema.ResourceData, meta interface{}) (*rds.DBInstance, error) { 949 conn := meta.(*AWSClient).rdsconn 950 951 opts := rds.DescribeDBInstancesInput{ 952 DBInstanceIdentifier: aws.String(d.Id()), 953 } 954 955 log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts) 956 957 resp, err := conn.DescribeDBInstances(&opts) 958 if err != nil { 959 dbinstanceerr, ok := err.(awserr.Error) 960 if ok && dbinstanceerr.Code() == "DBInstanceNotFound" { 961 return nil, nil 962 } 963 return nil, fmt.Errorf("Error retrieving DB Instances: %s", err) 964 } 965 966 if len(resp.DBInstances) != 1 || 967 *resp.DBInstances[0].DBInstanceIdentifier != d.Id() { 968 if err != nil { 969 return nil, nil 970 } 971 } 972 973 return resp.DBInstances[0], nil 974 } 975 976 func resourceAwsDbInstanceStateRefreshFunc( 977 d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { 978 return func() (interface{}, string, error) { 979 v, err := resourceAwsDbInstanceRetrieve(d, meta) 980 981 if err != nil { 982 log.Printf("Error on retrieving DB Instance when waiting: %s", err) 983 return nil, "", err 984 } 985 986 if v == nil { 987 return nil, "", nil 988 } 989 990 if v.DBInstanceStatus != nil { 991 log.Printf("[DEBUG] DB Instance status for instance %s: %s", d.Id(), *v.DBInstanceStatus) 992 } 993 994 return v, *v.DBInstanceStatus, nil 995 } 996 } 997 998 func buildRDSARN(identifier string, meta interface{}) (string, error) { 999 iamconn := meta.(*AWSClient).iamconn 1000 region := meta.(*AWSClient).region 1001 // An zero value GetUserInput{} defers to the currently logged in user 1002 resp, err := iamconn.GetUser(&iam.GetUserInput{}) 1003 if err != nil { 1004 return "", err 1005 } 1006 userARN := *resp.User.Arn 1007 accountID := strings.Split(userARN, ":")[4] 1008 arn := fmt.Sprintf("arn:aws:rds:%s:%s:db:%s", region, accountID, identifier) 1009 return arn, nil 1010 }