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