github.com/ggiamarchi/terraform@v0.3.7-0.20150607194748-ed2a66a46a71/builtin/providers/azure/resource_azure_security_group.go (about) 1 package azure 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "strconv" 8 9 "github.com/hashicorp/terraform/helper/hashcode" 10 "github.com/hashicorp/terraform/helper/schema" 11 "github.com/svanharmelen/azure-sdk-for-go/management" 12 "github.com/svanharmelen/azure-sdk-for-go/management/networksecuritygroup" 13 ) 14 15 func resourceAzureSecurityGroup() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAzureSecurityGroupCreate, 18 Read: resourceAzureSecurityGroupRead, 19 Update: resourceAzureSecurityGroupUpdate, 20 Delete: resourceAzureSecurityGroupDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "name": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 }, 28 29 "label": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 Computed: true, 33 ForceNew: true, 34 }, 35 36 "location": &schema.Schema{ 37 Type: schema.TypeString, 38 Required: true, 39 ForceNew: true, 40 }, 41 42 "rule": &schema.Schema{ 43 Type: schema.TypeSet, 44 Required: true, 45 Elem: &schema.Resource{ 46 Schema: map[string]*schema.Schema{ 47 "name": &schema.Schema{ 48 Type: schema.TypeString, 49 Required: true, 50 }, 51 52 "type": &schema.Schema{ 53 Type: schema.TypeString, 54 Optional: true, 55 Default: "Inbound", 56 }, 57 58 "priority": &schema.Schema{ 59 Type: schema.TypeInt, 60 Required: true, 61 }, 62 63 "action": &schema.Schema{ 64 Type: schema.TypeString, 65 Optional: true, 66 Default: "Allow", 67 }, 68 69 "source_cidr": &schema.Schema{ 70 Type: schema.TypeString, 71 Required: true, 72 }, 73 74 "source_port": &schema.Schema{ 75 Type: schema.TypeString, 76 Required: true, 77 }, 78 79 "destination_cidr": &schema.Schema{ 80 Type: schema.TypeString, 81 Required: true, 82 }, 83 84 "destination_port": &schema.Schema{ 85 Type: schema.TypeString, 86 Required: true, 87 }, 88 89 "protocol": &schema.Schema{ 90 Type: schema.TypeString, 91 Optional: true, 92 Default: "TCP", 93 }, 94 }, 95 }, 96 Set: resourceAzureSecurityGroupRuleHash, 97 }, 98 }, 99 } 100 } 101 102 func resourceAzureSecurityGroupCreate(d *schema.ResourceData, meta interface{}) (err error) { 103 mc := meta.(*Client).mgmtClient 104 105 name := d.Get("name").(string) 106 107 // Compute/set the label 108 label := d.Get("label").(string) 109 if label == "" { 110 label = name 111 } 112 113 req, err := networksecuritygroup.NewClient(mc).CreateNetworkSecurityGroup( 114 name, 115 label, 116 d.Get("location").(string), 117 ) 118 if err != nil { 119 return fmt.Errorf("Error creating Network Security Group %s: %s", name, err) 120 } 121 122 if err := mc.WaitForOperation(req, nil); err != nil { 123 return fmt.Errorf( 124 "Error waiting for Network Security Group %s to be created: %s", name, err) 125 } 126 127 d.SetId(name) 128 129 // Create all rules that are configured 130 if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 { 131 132 // Create an empty schema.Set to hold all rules 133 rules := &schema.Set{ 134 F: resourceAzureSecurityGroupRuleHash, 135 } 136 137 for _, rule := range rs.List() { 138 // Create a single rule 139 err := resourceAzureSecurityGroupRuleCreate(d, meta, rule.(map[string]interface{})) 140 141 // We need to update this first to preserve the correct state 142 rules.Add(rule) 143 d.Set("rule", rules) 144 145 if err != nil { 146 return err 147 } 148 } 149 } 150 151 return resourceAzureSecurityGroupRead(d, meta) 152 } 153 154 func resourceAzureSecurityGroupRuleCreate( 155 d *schema.ResourceData, 156 meta interface{}, 157 rule map[string]interface{}) error { 158 mc := meta.(*Client).mgmtClient 159 160 // Make sure all required parameters are there 161 if err := verifySecurityGroupRuleParams(rule); err != nil { 162 return err 163 } 164 165 name := rule["name"].(string) 166 167 // Create the rule 168 req, err := networksecuritygroup.NewClient(mc).SetNetworkSecurityGroupRule(d.Id(), 169 networksecuritygroup.RuleRequest{ 170 Name: name, 171 Type: networksecuritygroup.RuleType(rule["type"].(string)), 172 Priority: rule["priority"].(int), 173 Action: networksecuritygroup.RuleAction(rule["action"].(string)), 174 SourceAddressPrefix: rule["source_cidr"].(string), 175 SourcePortRange: rule["source_port"].(string), 176 DestinationAddressPrefix: rule["destination_cidr"].(string), 177 DestinationPortRange: rule["destination_port"].(string), 178 Protocol: networksecuritygroup.RuleProtocol(rule["protocol"].(string)), 179 }, 180 ) 181 if err != nil { 182 return fmt.Errorf("Error creating Network Security Group rule %s: %s", name, err) 183 } 184 185 if err := mc.WaitForOperation(req, nil); err != nil { 186 return fmt.Errorf( 187 "Error waiting for Network Security Group rule %s to be created: %s", name, err) 188 } 189 190 return nil 191 } 192 193 func resourceAzureSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { 194 mc := meta.(*Client).mgmtClient 195 196 sg, err := networksecuritygroup.NewClient(mc).GetNetworkSecurityGroup(d.Id()) 197 if err != nil { 198 if management.IsResourceNotFoundError(err) { 199 d.SetId("") 200 return nil 201 } 202 return fmt.Errorf("Error retrieving Network Security Group %s: %s", d.Id(), err) 203 } 204 205 d.Set("label", sg.Label) 206 d.Set("location", sg.Location) 207 208 // Create an empty schema.Set to hold all rules 209 rules := &schema.Set{ 210 F: resourceAzureSecurityGroupRuleHash, 211 } 212 213 for _, r := range sg.Rules { 214 if !r.IsDefault { 215 rule := map[string]interface{}{ 216 "name": r.Name, 217 "type": string(r.Type), 218 "priority": r.Priority, 219 "action": string(r.Action), 220 "source_cidr": r.SourceAddressPrefix, 221 "source_port": r.SourcePortRange, 222 "destination_cidr": r.DestinationAddressPrefix, 223 "destination_port": r.DestinationPortRange, 224 "protocol": string(r.Protocol), 225 } 226 rules.Add(rule) 227 } 228 } 229 230 d.Set("rule", rules) 231 232 return nil 233 } 234 235 func resourceAzureSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error { 236 // Check if the rule set as a whole has changed 237 if d.HasChange("rule") { 238 o, n := d.GetChange("rule") 239 ors := o.(*schema.Set).Difference(n.(*schema.Set)) 240 nrs := n.(*schema.Set).Difference(o.(*schema.Set)) 241 242 // Now first loop through all the old rules and delete any obsolete ones 243 for _, rule := range ors.List() { 244 // Delete the rule as it no longer exists in the config 245 err := resourceAzureSecurityGroupRuleDelete(d, meta, rule.(map[string]interface{})) 246 if err != nil { 247 return err 248 } 249 } 250 251 // Make sure we save the state of the currently configured rules 252 rules := o.(*schema.Set).Intersection(n.(*schema.Set)) 253 d.Set("rule", rules) 254 255 // Then loop through al the currently configured rules and create the new ones 256 for _, rule := range nrs.List() { 257 err := resourceAzureSecurityGroupRuleCreate(d, meta, rule.(map[string]interface{})) 258 259 // We need to update this first to preserve the correct state 260 rules.Add(rule) 261 d.Set("rule", rules) 262 263 if err != nil { 264 return err 265 } 266 } 267 } 268 269 return resourceAzureSecurityGroupRead(d, meta) 270 } 271 272 func resourceAzureSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { 273 mc := meta.(*Client).mgmtClient 274 275 log.Printf("[DEBUG] Deleting Network Security Group: %s", d.Id()) 276 req, err := networksecuritygroup.NewClient(mc).DeleteNetworkSecurityGroup(d.Id()) 277 if err != nil { 278 return fmt.Errorf("Error deleting Network Security Group %s: %s", d.Id(), err) 279 } 280 281 // Wait until the network security group is deleted 282 if err := mc.WaitForOperation(req, nil); err != nil { 283 return fmt.Errorf( 284 "Error waiting for Network Security Group %s to be deleted: %s", d.Id(), err) 285 } 286 287 d.SetId("") 288 289 return nil 290 } 291 292 func resourceAzureSecurityGroupRuleDelete( 293 d *schema.ResourceData, meta interface{}, rule map[string]interface{}) error { 294 mc := meta.(*Client).mgmtClient 295 296 name := rule["name"].(string) 297 298 // Delete the rule 299 req, err := networksecuritygroup.NewClient(mc).DeleteNetworkSecurityGroupRule(d.Id(), name) 300 if err != nil { 301 if management.IsResourceNotFoundError(err) { 302 return nil 303 } 304 return fmt.Errorf("Error deleting Network Security Group rule %s: %s", name, err) 305 } 306 307 if err := mc.WaitForOperation(req, nil); err != nil { 308 return fmt.Errorf( 309 "Error waiting for Network Security Group rule %s to be deleted: %s", name, err) 310 } 311 312 return nil 313 } 314 315 func resourceAzureSecurityGroupRuleHash(v interface{}) int { 316 var buf bytes.Buffer 317 m := v.(map[string]interface{}) 318 buf.WriteString(fmt.Sprintf( 319 "%s-%d-%s-%s-%s-%s-%s-%s", 320 m["type"].(string), 321 m["priority"].(int), 322 m["action"].(string), 323 m["source_cidr"].(string), 324 m["source_port"].(string), 325 m["destination_cidr"].(string), 326 m["destination_port"].(string), 327 m["protocol"].(string))) 328 329 return hashcode.String(buf.String()) 330 } 331 332 func verifySecurityGroupRuleParams(rule map[string]interface{}) error { 333 typ := rule["type"].(string) 334 if typ != "Inbound" && typ != "Outbound" { 335 return fmt.Errorf("Parameter type only accepts 'Inbound' or 'Outbound' as values") 336 } 337 338 action := rule["action"].(string) 339 if action != "Allow" && action != "Deny" { 340 return fmt.Errorf("Parameter action only accepts 'Allow' or 'Deny' as values") 341 } 342 343 protocol := rule["protocol"].(string) 344 if protocol != "TCP" && protocol != "UDP" && protocol != "*" { 345 _, err := strconv.ParseInt(protocol, 0, 0) 346 if err != nil { 347 return fmt.Errorf( 348 "Parameter type only accepts 'TCP', 'UDP' or '*' as values") 349 } 350 } 351 352 return nil 353 }