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  }