github.com/richardmarshall/terraform@v0.9.5-0.20170429023105-15704cc6ee35/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 _, err := secClient.CreateOrUpdate(resGroup, name, sg, make(chan struct{})) 150 if err != nil { 151 return err 152 } 153 154 read, err := secClient.Get(resGroup, name, "") 155 if err != nil { 156 return err 157 } 158 if read.ID == nil { 159 return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup) 160 } 161 162 log.Printf("[DEBUG] Waiting for NSG (%s) to become available", d.Get("name")) 163 stateConf := &resource.StateChangeConf{ 164 Pending: []string{"Updating", "Creating"}, 165 Target: []string{"Succeeded"}, 166 Refresh: networkSecurityGroupStateRefreshFunc(client, resGroup, name), 167 Timeout: 30 * time.Minute, 168 MinTimeout: 15 * time.Second, 169 } 170 if _, err := stateConf.WaitForState(); err != nil { 171 return fmt.Errorf("Error waiting for NSG (%s) to become available: %s", d.Get("name"), err) 172 } 173 174 d.SetId(*read.ID) 175 176 return resourceArmNetworkSecurityGroupRead(d, meta) 177 } 178 179 func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { 180 secGroupClient := meta.(*ArmClient).secGroupClient 181 182 id, err := parseAzureResourceID(d.Id()) 183 if err != nil { 184 return err 185 } 186 resGroup := id.ResourceGroup 187 name := id.Path["networkSecurityGroups"] 188 189 resp, err := secGroupClient.Get(resGroup, name, "") 190 if err != nil { 191 if resp.StatusCode == http.StatusNotFound { 192 d.SetId("") 193 return nil 194 } 195 return fmt.Errorf("Error making Read request on Azure Network Security Group %s: %s", name, err) 196 } 197 198 if resp.SecurityGroupPropertiesFormat.SecurityRules != nil { 199 d.Set("security_rule", flattenNetworkSecurityRules(resp.SecurityGroupPropertiesFormat.SecurityRules)) 200 } 201 202 d.Set("resource_group_name", resGroup) 203 d.Set("name", resp.Name) 204 d.Set("location", resp.Location) 205 flattenAndSetTags(d, resp.Tags) 206 207 return nil 208 } 209 210 func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { 211 secGroupClient := meta.(*ArmClient).secGroupClient 212 213 id, err := parseAzureResourceID(d.Id()) 214 if err != nil { 215 return err 216 } 217 resGroup := id.ResourceGroup 218 name := id.Path["networkSecurityGroups"] 219 220 _, err = secGroupClient.Delete(resGroup, name, make(chan struct{})) 221 222 return err 223 } 224 225 func resourceArmNetworkSecurityGroupRuleHash(v interface{}) int { 226 var buf bytes.Buffer 227 m := v.(map[string]interface{}) 228 buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) 229 buf.WriteString(fmt.Sprintf("%s-", m["source_port_range"].(string))) 230 buf.WriteString(fmt.Sprintf("%s-", m["destination_port_range"].(string))) 231 buf.WriteString(fmt.Sprintf("%s-", m["source_address_prefix"].(string))) 232 buf.WriteString(fmt.Sprintf("%s-", m["destination_address_prefix"].(string))) 233 buf.WriteString(fmt.Sprintf("%s-", m["access"].(string))) 234 buf.WriteString(fmt.Sprintf("%d-", m["priority"].(int))) 235 buf.WriteString(fmt.Sprintf("%s-", m["direction"].(string))) 236 237 return hashcode.String(buf.String()) 238 } 239 240 func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]interface{} { 241 result := make([]map[string]interface{}, 0, len(*rules)) 242 for _, rule := range *rules { 243 sgRule := make(map[string]interface{}) 244 sgRule["name"] = *rule.Name 245 sgRule["destination_address_prefix"] = *rule.SecurityRulePropertiesFormat.DestinationAddressPrefix 246 sgRule["destination_port_range"] = *rule.SecurityRulePropertiesFormat.DestinationPortRange 247 sgRule["source_address_prefix"] = *rule.SecurityRulePropertiesFormat.SourceAddressPrefix 248 sgRule["source_port_range"] = *rule.SecurityRulePropertiesFormat.SourcePortRange 249 sgRule["priority"] = int(*rule.SecurityRulePropertiesFormat.Priority) 250 sgRule["access"] = rule.SecurityRulePropertiesFormat.Access 251 sgRule["direction"] = rule.SecurityRulePropertiesFormat.Direction 252 sgRule["protocol"] = rule.SecurityRulePropertiesFormat.Protocol 253 254 if rule.SecurityRulePropertiesFormat.Description != nil { 255 sgRule["description"] = *rule.SecurityRulePropertiesFormat.Description 256 } 257 258 result = append(result, sgRule) 259 } 260 return result 261 } 262 263 func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) { 264 sgRules := d.Get("security_rule").(*schema.Set).List() 265 rules := make([]network.SecurityRule, 0, len(sgRules)) 266 267 for _, sgRaw := range sgRules { 268 data := sgRaw.(map[string]interface{}) 269 270 source_port_range := data["source_port_range"].(string) 271 destination_port_range := data["destination_port_range"].(string) 272 source_address_prefix := data["source_address_prefix"].(string) 273 destination_address_prefix := data["destination_address_prefix"].(string) 274 priority := int32(data["priority"].(int)) 275 276 properties := network.SecurityRulePropertiesFormat{ 277 SourcePortRange: &source_port_range, 278 DestinationPortRange: &destination_port_range, 279 SourceAddressPrefix: &source_address_prefix, 280 DestinationAddressPrefix: &destination_address_prefix, 281 Priority: &priority, 282 Access: network.SecurityRuleAccess(data["access"].(string)), 283 Direction: network.SecurityRuleDirection(data["direction"].(string)), 284 Protocol: network.SecurityRuleProtocol(data["protocol"].(string)), 285 } 286 287 if v := data["description"].(string); v != "" { 288 properties.Description = &v 289 } 290 291 name := data["name"].(string) 292 rule := network.SecurityRule{ 293 Name: &name, 294 SecurityRulePropertiesFormat: &properties, 295 } 296 297 rules = append(rules, rule) 298 } 299 300 return rules, nil 301 } 302 303 func networkSecurityGroupStateRefreshFunc(client *ArmClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { 304 return func() (interface{}, string, error) { 305 res, err := client.secGroupClient.Get(resourceGroupName, sgName, "") 306 if err != nil { 307 return nil, "", fmt.Errorf("Error issuing read request in networkSecurityGroupStateRefreshFunc to Azure ARM for NSG '%s' (RG: '%s'): %s", sgName, resourceGroupName, err) 308 } 309 310 return res, *res.SecurityGroupPropertiesFormat.ProvisioningState, nil 311 } 312 }