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