github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/builtin/providers/aws/resource_aws_db_instance.go (about)

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