github.com/econnell/terraform@v0.5.4-0.20150722160631-78eb236786a4/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/iam"
    13  	"github.com/aws/aws-sdk-go/service/rds"
    14  
    15  	"github.com/hashicorp/terraform/helper/resource"
    16  	"github.com/hashicorp/terraform/helper/schema"
    17  )
    18  
    19  func resourceAwsDbInstance() *schema.Resource {
    20  	return &schema.Resource{
    21  		Create: resourceAwsDbInstanceCreate,
    22  		Read:   resourceAwsDbInstanceRead,
    23  		Update: resourceAwsDbInstanceUpdate,
    24  		Delete: resourceAwsDbInstanceDelete,
    25  
    26  		Schema: map[string]*schema.Schema{
    27  			"name": &schema.Schema{
    28  				Type:     schema.TypeString,
    29  				Optional: true,
    30  				Computed: true,
    31  				ForceNew: true,
    32  			},
    33  
    34  			"username": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  				ForceNew: true,
    38  			},
    39  
    40  			"password": &schema.Schema{
    41  				Type:     schema.TypeString,
    42  				Required: true,
    43  			},
    44  
    45  			"engine": &schema.Schema{
    46  				Type:     schema.TypeString,
    47  				Required: true,
    48  				ForceNew: true,
    49  			},
    50  
    51  			"engine_version": &schema.Schema{
    52  				Type:     schema.TypeString,
    53  				Required: true,
    54  			},
    55  
    56  			"storage_encrypted": &schema.Schema{
    57  				Type:     schema.TypeBool,
    58  				Optional: true,
    59  				ForceNew: true,
    60  			},
    61  
    62  			"allocated_storage": &schema.Schema{
    63  				Type:     schema.TypeInt,
    64  				Required: true,
    65  			},
    66  
    67  			"storage_type": &schema.Schema{
    68  				Type:     schema.TypeString,
    69  				Optional: true,
    70  				Computed: true,
    71  			},
    72  
    73  			"identifier": &schema.Schema{
    74  				Type:     schema.TypeString,
    75  				Required: true,
    76  				ForceNew: true,
    77  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    78  					value := v.(string)
    79  					if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
    80  						errors = append(errors, fmt.Errorf(
    81  							"only lowercase alphanumeric characters and hyphens allowed in %q", k))
    82  					}
    83  					if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    84  						errors = append(errors, fmt.Errorf(
    85  							"first character of %q must be a letter", k))
    86  					}
    87  					if regexp.MustCompile(`--`).MatchString(value) {
    88  						errors = append(errors, fmt.Errorf(
    89  							"%q cannot contain two consecutive hyphens", k))
    90  					}
    91  					if regexp.MustCompile(`-$`).MatchString(value) {
    92  						errors = append(errors, fmt.Errorf(
    93  							"%q cannot end with a hyphen", k))
    94  					}
    95  					return
    96  				},
    97  			},
    98  
    99  			"instance_class": &schema.Schema{
   100  				Type:     schema.TypeString,
   101  				Required: true,
   102  			},
   103  
   104  			"availability_zone": &schema.Schema{
   105  				Type:     schema.TypeString,
   106  				Optional: true,
   107  				Computed: true,
   108  				ForceNew: true,
   109  			},
   110  
   111  			"backup_retention_period": &schema.Schema{
   112  				Type:     schema.TypeInt,
   113  				Optional: true,
   114  				Computed: true,
   115  			},
   116  
   117  			"backup_window": &schema.Schema{
   118  				Type:     schema.TypeString,
   119  				Optional: true,
   120  				Computed: true,
   121  			},
   122  
   123  			"iops": &schema.Schema{
   124  				Type:     schema.TypeInt,
   125  				Optional: true,
   126  			},
   127  
   128  			"license_model": &schema.Schema{
   129  				Type:     schema.TypeString,
   130  				Optional: true,
   131  				Computed: true,
   132  			},
   133  
   134  			"maintenance_window": &schema.Schema{
   135  				Type:     schema.TypeString,
   136  				Optional: true,
   137  				Computed: true,
   138  			},
   139  
   140  			"multi_az": &schema.Schema{
   141  				Type:     schema.TypeBool,
   142  				Optional: true,
   143  				Computed: true,
   144  			},
   145  
   146  			"port": &schema.Schema{
   147  				Type:     schema.TypeInt,
   148  				Optional: true,
   149  				Computed: true,
   150  				ForceNew: true,
   151  			},
   152  
   153  			"publicly_accessible": &schema.Schema{
   154  				Type:     schema.TypeBool,
   155  				Optional: true,
   156  				ForceNew: true,
   157  			},
   158  
   159  			"vpc_security_group_ids": &schema.Schema{
   160  				Type:     schema.TypeSet,
   161  				Optional: true,
   162  				Computed: true,
   163  				Elem:     &schema.Schema{Type: schema.TypeString},
   164  				Set:      schema.HashString,
   165  			},
   166  
   167  			"security_group_names": &schema.Schema{
   168  				Type:     schema.TypeSet,
   169  				Optional: true,
   170  				Elem:     &schema.Schema{Type: schema.TypeString},
   171  				Set:      schema.HashString,
   172  			},
   173  
   174  			"final_snapshot_identifier": &schema.Schema{
   175  				Type:     schema.TypeString,
   176  				Optional: true,
   177  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   178  					value := v.(string)
   179  					if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   180  						es = append(es, fmt.Errorf(
   181  							"only alphanumeric characters and hyphens allowed in %q", k))
   182  					}
   183  					if regexp.MustCompile(`--`).MatchString(value) {
   184  						es = append(es, fmt.Errorf("%q cannot contain two consecutive hyphens", k))
   185  					}
   186  					if regexp.MustCompile(`-$`).MatchString(value) {
   187  						es = append(es, fmt.Errorf("%q cannot end in a hyphen", k))
   188  					}
   189  					return
   190  				},
   191  			},
   192  
   193  			"db_subnet_group_name": &schema.Schema{
   194  				Type:     schema.TypeString,
   195  				Optional: true,
   196  				ForceNew: true,
   197  				Computed: true,
   198  			},
   199  
   200  			"parameter_group_name": &schema.Schema{
   201  				Type:     schema.TypeString,
   202  				Optional: true,
   203  				Computed: true,
   204  			},
   205  
   206  			"address": &schema.Schema{
   207  				Type:     schema.TypeString,
   208  				Computed: true,
   209  			},
   210  
   211  			"endpoint": &schema.Schema{
   212  				Type:     schema.TypeString,
   213  				Computed: true,
   214  			},
   215  
   216  			"status": &schema.Schema{
   217  				Type:     schema.TypeString,
   218  				Computed: true,
   219  			},
   220  
   221  			// apply_immediately is used to determine when the update modifications
   222  			// take place.
   223  			// See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
   224  			"apply_immediately": &schema.Schema{
   225  				Type:     schema.TypeBool,
   226  				Optional: true,
   227  				Computed: true,
   228  			},
   229  
   230  			"replicate_source_db": &schema.Schema{
   231  				Type:     schema.TypeString,
   232  				Optional: true,
   233  			},
   234  
   235  			"replicas": &schema.Schema{
   236  				Type:     schema.TypeList,
   237  				Computed: true,
   238  				Elem:     &schema.Schema{Type: schema.TypeString},
   239  			},
   240  
   241  			"snapshot_identifier": &schema.Schema{
   242  				Type:     schema.TypeString,
   243  				Computed: false,
   244  				Optional: true,
   245  				Elem:     &schema.Schema{Type: schema.TypeString},
   246  			},
   247  
   248  			"auto_minor_version_upgrade": &schema.Schema{
   249  				Type:     schema.TypeBool,
   250  				Computed: false,
   251  				Optional: true,
   252  			},
   253  
   254  			"tags": tagsSchema(),
   255  		},
   256  	}
   257  }
   258  
   259  func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   260  	conn := meta.(*AWSClient).rdsconn
   261  	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
   262  
   263  	if v, ok := d.GetOk("replicate_source_db"); ok {
   264  		opts := rds.CreateDBInstanceReadReplicaInput{
   265  			SourceDBInstanceIdentifier: aws.String(v.(string)),
   266  			DBInstanceClass:            aws.String(d.Get("instance_class").(string)),
   267  			DBInstanceIdentifier:       aws.String(d.Get("identifier").(string)),
   268  			Tags:                       tags,
   269  		}
   270  		if attr, ok := d.GetOk("iops"); ok {
   271  			opts.IOPS = aws.Long(int64(attr.(int)))
   272  		}
   273  
   274  		if attr, ok := d.GetOk("port"); ok {
   275  			opts.Port = aws.Long(int64(attr.(int)))
   276  		}
   277  
   278  		if attr, ok := d.GetOk("availability_zone"); ok {
   279  			opts.AvailabilityZone = aws.String(attr.(string))
   280  		}
   281  
   282  		if attr, ok := d.GetOk("publicly_accessible"); ok {
   283  			opts.PubliclyAccessible = aws.Boolean(attr.(bool))
   284  		}
   285  		_, err := conn.CreateDBInstanceReadReplica(&opts)
   286  		if err != nil {
   287  			return fmt.Errorf("Error creating DB Instance: %s", err)
   288  		}
   289  	} else if _, ok := d.GetOk("snapshot_identifier"); ok {
   290  		opts := rds.RestoreDBInstanceFromDBSnapshotInput{
   291  			DBInstanceClass:      aws.String(d.Get("instance_class").(string)),
   292  			DBInstanceIdentifier: aws.String(d.Get("identifier").(string)),
   293  			DBSnapshotIdentifier: aws.String(d.Get("snapshot_identifier").(string)),
   294  			Tags:                 tags,
   295  		}
   296  
   297  		if attr, ok := d.GetOk("auto_minor_version_upgrade"); ok {
   298  			opts.AutoMinorVersionUpgrade = aws.Boolean(attr.(bool))
   299  		}
   300  
   301  		if attr, ok := d.GetOk("availability_zone"); ok {
   302  			opts.AvailabilityZone = aws.String(attr.(string))
   303  		}
   304  
   305  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   306  			opts.DBSubnetGroupName = aws.String(attr.(string))
   307  		}
   308  
   309  		if attr, ok := d.GetOk("engine"); ok {
   310  			opts.Engine = aws.String(attr.(string))
   311  		}
   312  
   313  		if attr, ok := d.GetOk("iops"); ok {
   314  			opts.IOPS = aws.Long(int64(attr.(int)))
   315  		}
   316  
   317  		if attr, ok := d.GetOk("license_model"); ok {
   318  			opts.LicenseModel = aws.String(attr.(string))
   319  		}
   320  
   321  		if attr, ok := d.GetOk("multi_az"); ok {
   322  			opts.MultiAZ = aws.Boolean(attr.(bool))
   323  		}
   324  
   325  		if attr, ok := d.GetOk("option_group_name"); ok {
   326  			opts.OptionGroupName = aws.String(attr.(string))
   327  		}
   328  
   329  		if attr, ok := d.GetOk("port"); ok {
   330  			opts.Port = aws.Long(int64(attr.(int)))
   331  		}
   332  
   333  		if attr, ok := d.GetOk("publicly_accessible"); ok {
   334  			opts.PubliclyAccessible = aws.Boolean(attr.(bool))
   335  		}
   336  
   337  		if attr, ok := d.GetOk("tde_credential_arn"); ok {
   338  			opts.TDECredentialARN = aws.String(attr.(string))
   339  		}
   340  
   341  		if attr, ok := d.GetOk("storage_type"); ok {
   342  			opts.StorageType = aws.String(attr.(string))
   343  		}
   344  
   345  		_, err := conn.RestoreDBInstanceFromDBSnapshot(&opts)
   346  		if err != nil {
   347  			return fmt.Errorf("Error creating DB Instance: %s", err)
   348  		}
   349  	} else {
   350  		opts := rds.CreateDBInstanceInput{
   351  			AllocatedStorage:     aws.Long(int64(d.Get("allocated_storage").(int))),
   352  			DBName:               aws.String(d.Get("name").(string)),
   353  			DBInstanceClass:      aws.String(d.Get("instance_class").(string)),
   354  			DBInstanceIdentifier: aws.String(d.Get("identifier").(string)),
   355  			MasterUsername:       aws.String(d.Get("username").(string)),
   356  			MasterUserPassword:   aws.String(d.Get("password").(string)),
   357  			Engine:               aws.String(d.Get("engine").(string)),
   358  			EngineVersion:        aws.String(d.Get("engine_version").(string)),
   359  			StorageEncrypted:     aws.Boolean(d.Get("storage_encrypted").(bool)),
   360  			Tags:                 tags,
   361  		}
   362  
   363  		attr := d.Get("backup_retention_period")
   364  		opts.BackupRetentionPeriod = aws.Long(int64(attr.(int)))
   365  		if attr, ok := d.GetOk("multi_az"); ok {
   366  			opts.MultiAZ = aws.Boolean(attr.(bool))
   367  		}
   368  
   369  		if attr, ok := d.GetOk("maintenance_window"); ok {
   370  			opts.PreferredMaintenanceWindow = aws.String(attr.(string))
   371  		}
   372  
   373  		if attr, ok := d.GetOk("backup_window"); ok {
   374  			opts.PreferredBackupWindow = aws.String(attr.(string))
   375  		}
   376  
   377  		if attr, ok := d.GetOk("license_model"); ok {
   378  			opts.LicenseModel = aws.String(attr.(string))
   379  		}
   380  		if attr, ok := d.GetOk("parameter_group_name"); ok {
   381  			opts.DBParameterGroupName = aws.String(attr.(string))
   382  		}
   383  
   384  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   385  			var s []*string
   386  			for _, v := range attr.List() {
   387  				s = append(s, aws.String(v.(string)))
   388  			}
   389  			opts.VPCSecurityGroupIDs = s
   390  		}
   391  
   392  		if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 {
   393  			var s []*string
   394  			for _, v := range attr.List() {
   395  				s = append(s, aws.String(v.(string)))
   396  			}
   397  			opts.DBSecurityGroups = s
   398  		}
   399  		if attr, ok := d.GetOk("storage_type"); ok {
   400  			opts.StorageType = aws.String(attr.(string))
   401  		}
   402  
   403  		if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   404  			opts.DBSubnetGroupName = aws.String(attr.(string))
   405  		}
   406  
   407  		if attr, ok := d.GetOk("iops"); ok {
   408  			opts.IOPS = aws.Long(int64(attr.(int)))
   409  		}
   410  
   411  		if attr, ok := d.GetOk("port"); ok {
   412  			opts.Port = aws.Long(int64(attr.(int)))
   413  		}
   414  
   415  		if attr, ok := d.GetOk("availability_zone"); ok {
   416  			opts.AvailabilityZone = aws.String(attr.(string))
   417  		}
   418  
   419  		if attr, ok := d.GetOk("publicly_accessible"); ok {
   420  			opts.PubliclyAccessible = aws.Boolean(attr.(bool))
   421  		}
   422  
   423  		log.Printf("[DEBUG] DB Instance create configuration: %#v", opts)
   424  		var err error
   425  		_, err = conn.CreateDBInstance(&opts)
   426  		if err != nil {
   427  			return fmt.Errorf("Error creating DB Instance: %s", err)
   428  		}
   429  	}
   430  
   431  	d.SetId(d.Get("identifier").(string))
   432  
   433  	log.Printf("[INFO] DB Instance ID: %s", d.Id())
   434  
   435  	log.Println(
   436  		"[INFO] Waiting for DB Instance to be available")
   437  
   438  	stateConf := &resource.StateChangeConf{
   439  		Pending:    []string{"creating", "backing-up", "modifying"},
   440  		Target:     "available",
   441  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   442  		Timeout:    40 * time.Minute,
   443  		MinTimeout: 10 * time.Second,
   444  		Delay:      30 * time.Second, // Wait 30 secs before starting
   445  	}
   446  
   447  	// Wait, catching any errors
   448  	_, err := stateConf.WaitForState()
   449  	if err != nil {
   450  		return err
   451  	}
   452  
   453  	return resourceAwsDbInstanceRead(d, meta)
   454  }
   455  
   456  func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error {
   457  	v, err := resourceAwsDbInstanceRetrieve(d, meta)
   458  
   459  	if err != nil {
   460  		return err
   461  	}
   462  	if v == nil {
   463  		d.SetId("")
   464  		return nil
   465  	}
   466  
   467  	d.Set("name", v.DBName)
   468  	d.Set("username", v.MasterUsername)
   469  	d.Set("engine", v.Engine)
   470  	d.Set("engine_version", v.EngineVersion)
   471  	d.Set("allocated_storage", v.AllocatedStorage)
   472  	d.Set("storage_type", v.StorageType)
   473  	d.Set("instance_class", v.DBInstanceClass)
   474  	d.Set("availability_zone", v.AvailabilityZone)
   475  	d.Set("backup_retention_period", v.BackupRetentionPeriod)
   476  	d.Set("backup_window", v.PreferredBackupWindow)
   477  	d.Set("license_model", v.LicenseModel)
   478  	d.Set("maintenance_window", v.PreferredMaintenanceWindow)
   479  	d.Set("multi_az", v.MultiAZ)
   480  	if v.DBSubnetGroup != nil {
   481  		d.Set("db_subnet_group_name", v.DBSubnetGroup.DBSubnetGroupName)
   482  	}
   483  
   484  	if len(v.DBParameterGroups) > 0 {
   485  		d.Set("parameter_group_name", v.DBParameterGroups[0].DBParameterGroupName)
   486  	}
   487  
   488  	if v.Endpoint != nil {
   489  		d.Set("port", v.Endpoint.Port)
   490  		d.Set("address", v.Endpoint.Address)
   491  
   492  		if v.Endpoint.Address != nil && v.Endpoint.Port != nil {
   493  			d.Set("endpoint",
   494  				fmt.Sprintf("%s:%d", *v.Endpoint.Address, *v.Endpoint.Port))
   495  		}
   496  	}
   497  
   498  	d.Set("status", v.DBInstanceStatus)
   499  	d.Set("storage_encrypted", v.StorageEncrypted)
   500  
   501  	// list tags for resource
   502  	// set tags
   503  	conn := meta.(*AWSClient).rdsconn
   504  	arn, err := buildRDSARN(d, meta)
   505  	if err != nil {
   506  		name := "<empty>"
   507  		if v.DBName != nil && *v.DBName != "" {
   508  			name = *v.DBName
   509  		}
   510  
   511  		log.Printf("[DEBUG] Error building ARN for DB Instance, not setting Tags for DB %s", name)
   512  	} else {
   513  		resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{
   514  			ResourceName: aws.String(arn),
   515  		})
   516  
   517  		if err != nil {
   518  			log.Printf("[DEBUG] Error retreiving tags for ARN: %s", arn)
   519  		}
   520  
   521  		var dt []*rds.Tag
   522  		if len(resp.TagList) > 0 {
   523  			dt = resp.TagList
   524  		}
   525  		d.Set("tags", tagsToMapRDS(dt))
   526  	}
   527  
   528  	// Create an empty schema.Set to hold all vpc security group ids
   529  	ids := &schema.Set{
   530  		F: schema.HashString,
   531  	}
   532  	for _, v := range v.VPCSecurityGroups {
   533  		ids.Add(*v.VPCSecurityGroupID)
   534  	}
   535  	d.Set("vpc_security_group_ids", ids)
   536  
   537  	// Create an empty schema.Set to hold all security group names
   538  	sgn := &schema.Set{
   539  		F: schema.HashString,
   540  	}
   541  	for _, v := range v.DBSecurityGroups {
   542  		sgn.Add(*v.DBSecurityGroupName)
   543  	}
   544  	d.Set("security_group_names", sgn)
   545  
   546  	// replica things
   547  
   548  	var replicas []string
   549  	for _, v := range v.ReadReplicaDBInstanceIdentifiers {
   550  		replicas = append(replicas, *v)
   551  	}
   552  	if err := d.Set("replicas", replicas); err != nil {
   553  		return fmt.Errorf("[DEBUG] Error setting replicas attribute: %#v, error: %#v", replicas, err)
   554  	}
   555  
   556  	d.Set("replicate_source_db", v.ReadReplicaSourceDBInstanceIdentifier)
   557  
   558  	return nil
   559  }
   560  
   561  func resourceAwsDbInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   562  	conn := meta.(*AWSClient).rdsconn
   563  
   564  	log.Printf("[DEBUG] DB Instance destroy: %v", d.Id())
   565  
   566  	opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())}
   567  
   568  	finalSnapshot := d.Get("final_snapshot_identifier").(string)
   569  	if finalSnapshot == "" {
   570  		opts.SkipFinalSnapshot = aws.Boolean(true)
   571  	} else {
   572  		opts.FinalDBSnapshotIdentifier = aws.String(finalSnapshot)
   573  	}
   574  
   575  	log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts)
   576  	if _, err := conn.DeleteDBInstance(&opts); err != nil {
   577  		return err
   578  	}
   579  
   580  	log.Println(
   581  		"[INFO] Waiting for DB Instance to be destroyed")
   582  	stateConf := &resource.StateChangeConf{
   583  		Pending: []string{"creating", "backing-up",
   584  			"modifying", "deleting", "available"},
   585  		Target:     "",
   586  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   587  		Timeout:    40 * time.Minute,
   588  		MinTimeout: 10 * time.Second,
   589  		Delay:      30 * time.Second, // Wait 30 secs before starting
   590  	}
   591  	if _, err := stateConf.WaitForState(); err != nil {
   592  		return err
   593  	}
   594  
   595  	return nil
   596  }
   597  
   598  func resourceAwsDbInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   599  	conn := meta.(*AWSClient).rdsconn
   600  
   601  	d.Partial(true)
   602  
   603  	req := &rds.ModifyDBInstanceInput{
   604  		ApplyImmediately:     aws.Boolean(d.Get("apply_immediately").(bool)),
   605  		DBInstanceIdentifier: aws.String(d.Id()),
   606  	}
   607  	d.SetPartial("apply_immediately")
   608  
   609  	requestUpdate := false
   610  	if d.HasChange("allocated_storage") {
   611  		d.SetPartial("allocated_storage")
   612  		req.AllocatedStorage = aws.Long(int64(d.Get("allocated_storage").(int)))
   613  		requestUpdate = true
   614  	}
   615  	if d.HasChange("backup_retention_period") {
   616  		d.SetPartial("backup_retention_period")
   617  		req.BackupRetentionPeriod = aws.Long(int64(d.Get("backup_retention_period").(int)))
   618  		requestUpdate = true
   619  	}
   620  	if d.HasChange("instance_class") {
   621  		d.SetPartial("instance_class")
   622  		req.DBInstanceClass = aws.String(d.Get("instance_class").(string))
   623  		requestUpdate = true
   624  	}
   625  	if d.HasChange("parameter_group_name") {
   626  		d.SetPartial("parameter_group_name")
   627  		req.DBParameterGroupName = aws.String(d.Get("parameter_group_name").(string))
   628  		requestUpdate = true
   629  	}
   630  	if d.HasChange("engine_version") {
   631  		d.SetPartial("engine_version")
   632  		req.EngineVersion = aws.String(d.Get("engine_version").(string))
   633  		requestUpdate = true
   634  	}
   635  	if d.HasChange("iops") {
   636  		d.SetPartial("iops")
   637  		req.IOPS = aws.Long(int64(d.Get("iops").(int)))
   638  		requestUpdate = true
   639  	}
   640  	if d.HasChange("backup_window") {
   641  		d.SetPartial("backup_window")
   642  		req.PreferredBackupWindow = aws.String(d.Get("backup_window").(string))
   643  		requestUpdate = true
   644  	}
   645  	if d.HasChange("maintenance_window") {
   646  		d.SetPartial("maintenance_window")
   647  		req.PreferredMaintenanceWindow = aws.String(d.Get("maintenance_window").(string))
   648  		requestUpdate = true
   649  	}
   650  	if d.HasChange("password") {
   651  		d.SetPartial("password")
   652  		req.MasterUserPassword = aws.String(d.Get("password").(string))
   653  		requestUpdate = true
   654  	}
   655  	if d.HasChange("multi_az") {
   656  		d.SetPartial("multi_az")
   657  		req.MultiAZ = aws.Boolean(d.Get("multi_az").(bool))
   658  		requestUpdate = true
   659  	}
   660  	if d.HasChange("storage_type") {
   661  		d.SetPartial("storage_type")
   662  		req.StorageType = aws.String(d.Get("storage_type").(string))
   663  		requestUpdate = true
   664  	}
   665  
   666  	if d.HasChange("vpc_security_group_ids") {
   667  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   668  			var s []*string
   669  			for _, v := range attr.List() {
   670  				s = append(s, aws.String(v.(string)))
   671  			}
   672  			req.VPCSecurityGroupIDs = s
   673  		}
   674  		requestUpdate = true
   675  	}
   676  
   677  	if d.HasChange("vpc_security_group_ids") {
   678  		if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 {
   679  			var s []*string
   680  			for _, v := range attr.List() {
   681  				s = append(s, aws.String(v.(string)))
   682  			}
   683  			req.DBSecurityGroups = s
   684  		}
   685  		requestUpdate = true
   686  	}
   687  
   688  	log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate)
   689  	if requestUpdate {
   690  		log.Printf("[DEBUG] DB Instance Modification request: %#v", req)
   691  		_, err := conn.ModifyDBInstance(req)
   692  		if err != nil {
   693  			return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err)
   694  		}
   695  	}
   696  
   697  	// seperate request to promote a database
   698  	if d.HasChange("replicate_source_db") {
   699  		if d.Get("replicate_source_db").(string) == "" {
   700  			// promote
   701  			opts := rds.PromoteReadReplicaInput{
   702  				DBInstanceIdentifier: aws.String(d.Id()),
   703  			}
   704  			attr := d.Get("backup_retention_period")
   705  			opts.BackupRetentionPeriod = aws.Long(int64(attr.(int)))
   706  			if attr, ok := d.GetOk("backup_window"); ok {
   707  				opts.PreferredBackupWindow = aws.String(attr.(string))
   708  			}
   709  			_, err := conn.PromoteReadReplica(&opts)
   710  			if err != nil {
   711  				return fmt.Errorf("Error promoting database: %#v", err)
   712  			}
   713  			d.Set("replicate_source_db", "")
   714  		} else {
   715  			return fmt.Errorf("cannot elect new source database for replication")
   716  		}
   717  	}
   718  
   719  	if arn, err := buildRDSARN(d, meta); err == nil {
   720  		if err := setTagsRDS(conn, d, arn); err != nil {
   721  			return err
   722  		} else {
   723  			d.SetPartial("tags")
   724  		}
   725  	}
   726  	d.Partial(false)
   727  	return resourceAwsDbInstanceRead(d, meta)
   728  }
   729  
   730  func resourceAwsDbInstanceRetrieve(
   731  	d *schema.ResourceData, meta interface{}) (*rds.DBInstance, error) {
   732  	conn := meta.(*AWSClient).rdsconn
   733  
   734  	opts := rds.DescribeDBInstancesInput{
   735  		DBInstanceIdentifier: aws.String(d.Id()),
   736  	}
   737  
   738  	log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts)
   739  
   740  	resp, err := conn.DescribeDBInstances(&opts)
   741  
   742  	if err != nil {
   743  		dbinstanceerr, ok := err.(awserr.Error)
   744  		if ok && dbinstanceerr.Code() == "DBInstanceNotFound" {
   745  			return nil, nil
   746  		}
   747  		return nil, fmt.Errorf("Error retrieving DB Instances: %s", err)
   748  	}
   749  
   750  	if len(resp.DBInstances) != 1 ||
   751  		*resp.DBInstances[0].DBInstanceIdentifier != d.Id() {
   752  		if err != nil {
   753  			return nil, nil
   754  		}
   755  	}
   756  
   757  	return resp.DBInstances[0], nil
   758  }
   759  
   760  func resourceAwsDbInstanceStateRefreshFunc(
   761  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   762  	return func() (interface{}, string, error) {
   763  		v, err := resourceAwsDbInstanceRetrieve(d, meta)
   764  
   765  		if err != nil {
   766  			log.Printf("Error on retrieving DB Instance when waiting: %s", err)
   767  			return nil, "", err
   768  		}
   769  
   770  		if v == nil {
   771  			return nil, "", nil
   772  		}
   773  
   774  		if v.DBInstanceStatus != nil {
   775  			log.Printf("[DEBUG] DB Instance status for instance %s: %s", d.Id(), *v.DBInstanceStatus)
   776  		}
   777  
   778  		return v, *v.DBInstanceStatus, nil
   779  	}
   780  }
   781  
   782  func buildRDSARN(d *schema.ResourceData, meta interface{}) (string, error) {
   783  	iamconn := meta.(*AWSClient).iamconn
   784  	region := meta.(*AWSClient).region
   785  	// An zero value GetUserInput{} defers to the currently logged in user
   786  	resp, err := iamconn.GetUser(&iam.GetUserInput{})
   787  	if err != nil {
   788  		return "", err
   789  	}
   790  	userARN := *resp.User.ARN
   791  	accountID := strings.Split(userARN, ":")[4]
   792  	arn := fmt.Sprintf("arn:aws:rds:%s:%s:db:%s", region, accountID, d.Id())
   793  	return arn, nil
   794  }