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