github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 }, 70 71 "source_port_range": { 72 Type: schema.TypeString, 73 Required: true, 74 }, 75 76 "destination_port_range": { 77 Type: schema.TypeString, 78 Required: true, 79 }, 80 81 "source_address_prefix": { 82 Type: schema.TypeString, 83 Required: true, 84 }, 85 86 "destination_address_prefix": { 87 Type: schema.TypeString, 88 Required: true, 89 }, 90 91 "access": { 92 Type: schema.TypeString, 93 Required: true, 94 ValidateFunc: validateNetworkSecurityRuleAccess, 95 }, 96 97 "priority": { 98 Type: schema.TypeInt, 99 Required: true, 100 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 101 value := v.(int) 102 if value < 100 || value > 4096 { 103 errors = append(errors, fmt.Errorf( 104 "The `priority` can only be between 100 and 4096")) 105 } 106 return 107 }, 108 }, 109 110 "direction": { 111 Type: schema.TypeString, 112 Required: true, 113 ValidateFunc: validateNetworkSecurityRuleDirection, 114 }, 115 }, 116 }, 117 Set: resourceArmNetworkSecurityGroupRuleHash, 118 }, 119 120 "tags": tagsSchema(), 121 }, 122 } 123 } 124 125 func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { 126 client := meta.(*ArmClient) 127 secClient := client.secGroupClient 128 129 name := d.Get("name").(string) 130 location := d.Get("location").(string) 131 resGroup := d.Get("resource_group_name").(string) 132 tags := d.Get("tags").(map[string]interface{}) 133 134 sgRules, sgErr := expandAzureRmSecurityRules(d) 135 if sgErr != nil { 136 return fmt.Errorf("Error Building list of Network Security Group Rules: %s", sgErr) 137 } 138 139 sg := network.SecurityGroup{ 140 Name: &name, 141 Location: &location, 142 SecurityGroupPropertiesFormat: &network.SecurityGroupPropertiesFormat{ 143 SecurityRules: &sgRules, 144 }, 145 Tags: expandTags(tags), 146 } 147 148 _, err := secClient.CreateOrUpdate(resGroup, name, sg, make(chan struct{})) 149 if err != nil { 150 return err 151 } 152 153 read, err := secClient.Get(resGroup, name, "") 154 if err != nil { 155 return err 156 } 157 if read.ID == nil { 158 return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup) 159 } 160 161 log.Printf("[DEBUG] Waiting for NSG (%s) to become available", d.Get("name")) 162 stateConf := &resource.StateChangeConf{ 163 Pending: []string{"Updating", "Creating"}, 164 Target: []string{"Succeeded"}, 165 Refresh: networkSecurityGroupStateRefreshFunc(client, resGroup, name), 166 Timeout: 30 * time.Minute, 167 MinTimeout: 15 * time.Second, 168 } 169 if _, err := stateConf.WaitForState(); err != nil { 170 return fmt.Errorf("Error waiting for NSG (%s) to become available: %s", d.Get("name"), err) 171 } 172 173 d.SetId(*read.ID) 174 175 return resourceArmNetworkSecurityGroupRead(d, meta) 176 } 177 178 func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { 179 secGroupClient := meta.(*ArmClient).secGroupClient 180 181 id, err := parseAzureResourceID(d.Id()) 182 if err != nil { 183 return err 184 } 185 resGroup := id.ResourceGroup 186 name := id.Path["networkSecurityGroups"] 187 188 resp, err := secGroupClient.Get(resGroup, name, "") 189 if err != nil { 190 if resp.StatusCode == http.StatusNotFound { 191 d.SetId("") 192 return nil 193 } 194 return fmt.Errorf("Error making Read request on Azure Network Security Group %s: %s", name, err) 195 } 196 197 if resp.SecurityGroupPropertiesFormat.SecurityRules != nil { 198 d.Set("security_rule", flattenNetworkSecurityRules(resp.SecurityGroupPropertiesFormat.SecurityRules)) 199 } 200 201 d.Set("resource_group_name", resGroup) 202 d.Set("name", resp.Name) 203 d.Set("location", resp.Location) 204 flattenAndSetTags(d, resp.Tags) 205 206 return nil 207 } 208 209 func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { 210 secGroupClient := meta.(*ArmClient).secGroupClient 211 212 id, err := parseAzureResourceID(d.Id()) 213 if err != nil { 214 return err 215 } 216 resGroup := id.ResourceGroup 217 name := id.Path["networkSecurityGroups"] 218 219 _, err = secGroupClient.Delete(resGroup, name, make(chan struct{})) 220 221 return err 222 } 223 224 func resourceArmNetworkSecurityGroupRuleHash(v interface{}) int { 225 var buf bytes.Buffer 226 m := v.(map[string]interface{}) 227 buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) 228 buf.WriteString(fmt.Sprintf("%s-", m["source_port_range"].(string))) 229 buf.WriteString(fmt.Sprintf("%s-", m["destination_port_range"].(string))) 230 buf.WriteString(fmt.Sprintf("%s-", m["source_address_prefix"].(string))) 231 buf.WriteString(fmt.Sprintf("%s-", m["destination_address_prefix"].(string))) 232 buf.WriteString(fmt.Sprintf("%s-", m["access"].(string))) 233 buf.WriteString(fmt.Sprintf("%d-", m["priority"].(int))) 234 buf.WriteString(fmt.Sprintf("%s-", m["direction"].(string))) 235 236 return hashcode.String(buf.String()) 237 } 238 239 func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]interface{} { 240 result := make([]map[string]interface{}, 0, len(*rules)) 241 for _, rule := range *rules { 242 sgRule := make(map[string]interface{}) 243 sgRule["name"] = *rule.Name 244 sgRule["destination_address_prefix"] = *rule.SecurityRulePropertiesFormat.DestinationAddressPrefix 245 sgRule["destination_port_range"] = *rule.SecurityRulePropertiesFormat.DestinationPortRange 246 sgRule["source_address_prefix"] = *rule.SecurityRulePropertiesFormat.SourceAddressPrefix 247 sgRule["source_port_range"] = *rule.SecurityRulePropertiesFormat.SourcePortRange 248 sgRule["priority"] = int(*rule.SecurityRulePropertiesFormat.Priority) 249 sgRule["access"] = rule.SecurityRulePropertiesFormat.Access 250 sgRule["direction"] = rule.SecurityRulePropertiesFormat.Direction 251 sgRule["protocol"] = rule.SecurityRulePropertiesFormat.Protocol 252 253 if rule.SecurityRulePropertiesFormat.Description != nil { 254 sgRule["description"] = *rule.SecurityRulePropertiesFormat.Description 255 } 256 257 result = append(result, sgRule) 258 } 259 return result 260 } 261 262 func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) { 263 sgRules := d.Get("security_rule").(*schema.Set).List() 264 rules := make([]network.SecurityRule, 0, len(sgRules)) 265 266 for _, sgRaw := range sgRules { 267 data := sgRaw.(map[string]interface{}) 268 269 source_port_range := data["source_port_range"].(string) 270 destination_port_range := data["destination_port_range"].(string) 271 source_address_prefix := data["source_address_prefix"].(string) 272 destination_address_prefix := data["destination_address_prefix"].(string) 273 priority := int32(data["priority"].(int)) 274 275 properties := network.SecurityRulePropertiesFormat{ 276 SourcePortRange: &source_port_range, 277 DestinationPortRange: &destination_port_range, 278 SourceAddressPrefix: &source_address_prefix, 279 DestinationAddressPrefix: &destination_address_prefix, 280 Priority: &priority, 281 Access: network.SecurityRuleAccess(data["access"].(string)), 282 Direction: network.SecurityRuleDirection(data["direction"].(string)), 283 Protocol: network.SecurityRuleProtocol(data["protocol"].(string)), 284 } 285 286 if v := data["description"].(string); v != "" { 287 properties.Description = &v 288 } 289 290 name := data["name"].(string) 291 rule := network.SecurityRule{ 292 Name: &name, 293 SecurityRulePropertiesFormat: &properties, 294 } 295 296 rules = append(rules, rule) 297 } 298 299 return rules, nil 300 } 301 302 func networkSecurityGroupStateRefreshFunc(client *ArmClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { 303 return func() (interface{}, string, error) { 304 res, err := client.secGroupClient.Get(resourceGroupName, sgName, "") 305 if err != nil { 306 return nil, "", fmt.Errorf("Error issuing read request in networkSecurityGroupStateRefreshFunc to Azure ARM for NSG '%s' (RG: '%s'): %s", sgName, resourceGroupName, err) 307 } 308 309 return res, *res.SecurityGroupPropertiesFormat.ProvisioningState, nil 310 } 311 }