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