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 }