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