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