github.com/gabrielperezs/terraform@v0.7.0-rc2.0.20160715084931-f7da2612946f/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 return fmt.Errorf("Error detaching ENI: %s", detach_err) 200 } 201 202 log.Printf("[DEBUG] Waiting for ENI (%s) to become dettached", eniId) 203 stateConf := &resource.StateChangeConf{ 204 Pending: []string{"true"}, 205 Target: []string{"false"}, 206 Refresh: networkInterfaceAttachmentRefreshFunc(conn, eniId), 207 Timeout: 10 * time.Minute, 208 } 209 if _, err := stateConf.WaitForState(); err != nil { 210 return fmt.Errorf( 211 "Error waiting for ENI (%s) to become dettached: %s", eniId, err) 212 } 213 } 214 215 return nil 216 } 217 218 func resourceAwsNetworkInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { 219 conn := meta.(*AWSClient).ec2conn 220 d.Partial(true) 221 222 if d.HasChange("attachment") { 223 oa, na := d.GetChange("attachment") 224 225 detach_err := resourceAwsNetworkInterfaceDetach(oa.(*schema.Set), meta, d.Id()) 226 if detach_err != nil { 227 return detach_err 228 } 229 230 // if there is a new attachment, attach it 231 if na != nil && len(na.(*schema.Set).List()) > 0 { 232 new_attachment := na.(*schema.Set).List()[0].(map[string]interface{}) 233 di := new_attachment["device_index"].(int) 234 attach_request := &ec2.AttachNetworkInterfaceInput{ 235 DeviceIndex: aws.Int64(int64(di)), 236 InstanceId: aws.String(new_attachment["instance"].(string)), 237 NetworkInterfaceId: aws.String(d.Id()), 238 } 239 _, attach_err := conn.AttachNetworkInterface(attach_request) 240 if attach_err != nil { 241 return fmt.Errorf("Error attaching ENI: %s", attach_err) 242 } 243 } 244 245 d.SetPartial("attachment") 246 } 247 248 if d.HasChange("private_ips") { 249 o, n := d.GetChange("private_ips") 250 if o == nil { 251 o = new(schema.Set) 252 } 253 if n == nil { 254 n = new(schema.Set) 255 } 256 257 os := o.(*schema.Set) 258 ns := n.(*schema.Set) 259 260 // Unassign old IP addresses 261 unassignIps := os.Difference(ns) 262 if unassignIps.Len() != 0 { 263 input := &ec2.UnassignPrivateIpAddressesInput{ 264 NetworkInterfaceId: aws.String(d.Id()), 265 PrivateIpAddresses: expandStringList(unassignIps.List()), 266 } 267 _, err := conn.UnassignPrivateIpAddresses(input) 268 if err != nil { 269 return fmt.Errorf("Failure to unassign Private IPs: %s", err) 270 } 271 } 272 273 // Assign new IP addresses 274 assignIps := ns.Difference(os) 275 if assignIps.Len() != 0 { 276 input := &ec2.AssignPrivateIpAddressesInput{ 277 NetworkInterfaceId: aws.String(d.Id()), 278 PrivateIpAddresses: expandStringList(assignIps.List()), 279 } 280 _, err := conn.AssignPrivateIpAddresses(input) 281 if err != nil { 282 return fmt.Errorf("Failure to assign Private IPs: %s", err) 283 } 284 } 285 286 d.SetPartial("private_ips") 287 } 288 289 request := &ec2.ModifyNetworkInterfaceAttributeInput{ 290 NetworkInterfaceId: aws.String(d.Id()), 291 SourceDestCheck: &ec2.AttributeBooleanValue{Value: aws.Bool(d.Get("source_dest_check").(bool))}, 292 } 293 294 _, err := conn.ModifyNetworkInterfaceAttribute(request) 295 if err != nil { 296 return fmt.Errorf("Failure updating ENI: %s", err) 297 } 298 299 d.SetPartial("source_dest_check") 300 301 if d.HasChange("security_groups") { 302 request := &ec2.ModifyNetworkInterfaceAttributeInput{ 303 NetworkInterfaceId: aws.String(d.Id()), 304 Groups: expandStringList(d.Get("security_groups").(*schema.Set).List()), 305 } 306 307 _, err := conn.ModifyNetworkInterfaceAttribute(request) 308 if err != nil { 309 return fmt.Errorf("Failure updating ENI: %s", err) 310 } 311 312 d.SetPartial("security_groups") 313 } 314 315 if d.HasChange("description") { 316 request := &ec2.ModifyNetworkInterfaceAttributeInput{ 317 NetworkInterfaceId: aws.String(d.Id()), 318 Description: &ec2.AttributeValue{Value: aws.String(d.Get("description").(string))}, 319 } 320 321 _, err := conn.ModifyNetworkInterfaceAttribute(request) 322 if err != nil { 323 return fmt.Errorf("Failure updating ENI: %s", err) 324 } 325 326 d.SetPartial("description") 327 } 328 329 if err := setTags(conn, d); err != nil { 330 return err 331 } else { 332 d.SetPartial("tags") 333 } 334 335 d.Partial(false) 336 337 return resourceAwsNetworkInterfaceRead(d, meta) 338 } 339 340 func resourceAwsNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error { 341 conn := meta.(*AWSClient).ec2conn 342 343 log.Printf("[INFO] Deleting ENI: %s", d.Id()) 344 345 detach_err := resourceAwsNetworkInterfaceDetach(d.Get("attachment").(*schema.Set), meta, d.Id()) 346 if detach_err != nil { 347 return detach_err 348 } 349 350 deleteEniOpts := ec2.DeleteNetworkInterfaceInput{ 351 NetworkInterfaceId: aws.String(d.Id()), 352 } 353 if _, err := conn.DeleteNetworkInterface(&deleteEniOpts); err != nil { 354 return fmt.Errorf("Error deleting ENI: %s", err) 355 } 356 357 return nil 358 } 359 360 func resourceAwsEniAttachmentHash(v interface{}) int { 361 var buf bytes.Buffer 362 m := v.(map[string]interface{}) 363 buf.WriteString(fmt.Sprintf("%s-", m["instance"].(string))) 364 buf.WriteString(fmt.Sprintf("%d-", m["device_index"].(int))) 365 return hashcode.String(buf.String()) 366 }