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