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