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