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