github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/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 "resource_id": { 344 Type: schema.TypeString, 345 Computed: true, 346 }, 347 348 "tags": tagsSchema(), 349 }, 350 } 351 } 352 353 func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error { 354 conn := meta.(*AWSClient).rdsconn 355 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 356 357 var identifier string 358 if v, ok := d.GetOk("identifier"); ok { 359 identifier = v.(string) 360 } else { 361 if v, ok := d.GetOk("identifier_prefix"); ok { 362 identifier = resource.PrefixedUniqueId(v.(string)) 363 } else { 364 identifier = resource.UniqueId() 365 } 366 367 // SQL Server identifier size is max 15 chars, so truncate 368 if engine := d.Get("engine").(string); engine != "" { 369 if strings.Contains(strings.ToLower(engine), "sqlserver") { 370 identifier = identifier[:15] 371 } 372 } 373 d.Set("identifier", identifier) 374 } 375 376 if v, ok := d.GetOk("replicate_source_db"); ok { 377 opts := rds.CreateDBInstanceReadReplicaInput{ 378 SourceDBInstanceIdentifier: aws.String(v.(string)), 379 CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), 380 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 381 DBInstanceIdentifier: aws.String(identifier), 382 PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)), 383 Tags: tags, 384 } 385 if attr, ok := d.GetOk("iops"); ok { 386 opts.Iops = aws.Int64(int64(attr.(int))) 387 } 388 389 if attr, ok := d.GetOk("port"); ok { 390 opts.Port = aws.Int64(int64(attr.(int))) 391 } 392 393 if attr, ok := d.GetOk("availability_zone"); ok { 394 opts.AvailabilityZone = aws.String(attr.(string)) 395 } 396 397 if attr, ok := d.GetOk("storage_type"); ok { 398 opts.StorageType = 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("monitoring_role_arn"); ok { 406 opts.MonitoringRoleArn = aws.String(attr.(string)) 407 } 408 409 if attr, ok := d.GetOk("monitoring_interval"); ok { 410 opts.MonitoringInterval = aws.Int64(int64(attr.(int))) 411 } 412 413 if attr, ok := d.GetOk("option_group_name"); ok { 414 opts.OptionGroupName = aws.String(attr.(string)) 415 } 416 417 log.Printf("[DEBUG] DB Instance Replica create configuration: %#v", opts) 418 _, err := conn.CreateDBInstanceReadReplica(&opts) 419 if err != nil { 420 return fmt.Errorf("Error creating DB Instance: %s", err) 421 } 422 } else if _, ok := d.GetOk("snapshot_identifier"); ok { 423 opts := rds.RestoreDBInstanceFromDBSnapshotInput{ 424 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 425 DBInstanceIdentifier: aws.String(d.Get("identifier").(string)), 426 DBSnapshotIdentifier: aws.String(d.Get("snapshot_identifier").(string)), 427 AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)), 428 PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)), 429 Tags: tags, 430 CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), 431 } 432 433 if attr, ok := d.GetOk("name"); ok { 434 // "Note: This parameter [DBName] doesn't apply to the MySQL, PostgreSQL, or MariaDB engines." 435 // https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_RestoreDBInstanceFromDBSnapshot.html 436 switch strings.ToLower(d.Get("engine").(string)) { 437 case "mysql", "postgres", "mariadb": 438 // skip 439 default: 440 opts.DBName = aws.String(attr.(string)) 441 } 442 } 443 444 if attr, ok := d.GetOk("availability_zone"); ok { 445 opts.AvailabilityZone = aws.String(attr.(string)) 446 } 447 448 if attr, ok := d.GetOk("db_subnet_group_name"); ok { 449 opts.DBSubnetGroupName = aws.String(attr.(string)) 450 } 451 452 if attr, ok := d.GetOk("engine"); ok { 453 opts.Engine = aws.String(attr.(string)) 454 } 455 456 if attr, ok := d.GetOk("iops"); ok { 457 opts.Iops = aws.Int64(int64(attr.(int))) 458 } 459 460 if attr, ok := d.GetOk("license_model"); ok { 461 opts.LicenseModel = aws.String(attr.(string)) 462 } 463 464 if attr, ok := d.GetOk("multi_az"); ok { 465 opts.MultiAZ = aws.Bool(attr.(bool)) 466 } 467 468 if attr, ok := d.GetOk("option_group_name"); ok { 469 opts.OptionGroupName = aws.String(attr.(string)) 470 471 } 472 473 if attr, ok := d.GetOk("port"); ok { 474 opts.Port = aws.Int64(int64(attr.(int))) 475 } 476 477 if attr, ok := d.GetOk("tde_credential_arn"); ok { 478 opts.TdeCredentialArn = aws.String(attr.(string)) 479 } 480 481 if attr, ok := d.GetOk("storage_type"); ok { 482 opts.StorageType = aws.String(attr.(string)) 483 } 484 485 log.Printf("[DEBUG] DB Instance restore from snapshot configuration: %s", opts) 486 _, err := conn.RestoreDBInstanceFromDBSnapshot(&opts) 487 if err != nil { 488 return fmt.Errorf("Error creating DB Instance: %s", err) 489 } 490 491 var sgUpdate bool 492 var passwordUpdate bool 493 494 if _, ok := d.GetOk("password"); ok { 495 passwordUpdate = true 496 } 497 498 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 499 sgUpdate = true 500 } 501 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 502 sgUpdate = true 503 } 504 if sgUpdate || passwordUpdate { 505 log.Printf("[INFO] DB is restoring from snapshot with default security, but custom security should be set, will now update after snapshot is restored!") 506 507 // wait for instance to get up and then modify security 508 d.SetId(d.Get("identifier").(string)) 509 510 log.Printf("[INFO] DB Instance ID: %s", d.Id()) 511 512 log.Println( 513 "[INFO] Waiting for DB Instance to be available") 514 515 stateConf := &resource.StateChangeConf{ 516 Pending: []string{"creating", "backing-up", "modifying", "resetting-master-credentials", 517 "maintenance", "renaming", "rebooting", "upgrading"}, 518 Target: []string{"available"}, 519 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 520 Timeout: d.Timeout(schema.TimeoutCreate), 521 MinTimeout: 10 * time.Second, 522 Delay: 30 * time.Second, // Wait 30 secs before starting 523 } 524 525 // Wait, catching any errors 526 _, err := stateConf.WaitForState() 527 if err != nil { 528 return err 529 } 530 531 err = resourceAwsDbInstanceUpdate(d, meta) 532 if err != nil { 533 return err 534 } 535 536 } 537 } else { 538 if _, ok := d.GetOk("allocated_storage"); !ok { 539 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "allocated_storage": required field is not set`, d.Get("name").(string)) 540 } 541 if _, ok := d.GetOk("engine"); !ok { 542 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "engine": required field is not set`, d.Get("name").(string)) 543 } 544 if _, ok := d.GetOk("password"); !ok { 545 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "password": required field is not set`, d.Get("name").(string)) 546 } 547 if _, ok := d.GetOk("username"); !ok { 548 return fmt.Errorf(`provider.aws: aws_db_instance: %s: "username": required field is not set`, d.Get("name").(string)) 549 } 550 opts := rds.CreateDBInstanceInput{ 551 AllocatedStorage: aws.Int64(int64(d.Get("allocated_storage").(int))), 552 DBName: aws.String(d.Get("name").(string)), 553 DBInstanceClass: aws.String(d.Get("instance_class").(string)), 554 DBInstanceIdentifier: aws.String(d.Get("identifier").(string)), 555 MasterUsername: aws.String(d.Get("username").(string)), 556 MasterUserPassword: aws.String(d.Get("password").(string)), 557 Engine: aws.String(d.Get("engine").(string)), 558 EngineVersion: aws.String(d.Get("engine_version").(string)), 559 StorageEncrypted: aws.Bool(d.Get("storage_encrypted").(bool)), 560 AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)), 561 PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)), 562 Tags: tags, 563 CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), 564 } 565 566 attr := d.Get("backup_retention_period") 567 opts.BackupRetentionPeriod = aws.Int64(int64(attr.(int))) 568 if attr, ok := d.GetOk("multi_az"); ok { 569 opts.MultiAZ = aws.Bool(attr.(bool)) 570 571 } 572 573 if attr, ok := d.GetOk("character_set_name"); ok { 574 opts.CharacterSetName = aws.String(attr.(string)) 575 } 576 577 if attr, ok := d.GetOk("timezone"); ok { 578 opts.Timezone = aws.String(attr.(string)) 579 } 580 581 if attr, ok := d.GetOk("maintenance_window"); ok { 582 opts.PreferredMaintenanceWindow = aws.String(attr.(string)) 583 } 584 585 if attr, ok := d.GetOk("backup_window"); ok { 586 opts.PreferredBackupWindow = aws.String(attr.(string)) 587 } 588 589 if attr, ok := d.GetOk("license_model"); ok { 590 opts.LicenseModel = aws.String(attr.(string)) 591 } 592 if attr, ok := d.GetOk("parameter_group_name"); ok { 593 opts.DBParameterGroupName = aws.String(attr.(string)) 594 } 595 596 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 597 var s []*string 598 for _, v := range attr.List() { 599 s = append(s, aws.String(v.(string))) 600 } 601 opts.VpcSecurityGroupIds = s 602 } 603 604 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 605 var s []*string 606 for _, v := range attr.List() { 607 s = append(s, aws.String(v.(string))) 608 } 609 opts.DBSecurityGroups = s 610 } 611 if attr, ok := d.GetOk("storage_type"); ok { 612 opts.StorageType = aws.String(attr.(string)) 613 } 614 615 if attr, ok := d.GetOk("db_subnet_group_name"); ok { 616 opts.DBSubnetGroupName = aws.String(attr.(string)) 617 } 618 619 if attr, ok := d.GetOk("iops"); ok { 620 opts.Iops = aws.Int64(int64(attr.(int))) 621 } 622 623 if attr, ok := d.GetOk("port"); ok { 624 opts.Port = aws.Int64(int64(attr.(int))) 625 } 626 627 if attr, ok := d.GetOk("availability_zone"); ok { 628 opts.AvailabilityZone = aws.String(attr.(string)) 629 } 630 631 if attr, ok := d.GetOk("monitoring_role_arn"); ok { 632 opts.MonitoringRoleArn = aws.String(attr.(string)) 633 } 634 635 if attr, ok := d.GetOk("monitoring_interval"); ok { 636 opts.MonitoringInterval = aws.Int64(int64(attr.(int))) 637 } 638 639 if attr, ok := d.GetOk("option_group_name"); ok { 640 opts.OptionGroupName = aws.String(attr.(string)) 641 } 642 643 if attr, ok := d.GetOk("kms_key_id"); ok { 644 opts.KmsKeyId = aws.String(attr.(string)) 645 } 646 647 if attr, ok := d.GetOk("iam_database_authentication_enabled"); ok { 648 opts.EnableIAMDatabaseAuthentication = aws.Bool(attr.(bool)) 649 } 650 651 log.Printf("[DEBUG] DB Instance create configuration: %#v", opts) 652 var err error 653 err = resource.Retry(5*time.Minute, func() *resource.RetryError { 654 _, err = conn.CreateDBInstance(&opts) 655 if err != nil { 656 if awsErr, ok := err.(awserr.Error); ok { 657 if awsErr.Code() == "InvalidParameterValue" && strings.Contains(awsErr.Message(), "ENHANCED_MONITORING") { 658 return resource.RetryableError(awsErr) 659 } 660 } 661 return resource.NonRetryableError(err) 662 } 663 return nil 664 }) 665 if err != nil { 666 return fmt.Errorf("Error creating DB Instance: %s", err) 667 } 668 } 669 670 d.SetId(d.Get("identifier").(string)) 671 672 log.Printf("[INFO] DB Instance ID: %s", d.Id()) 673 674 log.Println( 675 "[INFO] Waiting for DB Instance to be available") 676 677 stateConf := &resource.StateChangeConf{ 678 Pending: []string{"creating", "backing-up", "modifying", "resetting-master-credentials", 679 "maintenance", "renaming", "rebooting", "upgrading", "configuring-enhanced-monitoring"}, 680 Target: []string{"available"}, 681 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 682 Timeout: d.Timeout(schema.TimeoutCreate), 683 MinTimeout: 10 * time.Second, 684 Delay: 30 * time.Second, // Wait 30 secs before starting 685 } 686 687 // Wait, catching any errors 688 _, err := stateConf.WaitForState() 689 if err != nil { 690 return err 691 } 692 693 return resourceAwsDbInstanceRead(d, meta) 694 } 695 696 func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { 697 v, err := resourceAwsDbInstanceRetrieve(d, meta) 698 699 if err != nil { 700 return err 701 } 702 if v == nil { 703 d.SetId("") 704 return nil 705 } 706 707 d.Set("name", v.DBName) 708 d.Set("identifier", v.DBInstanceIdentifier) 709 d.Set("resource_id", v.DbiResourceId) 710 d.Set("username", v.MasterUsername) 711 d.Set("engine", v.Engine) 712 d.Set("engine_version", v.EngineVersion) 713 d.Set("allocated_storage", v.AllocatedStorage) 714 d.Set("iops", v.Iops) 715 d.Set("copy_tags_to_snapshot", v.CopyTagsToSnapshot) 716 d.Set("auto_minor_version_upgrade", v.AutoMinorVersionUpgrade) 717 d.Set("storage_type", v.StorageType) 718 d.Set("instance_class", v.DBInstanceClass) 719 d.Set("availability_zone", v.AvailabilityZone) 720 d.Set("backup_retention_period", v.BackupRetentionPeriod) 721 d.Set("backup_window", v.PreferredBackupWindow) 722 d.Set("license_model", v.LicenseModel) 723 d.Set("maintenance_window", v.PreferredMaintenanceWindow) 724 d.Set("publicly_accessible", v.PubliclyAccessible) 725 d.Set("multi_az", v.MultiAZ) 726 d.Set("kms_key_id", v.KmsKeyId) 727 d.Set("port", v.DbInstancePort) 728 d.Set("iam_database_authentication_enabled", v.IAMDatabaseAuthenticationEnabled) 729 if v.DBSubnetGroup != nil { 730 d.Set("db_subnet_group_name", v.DBSubnetGroup.DBSubnetGroupName) 731 } 732 733 if v.CharacterSetName != nil { 734 d.Set("character_set_name", v.CharacterSetName) 735 } 736 737 d.Set("timezone", v.Timezone) 738 739 if len(v.DBParameterGroups) > 0 { 740 d.Set("parameter_group_name", v.DBParameterGroups[0].DBParameterGroupName) 741 } 742 743 if v.Endpoint != nil { 744 d.Set("port", v.Endpoint.Port) 745 d.Set("address", v.Endpoint.Address) 746 d.Set("hosted_zone_id", v.Endpoint.HostedZoneId) 747 if v.Endpoint.Address != nil && v.Endpoint.Port != nil { 748 d.Set("endpoint", 749 fmt.Sprintf("%s:%d", *v.Endpoint.Address, *v.Endpoint.Port)) 750 } 751 } 752 753 d.Set("status", v.DBInstanceStatus) 754 d.Set("storage_encrypted", v.StorageEncrypted) 755 if v.OptionGroupMemberships != nil { 756 d.Set("option_group_name", v.OptionGroupMemberships[0].OptionGroupName) 757 } 758 759 if v.MonitoringInterval != nil { 760 d.Set("monitoring_interval", v.MonitoringInterval) 761 } 762 763 if v.MonitoringRoleArn != nil { 764 d.Set("monitoring_role_arn", v.MonitoringRoleArn) 765 } 766 767 // list tags for resource 768 // set tags 769 conn := meta.(*AWSClient).rdsconn 770 arn, err := buildRDSARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region) 771 if err != nil { 772 name := "<empty>" 773 if v.DBName != nil && *v.DBName != "" { 774 name = *v.DBName 775 } 776 log.Printf("[DEBUG] Error building ARN for DB Instance, not setting Tags for DB %s", name) 777 } else { 778 d.Set("arn", arn) 779 resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{ 780 ResourceName: aws.String(arn), 781 }) 782 783 if err != nil { 784 log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn) 785 } 786 787 var dt []*rds.Tag 788 if len(resp.TagList) > 0 { 789 dt = resp.TagList 790 } 791 d.Set("tags", tagsToMapRDS(dt)) 792 } 793 794 // Create an empty schema.Set to hold all vpc security group ids 795 ids := &schema.Set{ 796 F: schema.HashString, 797 } 798 for _, v := range v.VpcSecurityGroups { 799 ids.Add(*v.VpcSecurityGroupId) 800 } 801 d.Set("vpc_security_group_ids", ids) 802 803 // Create an empty schema.Set to hold all security group names 804 sgn := &schema.Set{ 805 F: schema.HashString, 806 } 807 for _, v := range v.DBSecurityGroups { 808 sgn.Add(*v.DBSecurityGroupName) 809 } 810 d.Set("security_group_names", sgn) 811 812 // replica things 813 814 var replicas []string 815 for _, v := range v.ReadReplicaDBInstanceIdentifiers { 816 replicas = append(replicas, *v) 817 } 818 if err := d.Set("replicas", replicas); err != nil { 819 return fmt.Errorf("[DEBUG] Error setting replicas attribute: %#v, error: %#v", replicas, err) 820 } 821 822 d.Set("replicate_source_db", v.ReadReplicaSourceDBInstanceIdentifier) 823 824 return nil 825 } 826 827 func resourceAwsDbInstanceDelete(d *schema.ResourceData, meta interface{}) error { 828 conn := meta.(*AWSClient).rdsconn 829 830 log.Printf("[DEBUG] DB Instance destroy: %v", d.Id()) 831 832 opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())} 833 834 skipFinalSnapshot := d.Get("skip_final_snapshot").(bool) 835 opts.SkipFinalSnapshot = aws.Bool(skipFinalSnapshot) 836 837 if skipFinalSnapshot == false { 838 if name, present := d.GetOk("final_snapshot_identifier"); present { 839 opts.FinalDBSnapshotIdentifier = aws.String(name.(string)) 840 } else { 841 return fmt.Errorf("DB Instance FinalSnapshotIdentifier is required when a final snapshot is required") 842 } 843 } 844 845 log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts) 846 if _, err := conn.DeleteDBInstance(&opts); err != nil { 847 return err 848 } 849 850 log.Println( 851 "[INFO] Waiting for DB Instance to be destroyed") 852 stateConf := &resource.StateChangeConf{ 853 Pending: []string{"creating", "backing-up", 854 "modifying", "deleting", "available"}, 855 Target: []string{}, 856 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 857 Timeout: d.Timeout(schema.TimeoutDelete), 858 MinTimeout: 10 * time.Second, 859 Delay: 30 * time.Second, // Wait 30 secs before starting 860 } 861 if _, err := stateConf.WaitForState(); err != nil { 862 return err 863 } 864 865 return nil 866 } 867 868 func resourceAwsDbInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 869 conn := meta.(*AWSClient).rdsconn 870 871 d.Partial(true) 872 873 req := &rds.ModifyDBInstanceInput{ 874 ApplyImmediately: aws.Bool(d.Get("apply_immediately").(bool)), 875 DBInstanceIdentifier: aws.String(d.Id()), 876 } 877 d.SetPartial("apply_immediately") 878 879 if !d.Get("apply_immediately").(bool) { 880 log.Println("[INFO] Only settings updating, instance changes will be applied in next maintenance window") 881 } 882 883 requestUpdate := false 884 if d.HasChange("allocated_storage") || d.HasChange("iops") { 885 d.SetPartial("allocated_storage") 886 d.SetPartial("iops") 887 req.Iops = aws.Int64(int64(d.Get("iops").(int))) 888 req.AllocatedStorage = aws.Int64(int64(d.Get("allocated_storage").(int))) 889 requestUpdate = true 890 } 891 if d.HasChange("allow_major_version_upgrade") { 892 d.SetPartial("allow_major_version_upgrade") 893 req.AllowMajorVersionUpgrade = aws.Bool(d.Get("allow_major_version_upgrade").(bool)) 894 requestUpdate = true 895 } 896 if d.HasChange("backup_retention_period") { 897 d.SetPartial("backup_retention_period") 898 req.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_period").(int))) 899 requestUpdate = true 900 } 901 if d.HasChange("copy_tags_to_snapshot") { 902 d.SetPartial("copy_tags_to_snapshot") 903 req.CopyTagsToSnapshot = aws.Bool(d.Get("copy_tags_to_snapshot").(bool)) 904 requestUpdate = true 905 } 906 if d.HasChange("instance_class") { 907 d.SetPartial("instance_class") 908 req.DBInstanceClass = aws.String(d.Get("instance_class").(string)) 909 requestUpdate = true 910 } 911 if d.HasChange("parameter_group_name") { 912 d.SetPartial("parameter_group_name") 913 req.DBParameterGroupName = aws.String(d.Get("parameter_group_name").(string)) 914 requestUpdate = true 915 } 916 if d.HasChange("engine_version") { 917 d.SetPartial("engine_version") 918 req.EngineVersion = aws.String(d.Get("engine_version").(string)) 919 req.AllowMajorVersionUpgrade = aws.Bool(d.Get("allow_major_version_upgrade").(bool)) 920 requestUpdate = true 921 } 922 if d.HasChange("backup_window") { 923 d.SetPartial("backup_window") 924 req.PreferredBackupWindow = aws.String(d.Get("backup_window").(string)) 925 requestUpdate = true 926 } 927 if d.HasChange("maintenance_window") { 928 d.SetPartial("maintenance_window") 929 req.PreferredMaintenanceWindow = aws.String(d.Get("maintenance_window").(string)) 930 requestUpdate = true 931 } 932 if d.HasChange("password") { 933 d.SetPartial("password") 934 req.MasterUserPassword = aws.String(d.Get("password").(string)) 935 requestUpdate = true 936 } 937 if d.HasChange("multi_az") { 938 d.SetPartial("multi_az") 939 req.MultiAZ = aws.Bool(d.Get("multi_az").(bool)) 940 requestUpdate = true 941 } 942 if d.HasChange("publicly_accessible") { 943 d.SetPartial("publicly_accessible") 944 req.PubliclyAccessible = aws.Bool(d.Get("publicly_accessible").(bool)) 945 requestUpdate = true 946 } 947 if d.HasChange("storage_type") { 948 d.SetPartial("storage_type") 949 req.StorageType = aws.String(d.Get("storage_type").(string)) 950 requestUpdate = true 951 952 if *req.StorageType == "io1" { 953 req.Iops = aws.Int64(int64(d.Get("iops").(int))) 954 } 955 } 956 if d.HasChange("auto_minor_version_upgrade") { 957 d.SetPartial("auto_minor_version_upgrade") 958 req.AutoMinorVersionUpgrade = aws.Bool(d.Get("auto_minor_version_upgrade").(bool)) 959 requestUpdate = true 960 } 961 962 if d.HasChange("monitoring_role_arn") { 963 d.SetPartial("monitoring_role_arn") 964 req.MonitoringRoleArn = aws.String(d.Get("monitoring_role_arn").(string)) 965 requestUpdate = true 966 } 967 968 if d.HasChange("monitoring_interval") { 969 d.SetPartial("monitoring_interval") 970 req.MonitoringInterval = aws.Int64(int64(d.Get("monitoring_interval").(int))) 971 requestUpdate = true 972 } 973 974 if d.HasChange("vpc_security_group_ids") { 975 if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { 976 var s []*string 977 for _, v := range attr.List() { 978 s = append(s, aws.String(v.(string))) 979 } 980 req.VpcSecurityGroupIds = s 981 } 982 requestUpdate = true 983 } 984 985 if d.HasChange("security_group_names") { 986 if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 { 987 var s []*string 988 for _, v := range attr.List() { 989 s = append(s, aws.String(v.(string))) 990 } 991 req.DBSecurityGroups = s 992 } 993 requestUpdate = true 994 } 995 996 if d.HasChange("option_group_name") { 997 d.SetPartial("option_group_name") 998 req.OptionGroupName = aws.String(d.Get("option_group_name").(string)) 999 requestUpdate = true 1000 } 1001 1002 if d.HasChange("port") { 1003 d.SetPartial("port") 1004 req.DBPortNumber = aws.Int64(int64(d.Get("port").(int))) 1005 requestUpdate = true 1006 } 1007 if d.HasChange("db_subnet_group_name") && !d.IsNewResource() { 1008 d.SetPartial("db_subnet_group_name") 1009 req.DBSubnetGroupName = aws.String(d.Get("db_subnet_group_name").(string)) 1010 requestUpdate = true 1011 } 1012 1013 if d.HasChange("iam_database_authentication_enabled") { 1014 req.EnableIAMDatabaseAuthentication = aws.Bool(d.Get("iam_database_authentication_enabled").(bool)) 1015 requestUpdate = true 1016 } 1017 1018 log.Printf("[DEBUG] Send DB Instance Modification request: %t", requestUpdate) 1019 if requestUpdate { 1020 log.Printf("[DEBUG] DB Instance Modification request: %s", req) 1021 _, err := conn.ModifyDBInstance(req) 1022 if err != nil { 1023 return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err) 1024 } 1025 1026 log.Println("[INFO] Waiting for DB Instance to be available") 1027 1028 stateConf := &resource.StateChangeConf{ 1029 Pending: []string{"creating", "backing-up", "modifying", "resetting-master-credentials", 1030 "maintenance", "renaming", "rebooting", "upgrading", "configuring-enhanced-monitoring", "moving-to-vpc"}, 1031 Target: []string{"available"}, 1032 Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta), 1033 Timeout: d.Timeout(schema.TimeoutUpdate), 1034 MinTimeout: 10 * time.Second, 1035 Delay: 30 * time.Second, // Wait 30 secs before starting 1036 } 1037 1038 // Wait, catching any errors 1039 _, dbStateErr := stateConf.WaitForState() 1040 if dbStateErr != nil { 1041 return dbStateErr 1042 } 1043 } 1044 1045 // separate request to promote a database 1046 if d.HasChange("replicate_source_db") { 1047 if d.Get("replicate_source_db").(string) == "" { 1048 // promote 1049 opts := rds.PromoteReadReplicaInput{ 1050 DBInstanceIdentifier: aws.String(d.Id()), 1051 } 1052 attr := d.Get("backup_retention_period") 1053 opts.BackupRetentionPeriod = aws.Int64(int64(attr.(int))) 1054 if attr, ok := d.GetOk("backup_window"); ok { 1055 opts.PreferredBackupWindow = aws.String(attr.(string)) 1056 } 1057 _, err := conn.PromoteReadReplica(&opts) 1058 if err != nil { 1059 return fmt.Errorf("Error promoting database: %#v", err) 1060 } 1061 d.Set("replicate_source_db", "") 1062 } else { 1063 return fmt.Errorf("cannot elect new source database for replication") 1064 } 1065 } 1066 1067 if arn, err := buildRDSARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region); err == nil { 1068 if err := setTagsRDS(conn, d, arn); err != nil { 1069 return err 1070 } else { 1071 d.SetPartial("tags") 1072 } 1073 } 1074 d.Partial(false) 1075 1076 return resourceAwsDbInstanceRead(d, meta) 1077 } 1078 1079 // resourceAwsDbInstanceRetrieve fetches DBInstance information from the AWS 1080 // API. It returns an error if there is a communication problem or unexpected 1081 // error with AWS. When the DBInstance is not found, it returns no error and a 1082 // nil pointer. 1083 func resourceAwsDbInstanceRetrieve( 1084 d *schema.ResourceData, meta interface{}) (*rds.DBInstance, error) { 1085 conn := meta.(*AWSClient).rdsconn 1086 1087 opts := rds.DescribeDBInstancesInput{ 1088 DBInstanceIdentifier: aws.String(d.Id()), 1089 } 1090 1091 log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts) 1092 1093 resp, err := conn.DescribeDBInstances(&opts) 1094 if err != nil { 1095 dbinstanceerr, ok := err.(awserr.Error) 1096 if ok && dbinstanceerr.Code() == "DBInstanceNotFound" { 1097 return nil, nil 1098 } 1099 return nil, fmt.Errorf("Error retrieving DB Instances: %s", err) 1100 } 1101 1102 if len(resp.DBInstances) != 1 || 1103 *resp.DBInstances[0].DBInstanceIdentifier != d.Id() { 1104 if err != nil { 1105 return nil, nil 1106 } 1107 } 1108 1109 return resp.DBInstances[0], nil 1110 } 1111 1112 func resourceAwsDbInstanceImport( 1113 d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { 1114 // Neither skip_final_snapshot nor final_snapshot_identifier can be fetched 1115 // from any API call, so we need to default skip_final_snapshot to true so 1116 // that final_snapshot_identifier is not required 1117 d.Set("skip_final_snapshot", true) 1118 return []*schema.ResourceData{d}, nil 1119 } 1120 1121 func resourceAwsDbInstanceStateRefreshFunc( 1122 d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { 1123 return func() (interface{}, string, error) { 1124 v, err := resourceAwsDbInstanceRetrieve(d, meta) 1125 1126 if err != nil { 1127 log.Printf("Error on retrieving DB Instance when waiting: %s", err) 1128 return nil, "", err 1129 } 1130 1131 if v == nil { 1132 return nil, "", nil 1133 } 1134 1135 if v.DBInstanceStatus != nil { 1136 log.Printf("[DEBUG] DB Instance status for instance %s: %s", d.Id(), *v.DBInstanceStatus) 1137 } 1138 1139 return v, *v.DBInstanceStatus, nil 1140 } 1141 } 1142 1143 func buildRDSARN(identifier, partition, accountid, region string) (string, error) { 1144 if partition == "" { 1145 return "", fmt.Errorf("Unable to construct RDS ARN because of missing AWS partition") 1146 } 1147 if accountid == "" { 1148 return "", fmt.Errorf("Unable to construct RDS ARN because of missing AWS Account ID") 1149 } 1150 arn := fmt.Sprintf("arn:%s:rds:%s:%s:db:%s", partition, region, accountid, identifier) 1151 return arn, nil 1152 }