// resourceAzureSecurityGroupRuleCreate does all the necessary API calls to
// create a new network security group rule on Azure.
func resourceAzureSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
	azureClient := meta.(*Client)
	mgmtClient := azureClient.mgmtClient
	secGroupClient := azureClient.secGroupClient

	azureClient.secGroupMutex.Lock()
	defer azureClient.secGroupMutex.Unlock()

	// create and configure the RuleResponse:
	name := d.Get("name").(string)
	rule := netsecgroup.RuleRequest{
		Name:                     name,
		Type:                     netsecgroup.RuleType(d.Get("type").(string)),
		Priority:                 d.Get("priority").(int),
		Action:                   netsecgroup.RuleAction(d.Get("action").(string)),
		SourceAddressPrefix:      d.Get("source_address_prefix").(string),
		SourcePortRange:          d.Get("source_port_range").(string),
		DestinationAddressPrefix: d.Get("destination_address_prefix").(string),
		DestinationPortRange:     d.Get("destination_port_range").(string),
		Protocol:                 netsecgroup.RuleProtocol(d.Get("protocol").(string)),
	}

	// apply the rule to all the necessary network security groups:
	secGroups := d.Get("security_group_names").(*schema.Set).List()
	for _, sg := range secGroups {
		secGroup := sg.(string)

		// send the create request to Azure:
		log.Printf("[INFO] Sending Azure security group rule addition request for security group %q.", secGroup)
		reqID, err := secGroupClient.SetNetworkSecurityGroupRule(
			secGroup,
			rule,
		)
		if err != nil {
			return fmt.Errorf("Error sending Azure network security group rule creation request for security group %q: %s", secGroup, err)
		}
		err = mgmtClient.WaitForOperation(reqID, nil)
		if err != nil {
			return fmt.Errorf("Error creating Azure network security group rule for security group %q: %s", secGroup, err)
		}
	}

	d.SetId(name)
	return nil
}
// resourceAzureSecurityGroupRuleCreate does all the necessary API calls to
// create a new network security group rule on Azure.
func resourceAzureSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
	azureClient := meta.(*Client)
	mgmtClient := azureClient.mgmtClient
	secGroupClient := azureClient.secGroupClient

	// create and configure the RuleResponse:
	name := d.Get("name").(string)
	rule := netsecgroup.RuleRequest{
		Name:                     name,
		Type:                     netsecgroup.RuleType(d.Get("type").(string)),
		Priority:                 d.Get("priority").(int),
		Action:                   netsecgroup.RuleAction(d.Get("action").(string)),
		SourceAddressPrefix:      d.Get("source_address_prefix").(string),
		SourcePortRange:          d.Get("source_port_range").(string),
		DestinationAddressPrefix: d.Get("destination_address_prefix").(string),
		DestinationPortRange:     d.Get("destination_port_range").(string),
		Protocol:                 netsecgroup.RuleProtocol(d.Get("protocol").(string)),
	}

	// send the create request to Azure:
	log.Println("[INFO] Sending network security group rule creation request to Azure.")
	reqID, err := secGroupClient.SetNetworkSecurityGroupRule(
		d.Get("security_group_name").(string),
		rule,
	)
	if err != nil {
		return fmt.Errorf("Error sending network security group rule creation request to Azure: %s", err)
	}
	err = mgmtClient.WaitForOperation(reqID, nil)
	if err != nil {
		return fmt.Errorf("Error creating network security group rule on Azure: %s", err)
	}

	d.SetId(name)
	return nil
}
// resourceAzureSecurityGroupRuleUpdate does all the necessary API calls to
// update the state of a network security group ruke off Azure.
func resourceAzureSecurityGroupRuleUpdate(d *schema.ResourceData, meta interface{}) error {
	azureClient := meta.(*Client)
	mgmtClient := azureClient.mgmtClient
	secGroupClient := azureClient.secGroupClient

	secGroupName := d.Get("security_group_name").(string)

	// get info on the network security group and check its rules for this one:
	log.Println("[INFO] Sending network security group rule query for update to Azure.")
	secgroup, err := secGroupClient.GetNetworkSecurityGroup(secGroupName)
	if err != nil {
		if !management.IsResourceNotFoundError(err) {
			return fmt.Errorf("Error issuing network security group rules query: %s", err)
		} else {
			// it meants that the network security group this rule belonged to has
			// been deleted; so we must remove this resource from the schema:
			d.SetId("")
			return nil
		}
	}

	// try and find our security group rule:
	var found bool
	name := d.Get("name").(string)
	for _, rule := range secgroup.Rules {
		if rule.Name == name {
			found = true
		}
	}
	// check is the resource has not been deleted in the meantime:
	if !found {
		// if not; remove the resource:
		d.SetId("")
		return nil
	}

	// else, start building up the rule request struct:
	newRule := netsecgroup.RuleRequest{
		Name:                     d.Get("name").(string),
		Type:                     netsecgroup.RuleType(d.Get("type").(string)),
		Priority:                 d.Get("priority").(int),
		Action:                   netsecgroup.RuleAction(d.Get("action").(string)),
		SourceAddressPrefix:      d.Get("source_address_prefix").(string),
		SourcePortRange:          d.Get("source_port_range").(string),
		DestinationAddressPrefix: d.Get("destination_address_prefix").(string),
		DestinationPortRange:     d.Get("destination_port_range").(string),
		Protocol:                 netsecgroup.RuleProtocol(d.Get("protocol").(string)),
	}

	// send the create request to Azure:
	log.Println("[INFO] Sending network security group rule update request to Azure.")
	reqID, err := secGroupClient.SetNetworkSecurityGroupRule(
		secGroupName,
		newRule,
	)
	if err != nil {
		return fmt.Errorf("Error sending network security group rule update request to Azure: %s", err)
	}
	err = mgmtClient.WaitForOperation(reqID, nil)
	if err != nil {
		return fmt.Errorf("Error updating network security group rule on Azure: %s", err)
	}

	return nil
}
// resourceAzureSecurityGroupRuleUpdate does all the necessary API calls to
// update the state of a network security group rule off Azure.
func resourceAzureSecurityGroupRuleUpdate(d *schema.ResourceData, meta interface{}) error {
	azureClient := meta.(*Client)
	mgmtClient := azureClient.mgmtClient
	secGroupClient := azureClient.secGroupClient

	var found bool
	name := d.Get("name").(string)
	newRule := netsecgroup.RuleRequest{
		Name:                     d.Get("name").(string),
		Type:                     netsecgroup.RuleType(d.Get("type").(string)),
		Priority:                 d.Get("priority").(int),
		Action:                   netsecgroup.RuleAction(d.Get("action").(string)),
		SourceAddressPrefix:      d.Get("source_address_prefix").(string),
		SourcePortRange:          d.Get("source_port_range").(string),
		DestinationAddressPrefix: d.Get("destination_address_prefix").(string),
		DestinationPortRange:     d.Get("destination_port_range").(string),
		Protocol:                 netsecgroup.RuleProtocol(d.Get("protocol").(string)),
	}

	// iterate over all the security groups that should have this rule and
	// update it per security group:
	remaining := schema.NewSet(schema.HashString, nil)
	secGroupNames := d.Get("security_group_names").(*schema.Set).List()
	for _, sg := range secGroupNames {
		secGroupName := sg.(string)

		// get info on the network security group and check its rules for this one:
		log.Printf("[INFO] Sending Azure network security group rule query for security group %q.", secGroupName)
		secgroup, err := secGroupClient.GetNetworkSecurityGroup(secGroupName)
		if err != nil {
			if !management.IsResourceNotFoundError(err) {
				return fmt.Errorf("Error issuing network security group rules query: %s", err)
			} else {
				// it meants that the network security group this rule belonged to has
				// been deleted; so we skip this iteration:
				continue
			}
		}

		// try and find our security group rule:
		for _, rule := range secgroup.Rules {
			if rule.Name == name {
				// note the fact that this rule still applies to this security group:
				found = true
				remaining.Add(secGroupName)

				// and go ahead and update it:
				log.Printf("[INFO] Sending Azure network security group rule update request for security group %q.", secGroupName)
				reqID, err := secGroupClient.SetNetworkSecurityGroupRule(
					secGroupName,
					newRule,
				)
				if err != nil {
					return fmt.Errorf("Error sending Azure network security group rule update request for security group %q: %s", secGroupName, err)
				}
				err = mgmtClient.WaitForOperation(reqID, nil)
				if err != nil {
					return fmt.Errorf("Error updating Azure network security group rule for security group %q: %s", secGroupName, err)
				}

				break
			}
		}
	}

	// check to see if there is any security group still having this rule:
	if !found {
		d.SetId("")
		return nil
	}

	// here; we must update the set of security groups still having this rule:
	d.Set("security_group_names", remaining)

	return nil
}