github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_network_interface.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     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/ec2"
    13  	"github.com/hashicorp/terraform/helper/hashcode"
    14  	"github.com/hashicorp/terraform/helper/resource"
    15  	"github.com/hashicorp/terraform/helper/schema"
    16  )
    17  
    18  func resourceAwsNetworkInterface() *schema.Resource {
    19  	return &schema.Resource{
    20  		Create: resourceAwsNetworkInterfaceCreate,
    21  		Read:   resourceAwsNetworkInterfaceRead,
    22  		Update: resourceAwsNetworkInterfaceUpdate,
    23  		Delete: resourceAwsNetworkInterfaceDelete,
    24  
    25  		Schema: map[string]*schema.Schema{
    26  
    27  			"subnet_id": &schema.Schema{
    28  				Type:     schema.TypeString,
    29  				Required: true,
    30  				ForceNew: true,
    31  			},
    32  
    33  			"private_ips": &schema.Schema{
    34  				Type:     schema.TypeSet,
    35  				Optional: true,
    36  				Computed: true,
    37  				Elem:     &schema.Schema{Type: schema.TypeString},
    38  				Set:      schema.HashString,
    39  			},
    40  
    41  			"security_groups": &schema.Schema{
    42  				Type:     schema.TypeSet,
    43  				Optional: true,
    44  				Computed: true,
    45  				Elem:     &schema.Schema{Type: schema.TypeString},
    46  				Set:      schema.HashString,
    47  			},
    48  
    49  			"source_dest_check": &schema.Schema{
    50  				Type:     schema.TypeBool,
    51  				Optional: true,
    52  				Default:  true,
    53  			},
    54  
    55  			"description": &schema.Schema{
    56  				Type:     schema.TypeString,
    57  				Optional: true,
    58  			},
    59  
    60  			"attachment": &schema.Schema{
    61  				Type:     schema.TypeSet,
    62  				Optional: true,
    63  				Computed: true,
    64  				Elem: &schema.Resource{
    65  					Schema: map[string]*schema.Schema{
    66  						"instance": &schema.Schema{
    67  							Type:     schema.TypeString,
    68  							Required: true,
    69  						},
    70  						"device_index": &schema.Schema{
    71  							Type:     schema.TypeInt,
    72  							Required: true,
    73  						},
    74  						"attachment_id": &schema.Schema{
    75  							Type:     schema.TypeString,
    76  							Computed: true,
    77  						},
    78  					},
    79  				},
    80  				Set: resourceAwsEniAttachmentHash,
    81  			},
    82  
    83  			"tags": tagsSchema(),
    84  		},
    85  	}
    86  }
    87  
    88  func resourceAwsNetworkInterfaceCreate(d *schema.ResourceData, meta interface{}) error {
    89  
    90  	conn := meta.(*AWSClient).ec2conn
    91  
    92  	request := &ec2.CreateNetworkInterfaceInput{
    93  		SubnetId: aws.String(d.Get("subnet_id").(string)),
    94  	}
    95  
    96  	security_groups := d.Get("security_groups").(*schema.Set).List()
    97  	if len(security_groups) != 0 {
    98  		request.Groups = expandStringList(security_groups)
    99  	}
   100  
   101  	private_ips := d.Get("private_ips").(*schema.Set).List()
   102  	if len(private_ips) != 0 {
   103  		request.PrivateIpAddresses = expandPrivateIPAddresses(private_ips)
   104  	}
   105  
   106  	if v, ok := d.GetOk("description"); ok {
   107  		request.Description = aws.String(v.(string))
   108  	}
   109  
   110  	log.Printf("[DEBUG] Creating network interface")
   111  	resp, err := conn.CreateNetworkInterface(request)
   112  	if err != nil {
   113  		return fmt.Errorf("Error creating ENI: %s", err)
   114  	}
   115  
   116  	d.SetId(*resp.NetworkInterface.NetworkInterfaceId)
   117  	log.Printf("[INFO] ENI ID: %s", d.Id())
   118  	return resourceAwsNetworkInterfaceUpdate(d, meta)
   119  }
   120  
   121  func resourceAwsNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error {
   122  
   123  	conn := meta.(*AWSClient).ec2conn
   124  	describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesInput{
   125  		NetworkInterfaceIds: []*string{aws.String(d.Id())},
   126  	}
   127  	describeResp, err := conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
   128  
   129  	if err != nil {
   130  		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidNetworkInterfaceID.NotFound" {
   131  			// The ENI is gone now, so just remove it from the state
   132  			d.SetId("")
   133  			return nil
   134  		}
   135  
   136  		return fmt.Errorf("Error retrieving ENI: %s", err)
   137  	}
   138  	if len(describeResp.NetworkInterfaces) != 1 {
   139  		return fmt.Errorf("Unable to find ENI: %#v", describeResp.NetworkInterfaces)
   140  	}
   141  
   142  	eni := describeResp.NetworkInterfaces[0]
   143  	d.Set("subnet_id", eni.SubnetId)
   144  	d.Set("private_ips", flattenNetworkInterfacesPrivateIPAddresses(eni.PrivateIpAddresses))
   145  	d.Set("security_groups", flattenGroupIdentifiers(eni.Groups))
   146  	d.Set("source_dest_check", eni.SourceDestCheck)
   147  
   148  	if eni.Description != nil {
   149  		d.Set("description", eni.Description)
   150  	}
   151  
   152  	// Tags
   153  	d.Set("tags", tagsToMap(eni.TagSet))
   154  
   155  	if eni.Attachment != nil {
   156  		attachment := []map[string]interface{}{flattenAttachment(eni.Attachment)}
   157  		d.Set("attachment", attachment)
   158  	} else {
   159  		d.Set("attachment", nil)
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  func networkInterfaceAttachmentRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   166  	return func() (interface{}, string, error) {
   167  
   168  		describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesInput{
   169  			NetworkInterfaceIds: []*string{aws.String(id)},
   170  		}
   171  		describeResp, err := conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
   172  
   173  		if err != nil {
   174  			log.Printf("[ERROR] Could not find network interface %s. %s", id, err)
   175  			return nil, "", err
   176  		}
   177  
   178  		eni := describeResp.NetworkInterfaces[0]
   179  		hasAttachment := strconv.FormatBool(eni.Attachment != nil)
   180  		log.Printf("[DEBUG] ENI %s has attachment state %s", id, hasAttachment)
   181  		return eni, hasAttachment, nil
   182  	}
   183  }
   184  
   185  func resourceAwsNetworkInterfaceDetach(oa *schema.Set, meta interface{}, eniId string) error {
   186  	// if there was an old attachment, remove it
   187  	if oa != nil && len(oa.List()) > 0 {
   188  		old_attachment := oa.List()[0].(map[string]interface{})
   189  		detach_request := &ec2.DetachNetworkInterfaceInput{
   190  			AttachmentId: aws.String(old_attachment["attachment_id"].(string)),
   191  			Force:        aws.Bool(true),
   192  		}
   193  		conn := meta.(*AWSClient).ec2conn
   194  		_, detach_err := conn.DetachNetworkInterface(detach_request)
   195  		if detach_err != nil {
   196  			return fmt.Errorf("Error detaching ENI: %s", detach_err)
   197  		}
   198  
   199  		log.Printf("[DEBUG] Waiting for ENI (%s) to become dettached", eniId)
   200  		stateConf := &resource.StateChangeConf{
   201  			Pending: []string{"true"},
   202  			Target:  []string{"false"},
   203  			Refresh: networkInterfaceAttachmentRefreshFunc(conn, eniId),
   204  			Timeout: 10 * time.Minute,
   205  		}
   206  		if _, err := stateConf.WaitForState(); err != nil {
   207  			return fmt.Errorf(
   208  				"Error waiting for ENI (%s) to become dettached: %s", eniId, err)
   209  		}
   210  	}
   211  
   212  	return nil
   213  }
   214  
   215  func resourceAwsNetworkInterfaceUpdate(d *schema.ResourceData, meta interface{}) error {
   216  	conn := meta.(*AWSClient).ec2conn
   217  	d.Partial(true)
   218  
   219  	if d.HasChange("attachment") {
   220  		oa, na := d.GetChange("attachment")
   221  
   222  		detach_err := resourceAwsNetworkInterfaceDetach(oa.(*schema.Set), meta, d.Id())
   223  		if detach_err != nil {
   224  			return detach_err
   225  		}
   226  
   227  		// if there is a new attachment, attach it
   228  		if na != nil && len(na.(*schema.Set).List()) > 0 {
   229  			new_attachment := na.(*schema.Set).List()[0].(map[string]interface{})
   230  			di := new_attachment["device_index"].(int)
   231  			attach_request := &ec2.AttachNetworkInterfaceInput{
   232  				DeviceIndex:        aws.Int64(int64(di)),
   233  				InstanceId:         aws.String(new_attachment["instance"].(string)),
   234  				NetworkInterfaceId: aws.String(d.Id()),
   235  			}
   236  			_, attach_err := conn.AttachNetworkInterface(attach_request)
   237  			if attach_err != nil {
   238  				return fmt.Errorf("Error attaching ENI: %s", attach_err)
   239  			}
   240  		}
   241  
   242  		d.SetPartial("attachment")
   243  	}
   244  
   245  	if d.HasChange("private_ips") {
   246  		o, n := d.GetChange("private_ips")
   247  		if o == nil {
   248  			o = new(schema.Set)
   249  		}
   250  		if n == nil {
   251  			n = new(schema.Set)
   252  		}
   253  
   254  		os := o.(*schema.Set)
   255  		ns := n.(*schema.Set)
   256  
   257  		// Unassign old IP addresses
   258  		unassignIps := os.Difference(ns)
   259  		if unassignIps.Len() != 0 {
   260  			input := &ec2.UnassignPrivateIpAddressesInput{
   261  				NetworkInterfaceId: aws.String(d.Id()),
   262  				PrivateIpAddresses: expandStringList(unassignIps.List()),
   263  			}
   264  			_, err := conn.UnassignPrivateIpAddresses(input)
   265  			if err != nil {
   266  				return fmt.Errorf("Failure to unassign Private IPs: %s", err)
   267  			}
   268  		}
   269  
   270  		// Assign new IP addresses
   271  		assignIps := ns.Difference(os)
   272  		if assignIps.Len() != 0 {
   273  			input := &ec2.AssignPrivateIpAddressesInput{
   274  				NetworkInterfaceId: aws.String(d.Id()),
   275  				PrivateIpAddresses: expandStringList(assignIps.List()),
   276  			}
   277  			_, err := conn.AssignPrivateIpAddresses(input)
   278  			if err != nil {
   279  				return fmt.Errorf("Failure to assign Private IPs: %s", err)
   280  			}
   281  		}
   282  
   283  		d.SetPartial("private_ips")
   284  	}
   285  
   286  	request := &ec2.ModifyNetworkInterfaceAttributeInput{
   287  		NetworkInterfaceId: aws.String(d.Id()),
   288  		SourceDestCheck:    &ec2.AttributeBooleanValue{Value: aws.Bool(d.Get("source_dest_check").(bool))},
   289  	}
   290  
   291  	_, err := conn.ModifyNetworkInterfaceAttribute(request)
   292  	if err != nil {
   293  		return fmt.Errorf("Failure updating ENI: %s", err)
   294  	}
   295  
   296  	d.SetPartial("source_dest_check")
   297  
   298  	if d.HasChange("security_groups") {
   299  		request := &ec2.ModifyNetworkInterfaceAttributeInput{
   300  			NetworkInterfaceId: aws.String(d.Id()),
   301  			Groups:             expandStringList(d.Get("security_groups").(*schema.Set).List()),
   302  		}
   303  
   304  		_, err := conn.ModifyNetworkInterfaceAttribute(request)
   305  		if err != nil {
   306  			return fmt.Errorf("Failure updating ENI: %s", err)
   307  		}
   308  
   309  		d.SetPartial("security_groups")
   310  	}
   311  
   312  	if d.HasChange("description") {
   313  		request := &ec2.ModifyNetworkInterfaceAttributeInput{
   314  			NetworkInterfaceId: aws.String(d.Id()),
   315  			Description:        &ec2.AttributeValue{Value: aws.String(d.Get("description").(string))},
   316  		}
   317  
   318  		_, err := conn.ModifyNetworkInterfaceAttribute(request)
   319  		if err != nil {
   320  			return fmt.Errorf("Failure updating ENI: %s", err)
   321  		}
   322  
   323  		d.SetPartial("description")
   324  	}
   325  
   326  	if err := setTags(conn, d); err != nil {
   327  		return err
   328  	} else {
   329  		d.SetPartial("tags")
   330  	}
   331  
   332  	d.Partial(false)
   333  
   334  	return resourceAwsNetworkInterfaceRead(d, meta)
   335  }
   336  
   337  func resourceAwsNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error {
   338  	conn := meta.(*AWSClient).ec2conn
   339  
   340  	log.Printf("[INFO] Deleting ENI: %s", d.Id())
   341  
   342  	detach_err := resourceAwsNetworkInterfaceDetach(d.Get("attachment").(*schema.Set), meta, d.Id())
   343  	if detach_err != nil {
   344  		return detach_err
   345  	}
   346  
   347  	deleteEniOpts := ec2.DeleteNetworkInterfaceInput{
   348  		NetworkInterfaceId: aws.String(d.Id()),
   349  	}
   350  	if _, err := conn.DeleteNetworkInterface(&deleteEniOpts); err != nil {
   351  		return fmt.Errorf("Error deleting ENI: %s", err)
   352  	}
   353  
   354  	return nil
   355  }
   356  
   357  func resourceAwsEniAttachmentHash(v interface{}) int {
   358  	var buf bytes.Buffer
   359  	m := v.(map[string]interface{})
   360  	buf.WriteString(fmt.Sprintf("%s-", m["instance"].(string)))
   361  	buf.WriteString(fmt.Sprintf("%d-", m["device_index"].(int)))
   362  	return hashcode.String(buf.String())
   363  }