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