github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/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 23 Schema: map[string]*schema.Schema{ 24 "name": { 25 Type: schema.TypeString, 26 Required: true, 27 ForceNew: true, 28 }, 29 30 "location": { 31 Type: schema.TypeString, 32 Required: true, 33 ForceNew: true, 34 StateFunc: azureRMNormalizeLocation, 35 }, 36 37 "resource_group_name": { 38 Type: schema.TypeString, 39 Required: true, 40 ForceNew: true, 41 }, 42 43 "security_rule": { 44 Type: schema.TypeSet, 45 Optional: true, 46 Computed: true, 47 Elem: &schema.Resource{ 48 Schema: map[string]*schema.Schema{ 49 "name": { 50 Type: schema.TypeString, 51 Required: true, 52 }, 53 54 "description": { 55 Type: schema.TypeString, 56 Optional: true, 57 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 58 value := v.(string) 59 if len(value) > 140 { 60 errors = append(errors, fmt.Errorf( 61 "The network security rule description can be no longer than 140 chars")) 62 } 63 return 64 }, 65 }, 66 67 "protocol": { 68 Type: schema.TypeString, 69 Required: true, 70 ValidateFunc: validateNetworkSecurityRuleProtocol, 71 }, 72 73 "source_port_range": { 74 Type: schema.TypeString, 75 Required: true, 76 }, 77 78 "destination_port_range": { 79 Type: schema.TypeString, 80 Required: true, 81 }, 82 83 "source_address_prefix": { 84 Type: schema.TypeString, 85 Required: true, 86 }, 87 88 "destination_address_prefix": { 89 Type: schema.TypeString, 90 Required: true, 91 }, 92 93 "access": { 94 Type: schema.TypeString, 95 Required: true, 96 ValidateFunc: validateNetworkSecurityRuleAccess, 97 }, 98 99 "priority": { 100 Type: schema.TypeInt, 101 Required: true, 102 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 103 value := v.(int) 104 if value < 100 || value > 4096 { 105 errors = append(errors, fmt.Errorf( 106 "The `priority` can only be between 100 and 4096")) 107 } 108 return 109 }, 110 }, 111 112 "direction": { 113 Type: schema.TypeString, 114 Required: true, 115 ValidateFunc: validateNetworkSecurityRuleDirection, 116 }, 117 }, 118 }, 119 Set: resourceArmNetworkSecurityGroupRuleHash, 120 }, 121 122 "tags": tagsSchema(), 123 }, 124 } 125 } 126 127 func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { 128 client := meta.(*ArmClient) 129 secClient := client.secGroupClient 130 131 name := d.Get("name").(string) 132 location := d.Get("location").(string) 133 resGroup := d.Get("resource_group_name").(string) 134 tags := d.Get("tags").(map[string]interface{}) 135 136 sgRules, sgErr := expandAzureRmSecurityRules(d) 137 if sgErr != nil { 138 return fmt.Errorf("Error Building list of Network Security Group Rules: %s", sgErr) 139 } 140 141 sg := network.SecurityGroup{ 142 Name: &name, 143 Location: &location, 144 Properties: &network.SecurityGroupPropertiesFormat{ 145 SecurityRules: &sgRules, 146 }, 147 Tags: expandTags(tags), 148 } 149 150 _, err := secClient.CreateOrUpdate(resGroup, name, sg, make(chan struct{})) 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 resp.StatusCode == http.StatusNotFound { 192 d.SetId("") 193 return nil 194 } 195 if err != nil { 196 return fmt.Errorf("Error making Read request on Azure Network Security Group %s: %s", name, err) 197 } 198 199 if resp.Properties.SecurityRules != nil { 200 d.Set("security_rule", flattenNetworkSecurityRules(resp.Properties.SecurityRules)) 201 } 202 203 flattenAndSetTags(d, resp.Tags) 204 205 return nil 206 } 207 208 func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { 209 secGroupClient := meta.(*ArmClient).secGroupClient 210 211 id, err := parseAzureResourceID(d.Id()) 212 if err != nil { 213 return err 214 } 215 resGroup := id.ResourceGroup 216 name := id.Path["networkSecurityGroups"] 217 218 _, err = secGroupClient.Delete(resGroup, name, make(chan struct{})) 219 220 return err 221 } 222 223 func resourceArmNetworkSecurityGroupRuleHash(v interface{}) int { 224 var buf bytes.Buffer 225 m := v.(map[string]interface{}) 226 buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) 227 buf.WriteString(fmt.Sprintf("%s-", m["source_port_range"].(string))) 228 buf.WriteString(fmt.Sprintf("%s-", m["destination_port_range"].(string))) 229 buf.WriteString(fmt.Sprintf("%s-", m["source_address_prefix"].(string))) 230 buf.WriteString(fmt.Sprintf("%s-", m["destination_address_prefix"].(string))) 231 buf.WriteString(fmt.Sprintf("%s-", m["access"].(string))) 232 buf.WriteString(fmt.Sprintf("%d-", m["priority"].(int))) 233 buf.WriteString(fmt.Sprintf("%s-", m["direction"].(string))) 234 235 return hashcode.String(buf.String()) 236 } 237 238 func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]interface{} { 239 result := make([]map[string]interface{}, 0, len(*rules)) 240 for _, rule := range *rules { 241 sgRule := make(map[string]interface{}) 242 sgRule["name"] = *rule.Name 243 sgRule["destination_address_prefix"] = *rule.Properties.DestinationAddressPrefix 244 sgRule["destination_port_range"] = *rule.Properties.DestinationPortRange 245 sgRule["source_address_prefix"] = *rule.Properties.SourceAddressPrefix 246 sgRule["source_port_range"] = *rule.Properties.SourcePortRange 247 sgRule["priority"] = int(*rule.Properties.Priority) 248 sgRule["access"] = rule.Properties.Access 249 sgRule["direction"] = rule.Properties.Direction 250 sgRule["protocol"] = rule.Properties.Protocol 251 252 if rule.Properties.Description != nil { 253 sgRule["description"] = *rule.Properties.Description 254 } 255 256 result = append(result, sgRule) 257 } 258 return result 259 } 260 261 func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) { 262 sgRules := d.Get("security_rule").(*schema.Set).List() 263 rules := make([]network.SecurityRule, 0, len(sgRules)) 264 265 for _, sgRaw := range sgRules { 266 data := sgRaw.(map[string]interface{}) 267 268 source_port_range := data["source_port_range"].(string) 269 destination_port_range := data["destination_port_range"].(string) 270 source_address_prefix := data["source_address_prefix"].(string) 271 destination_address_prefix := data["destination_address_prefix"].(string) 272 priority := int32(data["priority"].(int)) 273 274 properties := network.SecurityRulePropertiesFormat{ 275 SourcePortRange: &source_port_range, 276 DestinationPortRange: &destination_port_range, 277 SourceAddressPrefix: &source_address_prefix, 278 DestinationAddressPrefix: &destination_address_prefix, 279 Priority: &priority, 280 Access: network.SecurityRuleAccess(data["access"].(string)), 281 Direction: network.SecurityRuleDirection(data["direction"].(string)), 282 Protocol: network.SecurityRuleProtocol(data["protocol"].(string)), 283 } 284 285 if v := data["description"].(string); v != "" { 286 properties.Description = &v 287 } 288 289 name := data["name"].(string) 290 rule := network.SecurityRule{ 291 Name: &name, 292 Properties: &properties, 293 } 294 295 rules = append(rules, rule) 296 } 297 298 return rules, nil 299 } 300 301 func networkSecurityGroupStateRefreshFunc(client *ArmClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { 302 return func() (interface{}, string, error) { 303 res, err := client.secGroupClient.Get(resourceGroupName, sgName, "") 304 if err != nil { 305 return nil, "", fmt.Errorf("Error issuing read request in networkSecurityGroupStateRefreshFunc to Azure ARM for NSG '%s' (RG: '%s'): %s", sgName, resourceGroupName, err) 306 } 307 308 return res, *res.Properties.ProvisioningState, nil 309 } 310 }