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  }