github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/azurerm/resource_arm_network_interface_card.go (about) 1 package azurerm 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "net/http" 8 "strings" 9 10 "github.com/Azure/azure-sdk-for-go/arm/network" 11 "github.com/hashicorp/terraform/helper/hashcode" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceArmNetworkInterface() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceArmNetworkInterfaceCreate, 18 Read: resourceArmNetworkInterfaceRead, 19 Update: resourceArmNetworkInterfaceCreate, 20 Delete: resourceArmNetworkInterfaceDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "name": { 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 }, 28 29 "location": locationSchema(), 30 31 "resource_group_name": { 32 Type: schema.TypeString, 33 Required: true, 34 ForceNew: true, 35 }, 36 37 "network_security_group_id": { 38 Type: schema.TypeString, 39 Optional: true, 40 Computed: true, 41 }, 42 43 "mac_address": { 44 Type: schema.TypeString, 45 Optional: true, 46 Computed: true, 47 }, 48 49 "private_ip_address": { 50 Type: schema.TypeString, 51 Computed: true, 52 }, 53 54 "virtual_machine_id": { 55 Type: schema.TypeString, 56 Optional: true, 57 Computed: true, 58 }, 59 60 "ip_configuration": { 61 Type: schema.TypeSet, 62 Required: true, 63 Elem: &schema.Resource{ 64 Schema: map[string]*schema.Schema{ 65 "name": { 66 Type: schema.TypeString, 67 Required: true, 68 }, 69 70 "subnet_id": { 71 Type: schema.TypeString, 72 Required: true, 73 }, 74 75 "private_ip_address": { 76 Type: schema.TypeString, 77 Optional: true, 78 Computed: true, 79 }, 80 81 "private_ip_address_allocation": { 82 Type: schema.TypeString, 83 Required: true, 84 ValidateFunc: validateNetworkInterfacePrivateIpAddressAllocation, 85 StateFunc: ignoreCaseStateFunc, 86 DiffSuppressFunc: ignoreCaseDiffSuppressFunc, 87 }, 88 89 "public_ip_address_id": { 90 Type: schema.TypeString, 91 Optional: true, 92 Computed: true, 93 }, 94 95 "load_balancer_backend_address_pools_ids": { 96 Type: schema.TypeSet, 97 Optional: true, 98 Computed: true, 99 Elem: &schema.Schema{Type: schema.TypeString}, 100 Set: schema.HashString, 101 }, 102 103 "load_balancer_inbound_nat_rules_ids": { 104 Type: schema.TypeSet, 105 Optional: true, 106 Computed: true, 107 Elem: &schema.Schema{Type: schema.TypeString}, 108 Set: schema.HashString, 109 }, 110 }, 111 }, 112 Set: resourceArmNetworkInterfaceIpConfigurationHash, 113 }, 114 115 "dns_servers": { 116 Type: schema.TypeSet, 117 Optional: true, 118 Computed: true, 119 Elem: &schema.Schema{Type: schema.TypeString}, 120 Set: schema.HashString, 121 }, 122 123 "internal_dns_name_label": { 124 Type: schema.TypeString, 125 Optional: true, 126 Computed: true, 127 }, 128 129 "applied_dns_servers": { 130 Type: schema.TypeSet, 131 Optional: true, 132 Computed: true, 133 Elem: &schema.Schema{Type: schema.TypeString}, 134 Set: schema.HashString, 135 }, 136 137 "internal_fqdn": { 138 Type: schema.TypeString, 139 Optional: true, 140 Computed: true, 141 }, 142 143 "enable_ip_forwarding": { 144 Type: schema.TypeBool, 145 Optional: true, 146 Default: false, 147 }, 148 149 "tags": tagsSchema(), 150 }, 151 } 152 } 153 154 func resourceArmNetworkInterfaceCreate(d *schema.ResourceData, meta interface{}) error { 155 client := meta.(*ArmClient) 156 ifaceClient := client.ifaceClient 157 158 log.Printf("[INFO] preparing arguments for Azure ARM Network Interface creation.") 159 160 name := d.Get("name").(string) 161 location := d.Get("location").(string) 162 resGroup := d.Get("resource_group_name").(string) 163 enableIpForwarding := d.Get("enable_ip_forwarding").(bool) 164 tags := d.Get("tags").(map[string]interface{}) 165 166 properties := network.InterfacePropertiesFormat{ 167 EnableIPForwarding: &enableIpForwarding, 168 } 169 170 if v, ok := d.GetOk("network_security_group_id"); ok { 171 nsgId := v.(string) 172 properties.NetworkSecurityGroup = &network.SecurityGroup{ 173 ID: &nsgId, 174 } 175 176 networkSecurityGroupName, err := parseNetworkSecurityGroupName(nsgId) 177 if err != nil { 178 return err 179 } 180 181 armMutexKV.Lock(networkSecurityGroupName) 182 defer armMutexKV.Unlock(networkSecurityGroupName) 183 } 184 185 dns, hasDns := d.GetOk("dns_servers") 186 nameLabel, hasNameLabel := d.GetOk("internal_dns_name_label") 187 if hasDns || hasNameLabel { 188 ifaceDnsSettings := network.InterfaceDNSSettings{} 189 190 if hasDns { 191 var dnsServers []string 192 dns := dns.(*schema.Set).List() 193 for _, v := range dns { 194 str := v.(string) 195 dnsServers = append(dnsServers, str) 196 } 197 ifaceDnsSettings.DNSServers = &dnsServers 198 } 199 200 if hasNameLabel { 201 name_label := nameLabel.(string) 202 ifaceDnsSettings.InternalDNSNameLabel = &name_label 203 } 204 205 properties.DNSSettings = &ifaceDnsSettings 206 } 207 208 ipConfigs, namesToLock, sgErr := expandAzureRmNetworkInterfaceIpConfigurations(d) 209 if sgErr != nil { 210 return fmt.Errorf("Error Building list of Network Interface IP Configurations: %s", sgErr) 211 } 212 213 azureRMLockMultiple(namesToLock) 214 defer azureRMUnlockMultiple(namesToLock) 215 216 if len(ipConfigs) > 0 { 217 properties.IPConfigurations = &ipConfigs 218 } 219 220 iface := network.Interface{ 221 Name: &name, 222 Location: &location, 223 InterfacePropertiesFormat: &properties, 224 Tags: expandTags(tags), 225 } 226 227 _, error := ifaceClient.CreateOrUpdate(resGroup, name, iface, make(chan struct{})) 228 err := <-error 229 if err != nil { 230 return err 231 } 232 233 read, err := ifaceClient.Get(resGroup, name, "") 234 if err != nil { 235 return err 236 } 237 if read.ID == nil { 238 return fmt.Errorf("Cannot read NIC %s (resource group %s) ID", name, resGroup) 239 } 240 241 d.SetId(*read.ID) 242 243 return resourceArmNetworkInterfaceRead(d, meta) 244 } 245 246 func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error { 247 ifaceClient := meta.(*ArmClient).ifaceClient 248 249 id, err := parseAzureResourceID(d.Id()) 250 if err != nil { 251 return err 252 } 253 resGroup := id.ResourceGroup 254 name := id.Path["networkInterfaces"] 255 256 resp, err := ifaceClient.Get(resGroup, name, "") 257 if err != nil { 258 if resp.StatusCode == http.StatusNotFound { 259 d.SetId("") 260 return nil 261 } 262 return fmt.Errorf("Error making Read request on Azure Network Interface %s: %s", name, err) 263 } 264 265 iface := *resp.InterfacePropertiesFormat 266 267 if iface.MacAddress != nil { 268 if *iface.MacAddress != "" { 269 d.Set("mac_address", iface.MacAddress) 270 } 271 } 272 273 if iface.IPConfigurations != nil && len(*iface.IPConfigurations) > 0 { 274 var privateIPAddress *string 275 ///TODO: Change this to a loop when https://github.com/Azure/azure-sdk-for-go/issues/259 is fixed 276 if (*iface.IPConfigurations)[0].InterfaceIPConfigurationPropertiesFormat != nil { 277 privateIPAddress = (*iface.IPConfigurations)[0].InterfaceIPConfigurationPropertiesFormat.PrivateIPAddress 278 } 279 280 if *privateIPAddress != "" { 281 d.Set("private_ip_address", *privateIPAddress) 282 } 283 } 284 285 if iface.VirtualMachine != nil { 286 if *iface.VirtualMachine.ID != "" { 287 d.Set("virtual_machine_id", *iface.VirtualMachine.ID) 288 } 289 } 290 291 if iface.DNSSettings != nil { 292 if iface.DNSSettings.AppliedDNSServers != nil && len(*iface.DNSSettings.AppliedDNSServers) > 0 { 293 dnsServers := make([]string, 0, len(*iface.DNSSettings.AppliedDNSServers)) 294 for _, dns := range *iface.DNSSettings.AppliedDNSServers { 295 dnsServers = append(dnsServers, dns) 296 } 297 298 if err := d.Set("applied_dns_servers", dnsServers); err != nil { 299 return err 300 } 301 } 302 303 if iface.DNSSettings.InternalFqdn != nil && *iface.DNSSettings.InternalFqdn != "" { 304 d.Set("internal_fqdn", iface.DNSSettings.InternalFqdn) 305 } 306 } 307 308 flattenAndSetTags(d, resp.Tags) 309 310 return nil 311 } 312 313 func resourceArmNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error { 314 ifaceClient := meta.(*ArmClient).ifaceClient 315 316 id, err := parseAzureResourceID(d.Id()) 317 if err != nil { 318 return err 319 } 320 resGroup := id.ResourceGroup 321 name := id.Path["networkInterfaces"] 322 323 if v, ok := d.GetOk("network_security_group_id"); ok { 324 networkSecurityGroupId := v.(string) 325 networkSecurityGroupName, err := parseNetworkSecurityGroupName(networkSecurityGroupId) 326 if err != nil { 327 return err 328 } 329 330 armMutexKV.Lock(networkSecurityGroupName) 331 defer armMutexKV.Unlock(networkSecurityGroupName) 332 } 333 334 configs := d.Get("ip_configuration").(*schema.Set).List() 335 namesToLock := make([]string, 0) 336 337 for _, configRaw := range configs { 338 data := configRaw.(map[string]interface{}) 339 340 subnet_id := data["subnet_id"].(string) 341 subnetId, err := parseAzureResourceID(subnet_id) 342 if err != nil { 343 return err 344 } 345 subnetName := subnetId.Path["subnets"] 346 virtualNetworkName := subnetId.Path["virtualNetworks"] 347 namesToLock = append(namesToLock, subnetName) 348 namesToLock = append(namesToLock, virtualNetworkName) 349 } 350 351 azureRMLockMultiple(&namesToLock) 352 defer azureRMUnlockMultiple(&namesToLock) 353 354 _, error := ifaceClient.Delete(resGroup, name, make(chan struct{})) 355 err = <-error 356 357 return err 358 } 359 360 func resourceArmNetworkInterfaceIpConfigurationHash(v interface{}) int { 361 var buf bytes.Buffer 362 m := v.(map[string]interface{}) 363 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 364 buf.WriteString(fmt.Sprintf("%s-", m["subnet_id"].(string))) 365 if m["private_ip_address"] != nil { 366 buf.WriteString(fmt.Sprintf("%s-", m["private_ip_address"].(string))) 367 } 368 buf.WriteString(fmt.Sprintf("%s-", m["private_ip_address_allocation"].(string))) 369 if m["public_ip_address_id"] != nil { 370 buf.WriteString(fmt.Sprintf("%s-", m["public_ip_address_id"].(string))) 371 } 372 if m["load_balancer_backend_address_pools_ids"] != nil { 373 ids := m["load_balancer_backend_address_pools_ids"].(*schema.Set).List() 374 for _, id := range ids { 375 buf.WriteString(fmt.Sprintf("%d-", schema.HashString(id.(string)))) 376 } 377 } 378 if m["load_balancer_inbound_nat_rules_ids"] != nil { 379 ids := m["load_balancer_inbound_nat_rules_ids"].(*schema.Set).List() 380 for _, id := range ids { 381 buf.WriteString(fmt.Sprintf("%d-", schema.HashString(id.(string)))) 382 } 383 } 384 385 return hashcode.String(buf.String()) 386 } 387 388 func validateNetworkInterfacePrivateIpAddressAllocation(v interface{}, k string) (ws []string, errors []error) { 389 value := strings.ToLower(v.(string)) 390 allocations := map[string]bool{ 391 "static": true, 392 "dynamic": true, 393 } 394 395 if !allocations[value] { 396 errors = append(errors, fmt.Errorf("Network Interface Allocations can only be Static or Dynamic")) 397 } 398 return 399 } 400 401 func expandAzureRmNetworkInterfaceIpConfigurations(d *schema.ResourceData) ([]network.InterfaceIPConfiguration, *[]string, error) { 402 configs := d.Get("ip_configuration").(*schema.Set).List() 403 ipConfigs := make([]network.InterfaceIPConfiguration, 0, len(configs)) 404 namesToLock := make([]string, 0) 405 406 for _, configRaw := range configs { 407 data := configRaw.(map[string]interface{}) 408 409 subnet_id := data["subnet_id"].(string) 410 private_ip_allocation_method := data["private_ip_address_allocation"].(string) 411 412 var allocationMethod network.IPAllocationMethod 413 switch strings.ToLower(private_ip_allocation_method) { 414 case "dynamic": 415 allocationMethod = network.Dynamic 416 case "static": 417 allocationMethod = network.Static 418 default: 419 return []network.InterfaceIPConfiguration{}, nil, fmt.Errorf( 420 "valid values for private_ip_allocation_method are 'dynamic' and 'static' - got '%s'", 421 private_ip_allocation_method) 422 } 423 424 properties := network.InterfaceIPConfigurationPropertiesFormat{ 425 Subnet: &network.Subnet{ 426 ID: &subnet_id, 427 }, 428 PrivateIPAllocationMethod: allocationMethod, 429 } 430 431 subnetId, err := parseAzureResourceID(subnet_id) 432 if err != nil { 433 return []network.InterfaceIPConfiguration{}, nil, err 434 } 435 subnetName := subnetId.Path["subnets"] 436 virtualNetworkName := subnetId.Path["virtualNetworks"] 437 namesToLock = append(namesToLock, subnetName) 438 namesToLock = append(namesToLock, virtualNetworkName) 439 440 if v := data["private_ip_address"].(string); v != "" { 441 properties.PrivateIPAddress = &v 442 } 443 444 if v := data["public_ip_address_id"].(string); v != "" { 445 properties.PublicIPAddress = &network.PublicIPAddress{ 446 ID: &v, 447 } 448 } 449 450 if v, ok := data["load_balancer_backend_address_pools_ids"]; ok { 451 var ids []network.BackendAddressPool 452 pools := v.(*schema.Set).List() 453 for _, p := range pools { 454 pool_id := p.(string) 455 id := network.BackendAddressPool{ 456 ID: &pool_id, 457 } 458 459 ids = append(ids, id) 460 } 461 462 properties.LoadBalancerBackendAddressPools = &ids 463 } 464 465 if v, ok := data["load_balancer_inbound_nat_rules_ids"]; ok { 466 var natRules []network.InboundNatRule 467 rules := v.(*schema.Set).List() 468 for _, r := range rules { 469 rule_id := r.(string) 470 rule := network.InboundNatRule{ 471 ID: &rule_id, 472 } 473 474 natRules = append(natRules, rule) 475 } 476 477 properties.LoadBalancerInboundNatRules = &natRules 478 } 479 480 name := data["name"].(string) 481 ipConfig := network.InterfaceIPConfiguration{ 482 Name: &name, 483 InterfaceIPConfigurationPropertiesFormat: &properties, 484 } 485 486 ipConfigs = append(ipConfigs, ipConfig) 487 } 488 489 return ipConfigs, &namesToLock, nil 490 }