github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/azurerm/resource_arm_virtual_network.go (about) 1 package azurerm 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "net/http" 8 9 "github.com/Azure/azure-sdk-for-go/arm/network" 10 "github.com/hashicorp/terraform/helper/hashcode" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceArmVirtualNetwork() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceArmVirtualNetworkCreate, 17 Read: resourceArmVirtualNetworkRead, 18 Update: resourceArmVirtualNetworkCreate, 19 Delete: resourceArmVirtualNetworkDelete, 20 Importer: &schema.ResourceImporter{ 21 State: schema.ImportStatePassthrough, 22 }, 23 24 Schema: map[string]*schema.Schema{ 25 "name": { 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 }, 30 31 "address_space": { 32 Type: schema.TypeList, 33 Required: true, 34 Elem: &schema.Schema{ 35 Type: schema.TypeString, 36 }, 37 }, 38 39 "dns_servers": { 40 Type: schema.TypeList, 41 Optional: true, 42 Elem: &schema.Schema{ 43 Type: schema.TypeString, 44 }, 45 }, 46 47 "subnet": { 48 Type: schema.TypeSet, 49 Optional: true, 50 Computed: true, 51 Elem: &schema.Resource{ 52 Schema: map[string]*schema.Schema{ 53 "name": { 54 Type: schema.TypeString, 55 Required: true, 56 }, 57 "address_prefix": { 58 Type: schema.TypeString, 59 Required: true, 60 }, 61 "security_group": { 62 Type: schema.TypeString, 63 Optional: true, 64 }, 65 }, 66 }, 67 Set: resourceAzureSubnetHash, 68 }, 69 70 "location": locationSchema(), 71 72 "resource_group_name": { 73 Type: schema.TypeString, 74 Required: true, 75 ForceNew: true, 76 }, 77 78 "tags": tagsSchema(), 79 }, 80 } 81 } 82 83 func resourceArmVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) error { 84 client := meta.(*ArmClient) 85 vnetClient := client.vnetClient 86 87 log.Printf("[INFO] preparing arguments for Azure ARM virtual network creation.") 88 89 name := d.Get("name").(string) 90 location := d.Get("location").(string) 91 resGroup := d.Get("resource_group_name").(string) 92 tags := d.Get("tags").(map[string]interface{}) 93 vnetProperties, vnetPropsErr := getVirtualNetworkProperties(d, meta) 94 if vnetPropsErr != nil { 95 return vnetPropsErr 96 } 97 98 vnet := network.VirtualNetwork{ 99 Name: &name, 100 Location: &location, 101 VirtualNetworkPropertiesFormat: vnetProperties, 102 Tags: expandTags(tags), 103 } 104 105 networkSecurityGroupNames := make([]string, 0) 106 for _, subnet := range *vnet.VirtualNetworkPropertiesFormat.Subnets { 107 if subnet.NetworkSecurityGroup != nil { 108 nsgName, err := parseNetworkSecurityGroupName(*subnet.NetworkSecurityGroup.ID) 109 if err != nil { 110 return err 111 } 112 113 networkSecurityGroupNames = append(networkSecurityGroupNames, nsgName) 114 } 115 } 116 117 azureRMLockMultiple(&networkSecurityGroupNames) 118 defer azureRMUnlockMultiple(&networkSecurityGroupNames) 119 120 _, error := vnetClient.CreateOrUpdate(resGroup, name, vnet, make(chan struct{})) 121 err := <-error 122 if err != nil { 123 return err 124 } 125 126 read, err := vnetClient.Get(resGroup, name, "") 127 if err != nil { 128 return err 129 } 130 if read.ID == nil { 131 return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup) 132 } 133 134 d.SetId(*read.ID) 135 136 return resourceArmVirtualNetworkRead(d, meta) 137 } 138 139 func resourceArmVirtualNetworkRead(d *schema.ResourceData, meta interface{}) error { 140 vnetClient := meta.(*ArmClient).vnetClient 141 142 id, err := parseAzureResourceID(d.Id()) 143 if err != nil { 144 return err 145 } 146 resGroup := id.ResourceGroup 147 name := id.Path["virtualNetworks"] 148 149 resp, err := vnetClient.Get(resGroup, name, "") 150 if err != nil { 151 if resp.StatusCode == http.StatusNotFound { 152 d.SetId("") 153 return nil 154 } 155 return fmt.Errorf("Error making Read request on Azure virtual network %s: %s", name, err) 156 } 157 158 vnet := *resp.VirtualNetworkPropertiesFormat 159 160 // update appropriate values 161 d.Set("resource_group_name", resGroup) 162 d.Set("name", resp.Name) 163 d.Set("location", resp.Location) 164 d.Set("address_space", vnet.AddressSpace.AddressPrefixes) 165 166 subnets := &schema.Set{ 167 F: resourceAzureSubnetHash, 168 } 169 170 for _, subnet := range *vnet.Subnets { 171 s := map[string]interface{}{} 172 173 s["name"] = *subnet.Name 174 s["address_prefix"] = *subnet.SubnetPropertiesFormat.AddressPrefix 175 if subnet.SubnetPropertiesFormat.NetworkSecurityGroup != nil { 176 s["security_group"] = *subnet.SubnetPropertiesFormat.NetworkSecurityGroup.ID 177 } 178 179 subnets.Add(s) 180 } 181 d.Set("subnet", subnets) 182 183 if vnet.DhcpOptions != nil && vnet.DhcpOptions.DNSServers != nil { 184 dnses := []string{} 185 for _, dns := range *vnet.DhcpOptions.DNSServers { 186 dnses = append(dnses, dns) 187 } 188 d.Set("dns_servers", dnses) 189 } 190 191 flattenAndSetTags(d, resp.Tags) 192 193 return nil 194 } 195 196 func resourceArmVirtualNetworkDelete(d *schema.ResourceData, meta interface{}) error { 197 vnetClient := meta.(*ArmClient).vnetClient 198 199 id, err := parseAzureResourceID(d.Id()) 200 if err != nil { 201 return err 202 } 203 resGroup := id.ResourceGroup 204 name := id.Path["virtualNetworks"] 205 206 nsgNames, err := expandAzureRmVirtualNetworkVirtualNetworkSecurityGroupNames(d) 207 if err != nil { 208 return fmt.Errorf("[ERROR] Error parsing Network Security Group ID's: %+v", err) 209 } 210 211 azureRMLockMultiple(&nsgNames) 212 defer azureRMUnlockMultiple(&nsgNames) 213 214 _, error := vnetClient.Delete(resGroup, name, make(chan struct{})) 215 err = <-error 216 217 return err 218 } 219 220 func getVirtualNetworkProperties(d *schema.ResourceData, meta interface{}) (*network.VirtualNetworkPropertiesFormat, error) { 221 // first; get address space prefixes: 222 prefixes := []string{} 223 for _, prefix := range d.Get("address_space").([]interface{}) { 224 prefixes = append(prefixes, prefix.(string)) 225 } 226 227 // then; the dns servers: 228 dnses := []string{} 229 for _, dns := range d.Get("dns_servers").([]interface{}) { 230 dnses = append(dnses, dns.(string)) 231 } 232 233 // then; the subnets: 234 subnets := []network.Subnet{} 235 if subs := d.Get("subnet").(*schema.Set); subs.Len() > 0 { 236 for _, subnet := range subs.List() { 237 subnet := subnet.(map[string]interface{}) 238 239 name := subnet["name"].(string) 240 log.Printf("[INFO] setting subnets inside vNet, processing %q", name) 241 //since subnets can also be created outside of vNet definition (as root objects) 242 // do a GET on subnet properties from the server before setting them 243 resGroup := d.Get("resource_group_name").(string) 244 vnetName := d.Get("name").(string) 245 subnetObj, err := getExistingSubnet(resGroup, vnetName, name, meta) 246 if err != nil { 247 return nil, err 248 } 249 log.Printf("[INFO] Completed GET of Subnet props ") 250 251 prefix := subnet["address_prefix"].(string) 252 secGroup := subnet["security_group"].(string) 253 254 //set the props from config and leave the rest intact 255 subnetObj.Name = &name 256 if subnetObj.SubnetPropertiesFormat == nil { 257 subnetObj.SubnetPropertiesFormat = &network.SubnetPropertiesFormat{} 258 } 259 260 subnetObj.SubnetPropertiesFormat.AddressPrefix = &prefix 261 262 if secGroup != "" { 263 subnetObj.SubnetPropertiesFormat.NetworkSecurityGroup = &network.SecurityGroup{ 264 ID: &secGroup, 265 } 266 } else { 267 subnetObj.SubnetPropertiesFormat.NetworkSecurityGroup = nil 268 } 269 270 subnets = append(subnets, *subnetObj) 271 } 272 } 273 274 properties := &network.VirtualNetworkPropertiesFormat{ 275 AddressSpace: &network.AddressSpace{ 276 AddressPrefixes: &prefixes, 277 }, 278 DhcpOptions: &network.DhcpOptions{ 279 DNSServers: &dnses, 280 }, 281 Subnets: &subnets, 282 } 283 // finally; return the struct: 284 return properties, nil 285 } 286 287 func resourceAzureSubnetHash(v interface{}) int { 288 var buf bytes.Buffer 289 m := v.(map[string]interface{}) 290 buf.WriteString(fmt.Sprintf("%s", m["name"].(string))) 291 buf.WriteString(fmt.Sprintf("%s", m["address_prefix"].(string))) 292 if v, ok := m["security_group"]; ok { 293 buf.WriteString(v.(string)) 294 } 295 return hashcode.String(buf.String()) 296 } 297 298 func getExistingSubnet(resGroup string, vnetName string, subnetName string, meta interface{}) (*network.Subnet, error) { 299 //attempt to retrieve existing subnet from the server 300 existingSubnet := network.Subnet{} 301 subnetClient := meta.(*ArmClient).subnetClient 302 resp, err := subnetClient.Get(resGroup, vnetName, subnetName, "") 303 304 if err != nil { 305 if resp.StatusCode == http.StatusNotFound { 306 return &existingSubnet, nil 307 } 308 //raise an error if there was an issue other than 404 in getting subnet properties 309 return nil, err 310 } 311 312 existingSubnet.SubnetPropertiesFormat = &network.SubnetPropertiesFormat{} 313 existingSubnet.SubnetPropertiesFormat.AddressPrefix = resp.SubnetPropertiesFormat.AddressPrefix 314 315 if resp.SubnetPropertiesFormat.NetworkSecurityGroup != nil { 316 existingSubnet.SubnetPropertiesFormat.NetworkSecurityGroup = resp.SubnetPropertiesFormat.NetworkSecurityGroup 317 } 318 319 if resp.SubnetPropertiesFormat.RouteTable != nil { 320 existingSubnet.SubnetPropertiesFormat.RouteTable = resp.SubnetPropertiesFormat.RouteTable 321 } 322 323 if resp.SubnetPropertiesFormat.IPConfigurations != nil { 324 ips := make([]string, 0, len(*resp.SubnetPropertiesFormat.IPConfigurations)) 325 for _, ip := range *resp.SubnetPropertiesFormat.IPConfigurations { 326 ips = append(ips, *ip.ID) 327 } 328 329 existingSubnet.SubnetPropertiesFormat.IPConfigurations = resp.SubnetPropertiesFormat.IPConfigurations 330 } 331 332 return &existingSubnet, nil 333 } 334 335 func expandAzureRmVirtualNetworkVirtualNetworkSecurityGroupNames(d *schema.ResourceData) ([]string, error) { 336 nsgNames := make([]string, 0) 337 338 if v, ok := d.GetOk("subnet"); ok { 339 subnets := v.(*schema.Set).List() 340 for _, subnet := range subnets { 341 subnet, ok := subnet.(map[string]interface{}) 342 if !ok { 343 return nil, fmt.Errorf("[ERROR] Subnet should be a Hash - was '%+v'", subnet) 344 } 345 346 networkSecurityGroupId := subnet["security_group"].(string) 347 if networkSecurityGroupId != "" { 348 nsgName, err := parseNetworkSecurityGroupName(networkSecurityGroupId) 349 if err != nil { 350 return nil, err 351 } 352 353 nsgNames = append(nsgNames, nsgName) 354 } 355 } 356 } 357 358 return nsgNames, nil 359 }