github.com/paulmey/terraform@v0.5.2-0.20150519145237-046e9b4c884d/builtin/providers/aws/resource_aws_db_instance.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/awslabs/aws-sdk-go/aws"
    10  	"github.com/awslabs/aws-sdk-go/service/iam"
    11  	"github.com/awslabs/aws-sdk-go/service/rds"
    12  
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsDbInstance() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsDbInstanceCreate,
    20  		Read:   resourceAwsDbInstanceRead,
    21  		Update: resourceAwsDbInstanceUpdate,
    22  		Delete: resourceAwsDbInstanceDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Optional: true,
    28  				ForceNew: true,
    29  			},
    30  
    31  			"username": &schema.Schema{
    32  				Type:     schema.TypeString,
    33  				Required: true,
    34  				ForceNew: true,
    35  			},
    36  
    37  			"password": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Required: true,
    40  			},
    41  
    42  			"engine": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Required: true,
    45  				ForceNew: true,
    46  			},
    47  
    48  			"engine_version": &schema.Schema{
    49  				Type:     schema.TypeString,
    50  				Required: true,
    51  			},
    52  
    53  			"storage_encrypted": &schema.Schema{
    54  				Type:     schema.TypeBool,
    55  				Optional: true,
    56  				ForceNew: true,
    57  			},
    58  
    59  			"allocated_storage": &schema.Schema{
    60  				Type:     schema.TypeInt,
    61  				Required: true,
    62  			},
    63  
    64  			"storage_type": &schema.Schema{
    65  				Type:     schema.TypeString,
    66  				Optional: true,
    67  				Computed: true,
    68  			},
    69  
    70  			"identifier": &schema.Schema{
    71  				Type:     schema.TypeString,
    72  				Required: true,
    73  				ForceNew: true,
    74  			},
    75  
    76  			"instance_class": &schema.Schema{
    77  				Type:     schema.TypeString,
    78  				Required: true,
    79  			},
    80  
    81  			"availability_zone": &schema.Schema{
    82  				Type:     schema.TypeString,
    83  				Optional: true,
    84  				Computed: true,
    85  				ForceNew: true,
    86  			},
    87  
    88  			"backup_retention_period": &schema.Schema{
    89  				Type:     schema.TypeInt,
    90  				Optional: true,
    91  				Default:  1,
    92  			},
    93  
    94  			"backup_window": &schema.Schema{
    95  				Type:     schema.TypeString,
    96  				Optional: true,
    97  				Computed: true,
    98  			},
    99  
   100  			"iops": &schema.Schema{
   101  				Type:     schema.TypeInt,
   102  				Optional: true,
   103  			},
   104  
   105  			"license_model": &schema.Schema{
   106  				Type:     schema.TypeString,
   107  				Optional: true,
   108  				Computed: true,
   109  			},
   110  
   111  			"maintenance_window": &schema.Schema{
   112  				Type:     schema.TypeString,
   113  				Optional: true,
   114  				Computed: true,
   115  			},
   116  
   117  			"multi_az": &schema.Schema{
   118  				Type:     schema.TypeBool,
   119  				Optional: true,
   120  				Computed: true,
   121  			},
   122  
   123  			"port": &schema.Schema{
   124  				Type:     schema.TypeInt,
   125  				Optional: true,
   126  				Computed: true,
   127  				ForceNew: true,
   128  			},
   129  
   130  			"publicly_accessible": &schema.Schema{
   131  				Type:     schema.TypeBool,
   132  				Optional: true,
   133  				ForceNew: true,
   134  			},
   135  
   136  			"vpc_security_group_ids": &schema.Schema{
   137  				Type:     schema.TypeSet,
   138  				Optional: true,
   139  				Computed: true,
   140  				Elem:     &schema.Schema{Type: schema.TypeString},
   141  				Set:      schema.HashString,
   142  			},
   143  
   144  			"security_group_names": &schema.Schema{
   145  				Type:     schema.TypeSet,
   146  				Optional: true,
   147  				Elem:     &schema.Schema{Type: schema.TypeString},
   148  				Set:      schema.HashString,
   149  			},
   150  
   151  			"final_snapshot_identifier": &schema.Schema{
   152  				Type:     schema.TypeString,
   153  				Optional: true,
   154  			},
   155  
   156  			"db_subnet_group_name": &schema.Schema{
   157  				Type:     schema.TypeString,
   158  				Optional: true,
   159  				ForceNew: true,
   160  				Computed: true,
   161  			},
   162  
   163  			"parameter_group_name": &schema.Schema{
   164  				Type:     schema.TypeString,
   165  				Optional: true,
   166  				Computed: true,
   167  			},
   168  
   169  			"address": &schema.Schema{
   170  				Type:     schema.TypeString,
   171  				Computed: true,
   172  			},
   173  
   174  			"endpoint": &schema.Schema{
   175  				Type:     schema.TypeString,
   176  				Computed: true,
   177  			},
   178  
   179  			"status": &schema.Schema{
   180  				Type:     schema.TypeString,
   181  				Computed: true,
   182  			},
   183  
   184  			// apply_immediately is used to determine when the update modifications
   185  			// take place.
   186  			// See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
   187  			"apply_immediately": &schema.Schema{
   188  				Type:     schema.TypeBool,
   189  				Optional: true,
   190  				Computed: true,
   191  			},
   192  
   193  			"tags": tagsSchema(),
   194  		},
   195  	}
   196  }
   197  
   198  func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   199  	conn := meta.(*AWSClient).rdsconn
   200  	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
   201  	opts := rds.CreateDBInstanceInput{
   202  		AllocatedStorage:     aws.Long(int64(d.Get("allocated_storage").(int))),
   203  		DBInstanceClass:      aws.String(d.Get("instance_class").(string)),
   204  		DBInstanceIdentifier: aws.String(d.Get("identifier").(string)),
   205  		DBName:               aws.String(d.Get("name").(string)),
   206  		MasterUsername:       aws.String(d.Get("username").(string)),
   207  		MasterUserPassword:   aws.String(d.Get("password").(string)),
   208  		Engine:               aws.String(d.Get("engine").(string)),
   209  		EngineVersion:        aws.String(d.Get("engine_version").(string)),
   210  		StorageEncrypted:     aws.Boolean(d.Get("storage_encrypted").(bool)),
   211  		Tags:                 tags,
   212  	}
   213  
   214  	if attr, ok := d.GetOk("storage_type"); ok {
   215  		opts.StorageType = aws.String(attr.(string))
   216  	}
   217  
   218  	attr := d.Get("backup_retention_period")
   219  	opts.BackupRetentionPeriod = aws.Long(int64(attr.(int)))
   220  
   221  	if attr, ok := d.GetOk("iops"); ok {
   222  		opts.IOPS = aws.Long(int64(attr.(int)))
   223  	}
   224  
   225  	if attr, ok := d.GetOk("port"); ok {
   226  		opts.Port = aws.Long(int64(attr.(int)))
   227  	}
   228  
   229  	if attr, ok := d.GetOk("multi_az"); ok {
   230  		opts.MultiAZ = aws.Boolean(attr.(bool))
   231  	}
   232  
   233  	if attr, ok := d.GetOk("availability_zone"); ok {
   234  		opts.AvailabilityZone = aws.String(attr.(string))
   235  	}
   236  
   237  	if attr, ok := d.GetOk("license_model"); ok {
   238  		opts.LicenseModel = aws.String(attr.(string))
   239  	}
   240  
   241  	if attr, ok := d.GetOk("maintenance_window"); ok {
   242  		opts.PreferredMaintenanceWindow = aws.String(attr.(string))
   243  	}
   244  
   245  	if attr, ok := d.GetOk("backup_window"); ok {
   246  		opts.PreferredBackupWindow = aws.String(attr.(string))
   247  	}
   248  
   249  	if attr, ok := d.GetOk("publicly_accessible"); ok {
   250  		opts.PubliclyAccessible = aws.Boolean(attr.(bool))
   251  	}
   252  
   253  	if attr, ok := d.GetOk("db_subnet_group_name"); ok {
   254  		opts.DBSubnetGroupName = aws.String(attr.(string))
   255  	}
   256  
   257  	if attr, ok := d.GetOk("parameter_group_name"); ok {
   258  		opts.DBParameterGroupName = aws.String(attr.(string))
   259  	}
   260  
   261  	if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   262  		var s []*string
   263  		for _, v := range attr.List() {
   264  			s = append(s, aws.String(v.(string)))
   265  		}
   266  		opts.VPCSecurityGroupIDs = s
   267  	}
   268  
   269  	if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 {
   270  		var s []*string
   271  		for _, v := range attr.List() {
   272  			s = append(s, aws.String(v.(string)))
   273  		}
   274  		opts.DBSecurityGroups = s
   275  	}
   276  
   277  	log.Printf("[DEBUG] DB Instance create configuration: %#v", opts)
   278  	_, err := conn.CreateDBInstance(&opts)
   279  	if err != nil {
   280  		return fmt.Errorf("Error creating DB Instance: %s", err)
   281  	}
   282  
   283  	d.SetId(d.Get("identifier").(string))
   284  
   285  	log.Printf("[INFO] DB Instance ID: %s", d.Id())
   286  
   287  	log.Println(
   288  		"[INFO] Waiting for DB Instance to be available")
   289  
   290  	stateConf := &resource.StateChangeConf{
   291  		Pending:    []string{"creating", "backing-up", "modifying"},
   292  		Target:     "available",
   293  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   294  		Timeout:    40 * time.Minute,
   295  		MinTimeout: 10 * time.Second,
   296  		Delay:      30 * time.Second, // Wait 30 secs before starting
   297  	}
   298  
   299  	// Wait, catching any errors
   300  	_, err = stateConf.WaitForState()
   301  	if err != nil {
   302  		return err
   303  	}
   304  
   305  	return resourceAwsDbInstanceRead(d, meta)
   306  }
   307  
   308  func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error {
   309  	v, err := resourceAwsDbInstanceRetrieve(d, meta)
   310  
   311  	if err != nil {
   312  		return err
   313  	}
   314  	if v == nil {
   315  		d.SetId("")
   316  		return nil
   317  	}
   318  
   319  	d.Set("name", v.DBName)
   320  	d.Set("username", v.MasterUsername)
   321  	d.Set("engine", v.Engine)
   322  	d.Set("engine_version", v.EngineVersion)
   323  	d.Set("allocated_storage", v.AllocatedStorage)
   324  	d.Set("storage_type", v.StorageType)
   325  	d.Set("instance_class", v.DBInstanceClass)
   326  	d.Set("availability_zone", v.AvailabilityZone)
   327  	d.Set("backup_retention_period", v.BackupRetentionPeriod)
   328  	d.Set("backup_window", v.PreferredBackupWindow)
   329  	d.Set("license_model", v.LicenseModel)
   330  	d.Set("maintenance_window", v.PreferredMaintenanceWindow)
   331  	d.Set("multi_az", v.MultiAZ)
   332  	if v.DBSubnetGroup != nil {
   333  		d.Set("db_subnet_group_name", v.DBSubnetGroup.DBSubnetGroupName)
   334  	}
   335  
   336  	if len(v.DBParameterGroups) > 0 {
   337  		d.Set("parameter_group_name", v.DBParameterGroups[0].DBParameterGroupName)
   338  	}
   339  
   340  	if v.Endpoint != nil {
   341  		d.Set("port", v.Endpoint.Port)
   342  		d.Set("address", v.Endpoint.Address)
   343  
   344  		if v.Endpoint.Address != nil && v.Endpoint.Port != nil {
   345  			d.Set("endpoint",
   346  				fmt.Sprintf("%s:%d", *v.Endpoint.Address, *v.Endpoint.Port))
   347  		}
   348  	}
   349  
   350  	d.Set("status", v.DBInstanceStatus)
   351  	d.Set("storage_encrypted", v.StorageEncrypted)
   352  
   353  	// list tags for resource
   354  	// set tags
   355  	conn := meta.(*AWSClient).rdsconn
   356  	arn, err := buildRDSARN(d, meta)
   357  	if err != nil {
   358  		name := "<empty>"
   359  		if v.DBName != nil && *v.DBName != "" {
   360  			name = *v.DBName
   361  		}
   362  
   363  		log.Printf("[DEBUG] Error building ARN for DB Instance, not setting Tags for DB %s", name)
   364  	} else {
   365  		resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{
   366  			ResourceName: aws.String(arn),
   367  		})
   368  
   369  		if err != nil {
   370  			log.Printf("[DEBUG] Error retreiving tags for ARN: %s", arn)
   371  		}
   372  
   373  		var dt []*rds.Tag
   374  		if len(resp.TagList) > 0 {
   375  			dt = resp.TagList
   376  		}
   377  		d.Set("tags", tagsToMapRDS(dt))
   378  	}
   379  
   380  	// Create an empty schema.Set to hold all vpc security group ids
   381  	ids := &schema.Set{
   382  		F: schema.HashString,
   383  	}
   384  	for _, v := range v.VPCSecurityGroups {
   385  		ids.Add(*v.VPCSecurityGroupID)
   386  	}
   387  	d.Set("vpc_security_group_ids", ids)
   388  
   389  	// Create an empty schema.Set to hold all security group names
   390  	sgn := &schema.Set{
   391  		F: schema.HashString,
   392  	}
   393  	for _, v := range v.DBSecurityGroups {
   394  		sgn.Add(*v.DBSecurityGroupName)
   395  	}
   396  	d.Set("security_group_names", sgn)
   397  
   398  	return nil
   399  }
   400  
   401  func resourceAwsDbInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   402  	conn := meta.(*AWSClient).rdsconn
   403  
   404  	log.Printf("[DEBUG] DB Instance destroy: %v", d.Id())
   405  
   406  	opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())}
   407  
   408  	finalSnapshot := d.Get("final_snapshot_identifier").(string)
   409  	if finalSnapshot == "" {
   410  		opts.SkipFinalSnapshot = aws.Boolean(true)
   411  	} else {
   412  		opts.FinalDBSnapshotIdentifier = aws.String(finalSnapshot)
   413  	}
   414  
   415  	log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts)
   416  	if _, err := conn.DeleteDBInstance(&opts); err != nil {
   417  		return err
   418  	}
   419  
   420  	log.Println(
   421  		"[INFO] Waiting for DB Instance to be destroyed")
   422  	stateConf := &resource.StateChangeConf{
   423  		Pending: []string{"creating", "backing-up",
   424  			"modifying", "deleting", "available"},
   425  		Target:     "",
   426  		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
   427  		Timeout:    40 * time.Minute,
   428  		MinTimeout: 10 * time.Second,
   429  		Delay:      30 * time.Second, // Wait 30 secs before starting
   430  	}
   431  	if _, err := stateConf.WaitForState(); err != nil {
   432  		return err
   433  	}
   434  
   435  	return nil
   436  }
   437  
   438  func resourceAwsDbInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   439  	conn := meta.(*AWSClient).rdsconn
   440  
   441  	d.Partial(true)
   442  
   443  	req := &rds.ModifyDBInstanceInput{
   444  		ApplyImmediately:     aws.Boolean(d.Get("apply_immediately").(bool)),
   445  		DBInstanceIdentifier: aws.String(d.Id()),
   446  	}
   447  	d.SetPartial("apply_immediately")
   448  
   449  	requestUpdate := false
   450  	if d.HasChange("allocated_storage") {
   451  		d.SetPartial("allocated_storage")
   452  		req.AllocatedStorage = aws.Long(int64(d.Get("allocated_storage").(int)))
   453  		requestUpdate = true
   454  	}
   455  	if d.HasChange("backup_retention_period") {
   456  		d.SetPartial("backup_retention_period")
   457  		req.BackupRetentionPeriod = aws.Long(int64(d.Get("backup_retention_period").(int)))
   458  		requestUpdate = true
   459  	}
   460  	if d.HasChange("instance_class") {
   461  		d.SetPartial("instance_class")
   462  		req.DBInstanceClass = aws.String(d.Get("instance_class").(string))
   463  		requestUpdate = true
   464  	}
   465  	if d.HasChange("parameter_group_name") {
   466  		d.SetPartial("parameter_group_name")
   467  		req.DBParameterGroupName = aws.String(d.Get("parameter_group_name").(string))
   468  		requestUpdate = true
   469  	}
   470  	if d.HasChange("engine_version") {
   471  		d.SetPartial("engine_version")
   472  		req.EngineVersion = aws.String(d.Get("engine_version").(string))
   473  		requestUpdate = true
   474  	}
   475  	if d.HasChange("iops") {
   476  		d.SetPartial("iops")
   477  		req.IOPS = aws.Long(int64(d.Get("iops").(int)))
   478  		requestUpdate = true
   479  	}
   480  	if d.HasChange("backup_window") {
   481  		d.SetPartial("backup_window")
   482  		req.PreferredBackupWindow = aws.String(d.Get("backup_window").(string))
   483  		requestUpdate = true
   484  	}
   485  	if d.HasChange("maintenance_window") {
   486  		d.SetPartial("maintenance_window")
   487  		req.PreferredMaintenanceWindow = aws.String(d.Get("maintenance_window").(string))
   488  		requestUpdate = true
   489  	}
   490  	if d.HasChange("password") {
   491  		d.SetPartial("password")
   492  		req.MasterUserPassword = aws.String(d.Get("password").(string))
   493  		requestUpdate = true
   494  	}
   495  	if d.HasChange("multi_az") {
   496  		d.SetPartial("multi_az")
   497  		req.MultiAZ = aws.Boolean(d.Get("multi_az").(bool))
   498  		requestUpdate = true
   499  	}
   500  	if d.HasChange("storage_type") {
   501  		d.SetPartial("storage_type")
   502  		req.StorageType = aws.String(d.Get("storage_type").(string))
   503  		requestUpdate = true
   504  	}
   505  
   506  	if d.HasChange("vpc_security_group_ids") {
   507  		if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
   508  			var s []*string
   509  			for _, v := range attr.List() {
   510  				s = append(s, aws.String(v.(string)))
   511  			}
   512  			req.VPCSecurityGroupIDs = s
   513  		}
   514  		requestUpdate = true
   515  	}
   516  
   517  	if d.HasChange("vpc_security_group_ids") {
   518  		if attr := d.Get("security_group_names").(*schema.Set); attr.Len() > 0 {
   519  			var s []*string
   520  			for _, v := range attr.List() {
   521  				s = append(s, aws.String(v.(string)))
   522  			}
   523  			req.DBSecurityGroups = s
   524  		}
   525  		requestUpdate = true
   526  	}
   527  
   528  	log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate)
   529  	if requestUpdate {
   530  		log.Printf("[DEBUG] DB Instance Modification request: %#v", req)
   531  		_, err := conn.ModifyDBInstance(req)
   532  		if err != nil {
   533  			return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err)
   534  		}
   535  	}
   536  
   537  	if arn, err := buildRDSARN(d, meta); err == nil {
   538  		if err := setTagsRDS(conn, d, arn); err != nil {
   539  			return err
   540  		} else {
   541  			d.SetPartial("tags")
   542  		}
   543  	}
   544  	d.Partial(false)
   545  	return resourceAwsDbInstanceRead(d, meta)
   546  }
   547  
   548  func resourceAwsDbInstanceRetrieve(
   549  	d *schema.ResourceData, meta interface{}) (*rds.DBInstance, error) {
   550  	conn := meta.(*AWSClient).rdsconn
   551  
   552  	opts := rds.DescribeDBInstancesInput{
   553  		DBInstanceIdentifier: aws.String(d.Id()),
   554  	}
   555  
   556  	log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts)
   557  
   558  	resp, err := conn.DescribeDBInstances(&opts)
   559  
   560  	if err != nil {
   561  		dbinstanceerr, ok := err.(aws.APIError)
   562  		if ok && dbinstanceerr.Code == "DBInstanceNotFound" {
   563  			return nil, nil
   564  		}
   565  		return nil, fmt.Errorf("Error retrieving DB Instances: %s", err)
   566  	}
   567  
   568  	if len(resp.DBInstances) != 1 ||
   569  		*resp.DBInstances[0].DBInstanceIdentifier != d.Id() {
   570  		if err != nil {
   571  			return nil, nil
   572  		}
   573  	}
   574  
   575  	return resp.DBInstances[0], nil
   576  }
   577  
   578  func resourceAwsDbInstanceStateRefreshFunc(
   579  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   580  	return func() (interface{}, string, error) {
   581  		v, err := resourceAwsDbInstanceRetrieve(d, meta)
   582  
   583  		if err != nil {
   584  			log.Printf("Error on retrieving DB Instance when waiting: %s", err)
   585  			return nil, "", err
   586  		}
   587  
   588  		if v == nil {
   589  			return nil, "", nil
   590  		}
   591  
   592  		return v, *v.DBInstanceStatus, nil
   593  	}
   594  }
   595  
   596  func buildRDSARN(d *schema.ResourceData, meta interface{}) (string, error) {
   597  	iamconn := meta.(*AWSClient).iamconn
   598  	region := meta.(*AWSClient).region
   599  	// An zero value GetUserInput{} defers to the currently logged in user
   600  	resp, err := iamconn.GetUser(&iam.GetUserInput{})
   601  	if err != nil {
   602  		return "", err
   603  	}
   604  	userARN := *resp.User.ARN
   605  	accountID := strings.Split(userARN, ":")[4]
   606  	arn := fmt.Sprintf("arn:aws:rds:%s:%s:db:%s", region, accountID, d.Id())
   607  	return arn, nil
   608  }