github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/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  	_, error := secClient.CreateOrUpdate(resGroup, name, sg, make(chan struct{}))
   150  	err := <-error
   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 err != nil {
   192  		if resp.StatusCode == http.StatusNotFound {
   193  			d.SetId("")
   194  			return nil
   195  		}
   196  		return fmt.Errorf("Error making Read request on Azure Network Security Group %s: %s", name, err)
   197  	}
   198  
   199  	if resp.SecurityGroupPropertiesFormat.SecurityRules != nil {
   200  		d.Set("security_rule", flattenNetworkSecurityRules(resp.SecurityGroupPropertiesFormat.SecurityRules))
   201  	}
   202  
   203  	d.Set("resource_group_name", resGroup)
   204  	d.Set("name", resp.Name)
   205  	d.Set("location", resp.Location)
   206  	flattenAndSetTags(d, resp.Tags)
   207  
   208  	return nil
   209  }
   210  
   211  func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
   212  	secGroupClient := meta.(*ArmClient).secGroupClient
   213  
   214  	id, err := parseAzureResourceID(d.Id())
   215  	if err != nil {
   216  		return err
   217  	}
   218  	resGroup := id.ResourceGroup
   219  	name := id.Path["networkSecurityGroups"]
   220  
   221  	_, error := secGroupClient.Delete(resGroup, name, make(chan struct{}))
   222  	err = <-error
   223  
   224  	return err
   225  }
   226  
   227  func resourceArmNetworkSecurityGroupRuleHash(v interface{}) int {
   228  	var buf bytes.Buffer
   229  	m := v.(map[string]interface{})
   230  	buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string)))
   231  	buf.WriteString(fmt.Sprintf("%s-", m["source_port_range"].(string)))
   232  	buf.WriteString(fmt.Sprintf("%s-", m["destination_port_range"].(string)))
   233  	buf.WriteString(fmt.Sprintf("%s-", m["source_address_prefix"].(string)))
   234  	buf.WriteString(fmt.Sprintf("%s-", m["destination_address_prefix"].(string)))
   235  	buf.WriteString(fmt.Sprintf("%s-", m["access"].(string)))
   236  	buf.WriteString(fmt.Sprintf("%d-", m["priority"].(int)))
   237  	buf.WriteString(fmt.Sprintf("%s-", m["direction"].(string)))
   238  
   239  	return hashcode.String(buf.String())
   240  }
   241  
   242  func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]interface{} {
   243  	result := make([]map[string]interface{}, 0, len(*rules))
   244  	for _, rule := range *rules {
   245  		sgRule := make(map[string]interface{})
   246  		sgRule["name"] = *rule.Name
   247  		sgRule["destination_address_prefix"] = *rule.SecurityRulePropertiesFormat.DestinationAddressPrefix
   248  		sgRule["destination_port_range"] = *rule.SecurityRulePropertiesFormat.DestinationPortRange
   249  		sgRule["source_address_prefix"] = *rule.SecurityRulePropertiesFormat.SourceAddressPrefix
   250  		sgRule["source_port_range"] = *rule.SecurityRulePropertiesFormat.SourcePortRange
   251  		sgRule["priority"] = int(*rule.SecurityRulePropertiesFormat.Priority)
   252  		sgRule["access"] = rule.SecurityRulePropertiesFormat.Access
   253  		sgRule["direction"] = rule.SecurityRulePropertiesFormat.Direction
   254  		sgRule["protocol"] = rule.SecurityRulePropertiesFormat.Protocol
   255  
   256  		if rule.SecurityRulePropertiesFormat.Description != nil {
   257  			sgRule["description"] = *rule.SecurityRulePropertiesFormat.Description
   258  		}
   259  
   260  		result = append(result, sgRule)
   261  	}
   262  	return result
   263  }
   264  
   265  func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) {
   266  	sgRules := d.Get("security_rule").(*schema.Set).List()
   267  	rules := make([]network.SecurityRule, 0, len(sgRules))
   268  
   269  	for _, sgRaw := range sgRules {
   270  		data := sgRaw.(map[string]interface{})
   271  
   272  		source_port_range := data["source_port_range"].(string)
   273  		destination_port_range := data["destination_port_range"].(string)
   274  		source_address_prefix := data["source_address_prefix"].(string)
   275  		destination_address_prefix := data["destination_address_prefix"].(string)
   276  		priority := int32(data["priority"].(int))
   277  
   278  		properties := network.SecurityRulePropertiesFormat{
   279  			SourcePortRange:          &source_port_range,
   280  			DestinationPortRange:     &destination_port_range,
   281  			SourceAddressPrefix:      &source_address_prefix,
   282  			DestinationAddressPrefix: &destination_address_prefix,
   283  			Priority:                 &priority,
   284  			Access:                   network.SecurityRuleAccess(data["access"].(string)),
   285  			Direction:                network.SecurityRuleDirection(data["direction"].(string)),
   286  			Protocol:                 network.SecurityRuleProtocol(data["protocol"].(string)),
   287  		}
   288  
   289  		if v := data["description"].(string); v != "" {
   290  			properties.Description = &v
   291  		}
   292  
   293  		name := data["name"].(string)
   294  		rule := network.SecurityRule{
   295  			Name: &name,
   296  			SecurityRulePropertiesFormat: &properties,
   297  		}
   298  
   299  		rules = append(rules, rule)
   300  	}
   301  
   302  	return rules, nil
   303  }
   304  
   305  func networkSecurityGroupStateRefreshFunc(client *ArmClient, resourceGroupName string, sgName string) resource.StateRefreshFunc {
   306  	return func() (interface{}, string, error) {
   307  		res, err := client.secGroupClient.Get(resourceGroupName, sgName, "")
   308  		if err != nil {
   309  			return nil, "", fmt.Errorf("Error issuing read request in networkSecurityGroupStateRefreshFunc to Azure ARM for NSG '%s' (RG: '%s'): %s", sgName, resourceGroupName, err)
   310  		}
   311  
   312  		return res, *res.SecurityGroupPropertiesFormat.ProvisioningState, nil
   313  	}
   314  }