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