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