github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/azurerm/resource_arm_network_security_group.go (about) 1 package azurerm 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "net/http" 8 "time" 9 10 "github.com/Azure/azure-sdk-for-go/arm/network" 11 "github.com/hashicorp/terraform/helper/hashcode" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceArmNetworkSecurityGroup() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceArmNetworkSecurityGroupCreate, 19 Read: resourceArmNetworkSecurityGroupRead, 20 Update: resourceArmNetworkSecurityGroupCreate, 21 Delete: resourceArmNetworkSecurityGroupDelete, 22 Importer: &schema.ResourceImporter{ 23 State: schema.ImportStatePassthrough, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "name": { 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 }, 32 33 "location": locationSchema(), 34 35 "resource_group_name": { 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 41 "security_rule": { 42 Type: schema.TypeSet, 43 Optional: true, 44 Computed: true, 45 Elem: &schema.Resource{ 46 Schema: map[string]*schema.Schema{ 47 "name": { 48 Type: schema.TypeString, 49 Required: true, 50 }, 51 52 "description": { 53 Type: schema.TypeString, 54 Optional: true, 55 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 56 value := v.(string) 57 if len(value) > 140 { 58 errors = append(errors, fmt.Errorf( 59 "The network security rule description can be no longer than 140 chars")) 60 } 61 return 62 }, 63 }, 64 65 "protocol": { 66 Type: schema.TypeString, 67 Required: true, 68 ValidateFunc: validateNetworkSecurityRuleProtocol, 69 StateFunc: ignoreCaseStateFunc, 70 }, 71 72 "source_port_range": { 73 Type: schema.TypeString, 74 Required: true, 75 }, 76 77 "destination_port_range": { 78 Type: schema.TypeString, 79 Required: true, 80 }, 81 82 "source_address_prefix": { 83 Type: schema.TypeString, 84 Required: true, 85 }, 86 87 "destination_address_prefix": { 88 Type: schema.TypeString, 89 Required: true, 90 }, 91 92 "access": { 93 Type: schema.TypeString, 94 Required: true, 95 ValidateFunc: validateNetworkSecurityRuleAccess, 96 }, 97 98 "priority": { 99 Type: schema.TypeInt, 100 Required: true, 101 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 102 value := v.(int) 103 if value < 100 || value > 4096 { 104 errors = append(errors, fmt.Errorf( 105 "The `priority` can only be between 100 and 4096")) 106 } 107 return 108 }, 109 }, 110 111 "direction": { 112 Type: schema.TypeString, 113 Required: true, 114 ValidateFunc: validateNetworkSecurityRuleDirection, 115 }, 116 }, 117 }, 118 Set: resourceArmNetworkSecurityGroupRuleHash, 119 }, 120 121 "tags": tagsSchema(), 122 }, 123 } 124 } 125 126 func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { 127 client := meta.(*ArmClient) 128 secClient := client.secGroupClient 129 130 name := d.Get("name").(string) 131 location := d.Get("location").(string) 132 resGroup := d.Get("resource_group_name").(string) 133 tags := d.Get("tags").(map[string]interface{}) 134 135 sgRules, sgErr := expandAzureRmSecurityRules(d) 136 if sgErr != nil { 137 return fmt.Errorf("Error Building list of Network Security Group Rules: %s", sgErr) 138 } 139 140 sg := network.SecurityGroup{ 141 Name: &name, 142 Location: &location, 143 SecurityGroupPropertiesFormat: &network.SecurityGroupPropertiesFormat{ 144 SecurityRules: &sgRules, 145 }, 146 Tags: expandTags(tags), 147 } 148 149 _, error := secClient.CreateOrUpdate(resGroup, name, sg, make(chan struct{})) 150 err := <-error 151 if err != nil { 152 return err 153 } 154 155 read, err := secClient.Get(resGroup, name, "") 156 if err != nil { 157 return err 158 } 159 if read.ID == nil { 160 return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup) 161 } 162 163 log.Printf("[DEBUG] Waiting for NSG (%s) to become available", d.Get("name")) 164 stateConf := &resource.StateChangeConf{ 165 Pending: []string{"Updating", "Creating"}, 166 Target: []string{"Succeeded"}, 167 Refresh: networkSecurityGroupStateRefreshFunc(client, resGroup, name), 168 Timeout: 30 * time.Minute, 169 MinTimeout: 15 * time.Second, 170 } 171 if _, err := stateConf.WaitForState(); err != nil { 172 return fmt.Errorf("Error waiting for NSG (%s) to become available: %s", d.Get("name"), err) 173 } 174 175 d.SetId(*read.ID) 176 177 return resourceArmNetworkSecurityGroupRead(d, meta) 178 } 179 180 func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { 181 secGroupClient := meta.(*ArmClient).secGroupClient 182 183 id, err := parseAzureResourceID(d.Id()) 184 if err != nil { 185 return err 186 } 187 resGroup := id.ResourceGroup 188 name := id.Path["networkSecurityGroups"] 189 190 resp, err := secGroupClient.Get(resGroup, name, "") 191 if err != nil { 192 if resp.StatusCode == http.StatusNotFound { 193 d.SetId("") 194 return nil 195 } 196 return fmt.Errorf("Error making Read request on Azure Network Security Group %s: %s", name, err) 197 } 198 199 if resp.SecurityGroupPropertiesFormat.SecurityRules != nil { 200 d.Set("security_rule", flattenNetworkSecurityRules(resp.SecurityGroupPropertiesFormat.SecurityRules)) 201 } 202 203 d.Set("resource_group_name", resGroup) 204 d.Set("name", resp.Name) 205 d.Set("location", resp.Location) 206 flattenAndSetTags(d, resp.Tags) 207 208 return nil 209 } 210 211 func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { 212 secGroupClient := meta.(*ArmClient).secGroupClient 213 214 id, err := parseAzureResourceID(d.Id()) 215 if err != nil { 216 return err 217 } 218 resGroup := id.ResourceGroup 219 name := id.Path["networkSecurityGroups"] 220 221 _, error := secGroupClient.Delete(resGroup, name, make(chan struct{})) 222 err = <-error 223 224 return err 225 } 226 227 func resourceArmNetworkSecurityGroupRuleHash(v interface{}) int { 228 var buf bytes.Buffer 229 m := v.(map[string]interface{}) 230 buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) 231 buf.WriteString(fmt.Sprintf("%s-", m["source_port_range"].(string))) 232 buf.WriteString(fmt.Sprintf("%s-", m["destination_port_range"].(string))) 233 buf.WriteString(fmt.Sprintf("%s-", m["source_address_prefix"].(string))) 234 buf.WriteString(fmt.Sprintf("%s-", m["destination_address_prefix"].(string))) 235 buf.WriteString(fmt.Sprintf("%s-", m["access"].(string))) 236 buf.WriteString(fmt.Sprintf("%d-", m["priority"].(int))) 237 buf.WriteString(fmt.Sprintf("%s-", m["direction"].(string))) 238 239 return hashcode.String(buf.String()) 240 } 241 242 func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]interface{} { 243 result := make([]map[string]interface{}, 0, len(*rules)) 244 for _, rule := range *rules { 245 sgRule := make(map[string]interface{}) 246 sgRule["name"] = *rule.Name 247 sgRule["destination_address_prefix"] = *rule.SecurityRulePropertiesFormat.DestinationAddressPrefix 248 sgRule["destination_port_range"] = *rule.SecurityRulePropertiesFormat.DestinationPortRange 249 sgRule["source_address_prefix"] = *rule.SecurityRulePropertiesFormat.SourceAddressPrefix 250 sgRule["source_port_range"] = *rule.SecurityRulePropertiesFormat.SourcePortRange 251 sgRule["priority"] = int(*rule.SecurityRulePropertiesFormat.Priority) 252 sgRule["access"] = rule.SecurityRulePropertiesFormat.Access 253 sgRule["direction"] = rule.SecurityRulePropertiesFormat.Direction 254 sgRule["protocol"] = rule.SecurityRulePropertiesFormat.Protocol 255 256 if rule.SecurityRulePropertiesFormat.Description != nil { 257 sgRule["description"] = *rule.SecurityRulePropertiesFormat.Description 258 } 259 260 result = append(result, sgRule) 261 } 262 return result 263 } 264 265 func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) { 266 sgRules := d.Get("security_rule").(*schema.Set).List() 267 rules := make([]network.SecurityRule, 0, len(sgRules)) 268 269 for _, sgRaw := range sgRules { 270 data := sgRaw.(map[string]interface{}) 271 272 source_port_range := data["source_port_range"].(string) 273 destination_port_range := data["destination_port_range"].(string) 274 source_address_prefix := data["source_address_prefix"].(string) 275 destination_address_prefix := data["destination_address_prefix"].(string) 276 priority := int32(data["priority"].(int)) 277 278 properties := network.SecurityRulePropertiesFormat{ 279 SourcePortRange: &source_port_range, 280 DestinationPortRange: &destination_port_range, 281 SourceAddressPrefix: &source_address_prefix, 282 DestinationAddressPrefix: &destination_address_prefix, 283 Priority: &priority, 284 Access: network.SecurityRuleAccess(data["access"].(string)), 285 Direction: network.SecurityRuleDirection(data["direction"].(string)), 286 Protocol: network.SecurityRuleProtocol(data["protocol"].(string)), 287 } 288 289 if v := data["description"].(string); v != "" { 290 properties.Description = &v 291 } 292 293 name := data["name"].(string) 294 rule := network.SecurityRule{ 295 Name: &name, 296 SecurityRulePropertiesFormat: &properties, 297 } 298 299 rules = append(rules, rule) 300 } 301 302 return rules, nil 303 } 304 305 func networkSecurityGroupStateRefreshFunc(client *ArmClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { 306 return func() (interface{}, string, error) { 307 res, err := client.secGroupClient.Get(resourceGroupName, sgName, "") 308 if err != nil { 309 return nil, "", fmt.Errorf("Error issuing read request in networkSecurityGroupStateRefreshFunc to Azure ARM for NSG '%s' (RG: '%s'): %s", sgName, resourceGroupName, err) 310 } 311 312 return res, *res.SecurityGroupPropertiesFormat.ProvisioningState, nil 313 } 314 }