github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/builtin/providers/aws/resource_aws_security_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"sort"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/aws/aws-sdk-go/aws"
    13  	"github.com/aws/aws-sdk-go/aws/awserr"
    14  	"github.com/aws/aws-sdk-go/service/ec2"
    15  	"github.com/hashicorp/terraform/helper/hashcode"
    16  	"github.com/hashicorp/terraform/helper/resource"
    17  	"github.com/hashicorp/terraform/helper/schema"
    18  )
    19  
    20  func resourceAwsSecurityGroup() *schema.Resource {
    21  	return &schema.Resource{
    22  		Create: resourceAwsSecurityGroupCreate,
    23  		Read:   resourceAwsSecurityGroupRead,
    24  		Update: resourceAwsSecurityGroupUpdate,
    25  		Delete: resourceAwsSecurityGroupDelete,
    26  		Importer: &schema.ResourceImporter{
    27  			State: resourceAwsSecurityGroupImportState,
    28  		},
    29  
    30  		Schema: map[string]*schema.Schema{
    31  			"name": {
    32  				Type:          schema.TypeString,
    33  				Optional:      true,
    34  				Computed:      true,
    35  				ForceNew:      true,
    36  				ConflictsWith: []string{"name_prefix"},
    37  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    38  					value := v.(string)
    39  					if len(value) > 255 {
    40  						errors = append(errors, fmt.Errorf(
    41  							"%q cannot be longer than 255 characters", k))
    42  					}
    43  					return
    44  				},
    45  			},
    46  
    47  			"name_prefix": {
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				ForceNew: true,
    51  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    52  					value := v.(string)
    53  					if len(value) > 100 {
    54  						errors = append(errors, fmt.Errorf(
    55  							"%q cannot be longer than 100 characters, name is limited to 255", k))
    56  					}
    57  					return
    58  				},
    59  			},
    60  
    61  			"description": {
    62  				Type:     schema.TypeString,
    63  				Optional: true,
    64  				ForceNew: true,
    65  				Default:  "Managed by Terraform",
    66  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    67  					value := v.(string)
    68  					if len(value) > 255 {
    69  						errors = append(errors, fmt.Errorf(
    70  							"%q cannot be longer than 255 characters", k))
    71  					}
    72  					return
    73  				},
    74  			},
    75  
    76  			"vpc_id": {
    77  				Type:     schema.TypeString,
    78  				Optional: true,
    79  				ForceNew: true,
    80  				Computed: true,
    81  			},
    82  
    83  			"ingress": {
    84  				Type:     schema.TypeSet,
    85  				Optional: true,
    86  				Computed: true,
    87  				Elem: &schema.Resource{
    88  					Schema: map[string]*schema.Schema{
    89  						"from_port": {
    90  							Type:     schema.TypeInt,
    91  							Required: true,
    92  						},
    93  
    94  						"to_port": {
    95  							Type:     schema.TypeInt,
    96  							Required: true,
    97  						},
    98  
    99  						"protocol": {
   100  							Type:      schema.TypeString,
   101  							Required:  true,
   102  							StateFunc: protocolStateFunc,
   103  						},
   104  
   105  						"cidr_blocks": {
   106  							Type:     schema.TypeList,
   107  							Optional: true,
   108  							Elem: &schema.Schema{
   109  								Type:         schema.TypeString,
   110  								ValidateFunc: validateCIDRNetworkAddress,
   111  							},
   112  						},
   113  
   114  						"ipv6_cidr_blocks": {
   115  							Type:     schema.TypeList,
   116  							Optional: true,
   117  							Elem: &schema.Schema{
   118  								Type:         schema.TypeString,
   119  								ValidateFunc: validateCIDRNetworkAddress,
   120  							},
   121  						},
   122  
   123  						"security_groups": {
   124  							Type:     schema.TypeSet,
   125  							Optional: true,
   126  							Elem:     &schema.Schema{Type: schema.TypeString},
   127  							Set:      schema.HashString,
   128  						},
   129  
   130  						"self": {
   131  							Type:     schema.TypeBool,
   132  							Optional: true,
   133  							Default:  false,
   134  						},
   135  					},
   136  				},
   137  				Set: resourceAwsSecurityGroupRuleHash,
   138  			},
   139  
   140  			"egress": {
   141  				Type:     schema.TypeSet,
   142  				Optional: true,
   143  				Computed: true,
   144  				Elem: &schema.Resource{
   145  					Schema: map[string]*schema.Schema{
   146  						"from_port": {
   147  							Type:     schema.TypeInt,
   148  							Required: true,
   149  						},
   150  
   151  						"to_port": {
   152  							Type:     schema.TypeInt,
   153  							Required: true,
   154  						},
   155  
   156  						"protocol": {
   157  							Type:      schema.TypeString,
   158  							Required:  true,
   159  							StateFunc: protocolStateFunc,
   160  						},
   161  
   162  						"cidr_blocks": {
   163  							Type:     schema.TypeList,
   164  							Optional: true,
   165  							Elem: &schema.Schema{
   166  								Type:         schema.TypeString,
   167  								ValidateFunc: validateCIDRNetworkAddress,
   168  							},
   169  						},
   170  
   171  						"ipv6_cidr_blocks": {
   172  							Type:     schema.TypeList,
   173  							Optional: true,
   174  							Elem: &schema.Schema{
   175  								Type:         schema.TypeString,
   176  								ValidateFunc: validateCIDRNetworkAddress,
   177  							},
   178  						},
   179  
   180  						"prefix_list_ids": {
   181  							Type:     schema.TypeList,
   182  							Optional: true,
   183  							Elem:     &schema.Schema{Type: schema.TypeString},
   184  						},
   185  
   186  						"security_groups": {
   187  							Type:     schema.TypeSet,
   188  							Optional: true,
   189  							Elem:     &schema.Schema{Type: schema.TypeString},
   190  							Set:      schema.HashString,
   191  						},
   192  
   193  						"self": {
   194  							Type:     schema.TypeBool,
   195  							Optional: true,
   196  							Default:  false,
   197  						},
   198  					},
   199  				},
   200  				Set: resourceAwsSecurityGroupRuleHash,
   201  			},
   202  
   203  			"owner_id": {
   204  				Type:     schema.TypeString,
   205  				Computed: true,
   206  			},
   207  
   208  			"tags": tagsSchema(),
   209  		},
   210  	}
   211  }
   212  
   213  func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error {
   214  	conn := meta.(*AWSClient).ec2conn
   215  
   216  	securityGroupOpts := &ec2.CreateSecurityGroupInput{}
   217  
   218  	if v, ok := d.GetOk("vpc_id"); ok {
   219  		securityGroupOpts.VpcId = aws.String(v.(string))
   220  	}
   221  
   222  	if v := d.Get("description"); v != nil {
   223  		securityGroupOpts.Description = aws.String(v.(string))
   224  	}
   225  
   226  	var groupName string
   227  	if v, ok := d.GetOk("name"); ok {
   228  		groupName = v.(string)
   229  	} else if v, ok := d.GetOk("name_prefix"); ok {
   230  		groupName = resource.PrefixedUniqueId(v.(string))
   231  	} else {
   232  		groupName = resource.UniqueId()
   233  	}
   234  	securityGroupOpts.GroupName = aws.String(groupName)
   235  
   236  	var err error
   237  	log.Printf(
   238  		"[DEBUG] Security Group create configuration: %#v", securityGroupOpts)
   239  	createResp, err := conn.CreateSecurityGroup(securityGroupOpts)
   240  	if err != nil {
   241  		return fmt.Errorf("Error creating Security Group: %s", err)
   242  	}
   243  
   244  	d.SetId(*createResp.GroupId)
   245  
   246  	log.Printf("[INFO] Security Group ID: %s", d.Id())
   247  
   248  	// Wait for the security group to truly exist
   249  	log.Printf(
   250  		"[DEBUG] Waiting for Security Group (%s) to exist",
   251  		d.Id())
   252  	stateConf := &resource.StateChangeConf{
   253  		Pending: []string{""},
   254  		Target:  []string{"exists"},
   255  		Refresh: SGStateRefreshFunc(conn, d.Id()),
   256  		Timeout: 5 * time.Minute,
   257  	}
   258  
   259  	resp, err := stateConf.WaitForState()
   260  	if err != nil {
   261  		return fmt.Errorf(
   262  			"Error waiting for Security Group (%s) to become available: %s",
   263  			d.Id(), err)
   264  	}
   265  
   266  	if err := setTags(conn, d); err != nil {
   267  		return err
   268  	}
   269  
   270  	// AWS defaults all Security Groups to have an ALLOW ALL egress rule. Here we
   271  	// revoke that rule, so users don't unknowingly have/use it.
   272  	group := resp.(*ec2.SecurityGroup)
   273  	if group.VpcId != nil && *group.VpcId != "" {
   274  		log.Printf("[DEBUG] Revoking default egress rule for Security Group for %s", d.Id())
   275  
   276  		req := &ec2.RevokeSecurityGroupEgressInput{
   277  			GroupId: createResp.GroupId,
   278  			IpPermissions: []*ec2.IpPermission{
   279  				{
   280  					FromPort: aws.Int64(int64(0)),
   281  					ToPort:   aws.Int64(int64(0)),
   282  					IpRanges: []*ec2.IpRange{
   283  						{
   284  							CidrIp: aws.String("0.0.0.0/0"),
   285  						},
   286  					},
   287  					IpProtocol: aws.String("-1"),
   288  				},
   289  			},
   290  		}
   291  
   292  		if _, err = conn.RevokeSecurityGroupEgress(req); err != nil {
   293  			return fmt.Errorf(
   294  				"Error revoking default egress rule for Security Group (%s): %s",
   295  				d.Id(), err)
   296  		}
   297  
   298  		log.Printf("[DEBUG] Revoking default IPv6 egress rule for Security Group for %s", d.Id())
   299  		req = &ec2.RevokeSecurityGroupEgressInput{
   300  			GroupId: createResp.GroupId,
   301  			IpPermissions: []*ec2.IpPermission{
   302  				{
   303  					FromPort: aws.Int64(int64(0)),
   304  					ToPort:   aws.Int64(int64(0)),
   305  					Ipv6Ranges: []*ec2.Ipv6Range{
   306  						{
   307  							CidrIpv6: aws.String("::/0"),
   308  						},
   309  					},
   310  					IpProtocol: aws.String("-1"),
   311  				},
   312  			},
   313  		}
   314  
   315  		_, err = conn.RevokeSecurityGroupEgress(req)
   316  		if err != nil {
   317  			//If we have a NotFound, then we are trying to remove the default IPv6 egress of a non-IPv6
   318  			//enabled SG
   319  			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() != "InvalidPermission.NotFound" {
   320  				return fmt.Errorf(
   321  					"Error revoking default IPv6 egress rule for Security Group (%s): %s",
   322  					d.Id(), err)
   323  			}
   324  		}
   325  
   326  	}
   327  
   328  	return resourceAwsSecurityGroupUpdate(d, meta)
   329  }
   330  
   331  func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
   332  	conn := meta.(*AWSClient).ec2conn
   333  
   334  	sgRaw, _, err := SGStateRefreshFunc(conn, d.Id())()
   335  	if err != nil {
   336  		return err
   337  	}
   338  	if sgRaw == nil {
   339  		d.SetId("")
   340  		return nil
   341  	}
   342  
   343  	sg := sgRaw.(*ec2.SecurityGroup)
   344  
   345  	remoteIngressRules := resourceAwsSecurityGroupIPPermGather(d.Id(), sg.IpPermissions, sg.OwnerId)
   346  	remoteEgressRules := resourceAwsSecurityGroupIPPermGather(d.Id(), sg.IpPermissionsEgress, sg.OwnerId)
   347  
   348  	localIngressRules := d.Get("ingress").(*schema.Set).List()
   349  	localEgressRules := d.Get("egress").(*schema.Set).List()
   350  
   351  	// Loop through the local state of rules, doing a match against the remote
   352  	// ruleSet we built above.
   353  	ingressRules := matchRules("ingress", localIngressRules, remoteIngressRules)
   354  	egressRules := matchRules("egress", localEgressRules, remoteEgressRules)
   355  
   356  	d.Set("description", sg.Description)
   357  	d.Set("name", sg.GroupName)
   358  	d.Set("vpc_id", sg.VpcId)
   359  	d.Set("owner_id", sg.OwnerId)
   360  
   361  	if err := d.Set("ingress", ingressRules); err != nil {
   362  		log.Printf("[WARN] Error setting Ingress rule set for (%s): %s", d.Id(), err)
   363  	}
   364  
   365  	if err := d.Set("egress", egressRules); err != nil {
   366  		log.Printf("[WARN] Error setting Egress rule set for (%s): %s", d.Id(), err)
   367  	}
   368  
   369  	d.Set("tags", tagsToMap(sg.Tags))
   370  	return nil
   371  }
   372  
   373  func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   374  	conn := meta.(*AWSClient).ec2conn
   375  
   376  	sgRaw, _, err := SGStateRefreshFunc(conn, d.Id())()
   377  	if err != nil {
   378  		return err
   379  	}
   380  	if sgRaw == nil {
   381  		d.SetId("")
   382  		return nil
   383  	}
   384  
   385  	group := sgRaw.(*ec2.SecurityGroup)
   386  
   387  	err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group)
   388  	if err != nil {
   389  		return err
   390  	}
   391  
   392  	if d.Get("vpc_id") != nil {
   393  		err = resourceAwsSecurityGroupUpdateRules(d, "egress", meta, group)
   394  		if err != nil {
   395  			return err
   396  		}
   397  	}
   398  
   399  	if !d.IsNewResource() {
   400  		if err := setTags(conn, d); err != nil {
   401  			return err
   402  		}
   403  		d.SetPartial("tags")
   404  	}
   405  
   406  	return resourceAwsSecurityGroupRead(d, meta)
   407  }
   408  
   409  func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
   410  	conn := meta.(*AWSClient).ec2conn
   411  
   412  	log.Printf("[DEBUG] Security Group destroy: %v", d.Id())
   413  
   414  	if err := deleteLingeringLambdaENIs(conn, d); err != nil {
   415  		return fmt.Errorf("Failed to delete Lambda ENIs: %s", err)
   416  	}
   417  
   418  	return resource.Retry(5*time.Minute, func() *resource.RetryError {
   419  		_, err := conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{
   420  			GroupId: aws.String(d.Id()),
   421  		})
   422  		if err != nil {
   423  			ec2err, ok := err.(awserr.Error)
   424  			if !ok {
   425  				return resource.RetryableError(err)
   426  			}
   427  
   428  			switch ec2err.Code() {
   429  			case "InvalidGroup.NotFound":
   430  				return nil
   431  			case "DependencyViolation":
   432  				// If it is a dependency violation, we want to retry
   433  				return resource.RetryableError(err)
   434  			default:
   435  				// Any other error, we want to quit the retry loop immediately
   436  				return resource.NonRetryableError(err)
   437  			}
   438  		}
   439  
   440  		return nil
   441  	})
   442  }
   443  
   444  func resourceAwsSecurityGroupRuleHash(v interface{}) int {
   445  	var buf bytes.Buffer
   446  	m := v.(map[string]interface{})
   447  	buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int)))
   448  	buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int)))
   449  	p := protocolForValue(m["protocol"].(string))
   450  	buf.WriteString(fmt.Sprintf("%s-", p))
   451  	buf.WriteString(fmt.Sprintf("%t-", m["self"].(bool)))
   452  
   453  	// We need to make sure to sort the strings below so that we always
   454  	// generate the same hash code no matter what is in the set.
   455  	if v, ok := m["cidr_blocks"]; ok {
   456  		vs := v.([]interface{})
   457  		s := make([]string, len(vs))
   458  		for i, raw := range vs {
   459  			s[i] = raw.(string)
   460  		}
   461  		sort.Strings(s)
   462  
   463  		for _, v := range s {
   464  			buf.WriteString(fmt.Sprintf("%s-", v))
   465  		}
   466  	}
   467  	if v, ok := m["ipv6_cidr_blocks"]; ok {
   468  		vs := v.([]interface{})
   469  		s := make([]string, len(vs))
   470  		for i, raw := range vs {
   471  			s[i] = raw.(string)
   472  		}
   473  		sort.Strings(s)
   474  
   475  		for _, v := range s {
   476  			buf.WriteString(fmt.Sprintf("%s-", v))
   477  		}
   478  	}
   479  	if v, ok := m["prefix_list_ids"]; ok {
   480  		vs := v.([]interface{})
   481  		s := make([]string, len(vs))
   482  		for i, raw := range vs {
   483  			s[i] = raw.(string)
   484  		}
   485  		sort.Strings(s)
   486  
   487  		for _, v := range s {
   488  			buf.WriteString(fmt.Sprintf("%s-", v))
   489  		}
   490  	}
   491  	if v, ok := m["security_groups"]; ok {
   492  		vs := v.(*schema.Set).List()
   493  		s := make([]string, len(vs))
   494  		for i, raw := range vs {
   495  			s[i] = raw.(string)
   496  		}
   497  		sort.Strings(s)
   498  
   499  		for _, v := range s {
   500  			buf.WriteString(fmt.Sprintf("%s-", v))
   501  		}
   502  	}
   503  
   504  	return hashcode.String(buf.String())
   505  }
   506  
   507  func resourceAwsSecurityGroupIPPermGather(groupId string, permissions []*ec2.IpPermission, ownerId *string) []map[string]interface{} {
   508  	ruleMap := make(map[string]map[string]interface{})
   509  	for _, perm := range permissions {
   510  		var fromPort, toPort int64
   511  		if v := perm.FromPort; v != nil {
   512  			fromPort = *v
   513  		}
   514  		if v := perm.ToPort; v != nil {
   515  			toPort = *v
   516  		}
   517  
   518  		k := fmt.Sprintf("%s-%d-%d", *perm.IpProtocol, fromPort, toPort)
   519  		m, ok := ruleMap[k]
   520  		if !ok {
   521  			m = make(map[string]interface{})
   522  			ruleMap[k] = m
   523  		}
   524  
   525  		m["from_port"] = fromPort
   526  		m["to_port"] = toPort
   527  		m["protocol"] = *perm.IpProtocol
   528  
   529  		if len(perm.IpRanges) > 0 {
   530  			raw, ok := m["cidr_blocks"]
   531  			if !ok {
   532  				raw = make([]string, 0, len(perm.IpRanges))
   533  			}
   534  			list := raw.([]string)
   535  
   536  			for _, ip := range perm.IpRanges {
   537  				list = append(list, *ip.CidrIp)
   538  			}
   539  
   540  			m["cidr_blocks"] = list
   541  		}
   542  
   543  		if len(perm.Ipv6Ranges) > 0 {
   544  			raw, ok := m["ipv6_cidr_blocks"]
   545  			if !ok {
   546  				raw = make([]string, 0, len(perm.Ipv6Ranges))
   547  			}
   548  			list := raw.([]string)
   549  
   550  			for _, ip := range perm.Ipv6Ranges {
   551  				list = append(list, *ip.CidrIpv6)
   552  			}
   553  
   554  			m["ipv6_cidr_blocks"] = list
   555  		}
   556  
   557  		if len(perm.PrefixListIds) > 0 {
   558  			raw, ok := m["prefix_list_ids"]
   559  			if !ok {
   560  				raw = make([]string, 0, len(perm.PrefixListIds))
   561  			}
   562  			list := raw.([]string)
   563  
   564  			for _, pl := range perm.PrefixListIds {
   565  				list = append(list, *pl.PrefixListId)
   566  			}
   567  
   568  			m["prefix_list_ids"] = list
   569  		}
   570  
   571  		groups := flattenSecurityGroups(perm.UserIdGroupPairs, ownerId)
   572  		for i, g := range groups {
   573  			if *g.GroupId == groupId {
   574  				groups[i], groups = groups[len(groups)-1], groups[:len(groups)-1]
   575  				m["self"] = true
   576  			}
   577  		}
   578  
   579  		if len(groups) > 0 {
   580  			raw, ok := m["security_groups"]
   581  			if !ok {
   582  				raw = schema.NewSet(schema.HashString, nil)
   583  			}
   584  			list := raw.(*schema.Set)
   585  
   586  			for _, g := range groups {
   587  				if g.GroupName != nil {
   588  					list.Add(*g.GroupName)
   589  				} else {
   590  					list.Add(*g.GroupId)
   591  				}
   592  			}
   593  
   594  			m["security_groups"] = list
   595  		}
   596  	}
   597  	rules := make([]map[string]interface{}, 0, len(ruleMap))
   598  	for _, m := range ruleMap {
   599  		rules = append(rules, m)
   600  	}
   601  
   602  	return rules
   603  }
   604  
   605  func resourceAwsSecurityGroupUpdateRules(
   606  	d *schema.ResourceData, ruleset string,
   607  	meta interface{}, group *ec2.SecurityGroup) error {
   608  
   609  	if d.HasChange(ruleset) {
   610  		o, n := d.GetChange(ruleset)
   611  		if o == nil {
   612  			o = new(schema.Set)
   613  		}
   614  		if n == nil {
   615  			n = new(schema.Set)
   616  		}
   617  
   618  		os := o.(*schema.Set)
   619  		ns := n.(*schema.Set)
   620  
   621  		remove, err := expandIPPerms(group, os.Difference(ns).List())
   622  		if err != nil {
   623  			return err
   624  		}
   625  		add, err := expandIPPerms(group, ns.Difference(os).List())
   626  		if err != nil {
   627  			return err
   628  		}
   629  
   630  		// TODO: We need to handle partial state better in the in-between
   631  		// in this update.
   632  
   633  		// TODO: It'd be nicer to authorize before removing, but then we have
   634  		// to deal with complicated unrolling to get individual CIDR blocks
   635  		// to avoid authorizing already authorized sources. Removing before
   636  		// adding is easier here, and Terraform should be fast enough to
   637  		// not have service issues.
   638  
   639  		if len(remove) > 0 || len(add) > 0 {
   640  			conn := meta.(*AWSClient).ec2conn
   641  
   642  			var err error
   643  			if len(remove) > 0 {
   644  				log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v",
   645  					group, ruleset, remove)
   646  
   647  				if ruleset == "egress" {
   648  					req := &ec2.RevokeSecurityGroupEgressInput{
   649  						GroupId:       group.GroupId,
   650  						IpPermissions: remove,
   651  					}
   652  					_, err = conn.RevokeSecurityGroupEgress(req)
   653  				} else {
   654  					req := &ec2.RevokeSecurityGroupIngressInput{
   655  						GroupId:       group.GroupId,
   656  						IpPermissions: remove,
   657  					}
   658  					if group.VpcId == nil || *group.VpcId == "" {
   659  						req.GroupId = nil
   660  						req.GroupName = group.GroupName
   661  					}
   662  					_, err = conn.RevokeSecurityGroupIngress(req)
   663  				}
   664  
   665  				if err != nil {
   666  					return fmt.Errorf(
   667  						"Error revoking security group %s rules: %s",
   668  						ruleset, err)
   669  				}
   670  			}
   671  
   672  			if len(add) > 0 {
   673  				log.Printf("[DEBUG] Authorizing security group %#v %s rule: %#v",
   674  					group, ruleset, add)
   675  				// Authorize the new rules
   676  				if ruleset == "egress" {
   677  					req := &ec2.AuthorizeSecurityGroupEgressInput{
   678  						GroupId:       group.GroupId,
   679  						IpPermissions: add,
   680  					}
   681  					_, err = conn.AuthorizeSecurityGroupEgress(req)
   682  				} else {
   683  					req := &ec2.AuthorizeSecurityGroupIngressInput{
   684  						GroupId:       group.GroupId,
   685  						IpPermissions: add,
   686  					}
   687  					if group.VpcId == nil || *group.VpcId == "" {
   688  						req.GroupId = nil
   689  						req.GroupName = group.GroupName
   690  					}
   691  
   692  					_, err = conn.AuthorizeSecurityGroupIngress(req)
   693  				}
   694  
   695  				if err != nil {
   696  					return fmt.Errorf(
   697  						"Error authorizing security group %s rules: %s",
   698  						ruleset, err)
   699  				}
   700  			}
   701  		}
   702  	}
   703  	return nil
   704  }
   705  
   706  // SGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   707  // a security group.
   708  func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   709  	return func() (interface{}, string, error) {
   710  		req := &ec2.DescribeSecurityGroupsInput{
   711  			GroupIds: []*string{aws.String(id)},
   712  		}
   713  		resp, err := conn.DescribeSecurityGroups(req)
   714  		if err != nil {
   715  			if ec2err, ok := err.(awserr.Error); ok {
   716  				if ec2err.Code() == "InvalidSecurityGroupID.NotFound" ||
   717  					ec2err.Code() == "InvalidGroup.NotFound" {
   718  					resp = nil
   719  					err = nil
   720  				}
   721  			}
   722  
   723  			if err != nil {
   724  				log.Printf("Error on SGStateRefresh: %s", err)
   725  				return nil, "", err
   726  			}
   727  		}
   728  
   729  		if resp == nil {
   730  			return nil, "", nil
   731  		}
   732  
   733  		group := resp.SecurityGroups[0]
   734  		return group, "exists", nil
   735  	}
   736  }
   737  
   738  // matchRules receives the group id, type of rules, and the local / remote maps
   739  // of rules. We iterate through the local set of rules trying to find a matching
   740  // remote rule, which may be structured differently because of how AWS
   741  // aggregates the rules under the to, from, and type.
   742  //
   743  //
   744  // Matching rules are written to state, with their elements removed from the
   745  // remote set
   746  //
   747  // If no match is found, we'll write the remote rule to state and let the graph
   748  // sort things out
   749  func matchRules(rType string, local []interface{}, remote []map[string]interface{}) []map[string]interface{} {
   750  	// For each local ip or security_group, we need to match against the remote
   751  	// ruleSet until all ips or security_groups are found
   752  
   753  	// saves represents the rules that have been identified to be saved to state,
   754  	// in the appropriate d.Set("{ingress,egress}") call.
   755  	var saves []map[string]interface{}
   756  	for _, raw := range local {
   757  		l := raw.(map[string]interface{})
   758  
   759  		var selfVal bool
   760  		if v, ok := l["self"]; ok {
   761  			selfVal = v.(bool)
   762  		}
   763  
   764  		// matching against self is required to detect rules that only include self
   765  		// as the rule. resourceAwsSecurityGroupIPPermGather parses the group out
   766  		// and replaces it with self if it's ID is found
   767  		localHash := idHash(rType, l["protocol"].(string), int64(l["to_port"].(int)), int64(l["from_port"].(int)), selfVal)
   768  
   769  		// loop remote rules, looking for a matching hash
   770  		for _, r := range remote {
   771  			var remoteSelfVal bool
   772  			if v, ok := r["self"]; ok {
   773  				remoteSelfVal = v.(bool)
   774  			}
   775  
   776  			// hash this remote rule and compare it for a match consideration with the
   777  			// local rule we're examining
   778  			rHash := idHash(rType, r["protocol"].(string), r["to_port"].(int64), r["from_port"].(int64), remoteSelfVal)
   779  			if rHash == localHash {
   780  				var numExpectedCidrs, numExpectedIpv6Cidrs, numExpectedPrefixLists, numExpectedSGs, numRemoteCidrs, numRemoteIpv6Cidrs, numRemotePrefixLists, numRemoteSGs int
   781  				var matchingCidrs []string
   782  				var matchingIpv6Cidrs []string
   783  				var matchingSGs []string
   784  				var matchingPrefixLists []string
   785  
   786  				// grab the local/remote cidr and sg groups, capturing the expected and
   787  				// actual counts
   788  				lcRaw, ok := l["cidr_blocks"]
   789  				if ok {
   790  					numExpectedCidrs = len(l["cidr_blocks"].([]interface{}))
   791  				}
   792  				liRaw, ok := l["ipv6_cidr_blocks"]
   793  				if ok {
   794  					numExpectedIpv6Cidrs = len(l["ipv6_cidr_blocks"].([]interface{}))
   795  				}
   796  				lpRaw, ok := l["prefix_list_ids"]
   797  				if ok {
   798  					numExpectedPrefixLists = len(l["prefix_list_ids"].([]interface{}))
   799  				}
   800  				lsRaw, ok := l["security_groups"]
   801  				if ok {
   802  					numExpectedSGs = len(l["security_groups"].(*schema.Set).List())
   803  				}
   804  
   805  				rcRaw, ok := r["cidr_blocks"]
   806  				if ok {
   807  					numRemoteCidrs = len(r["cidr_blocks"].([]string))
   808  				}
   809  				riRaw, ok := r["ipv6_cidr_blocks"]
   810  				if ok {
   811  					numRemoteIpv6Cidrs = len(r["ipv6_cidr_blocks"].([]string))
   812  				}
   813  				rpRaw, ok := r["prefix_list_ids"]
   814  				if ok {
   815  					numRemotePrefixLists = len(r["prefix_list_ids"].([]string))
   816  				}
   817  
   818  				rsRaw, ok := r["security_groups"]
   819  				if ok {
   820  					numRemoteSGs = len(r["security_groups"].(*schema.Set).List())
   821  				}
   822  
   823  				// check some early failures
   824  				if numExpectedCidrs > numRemoteCidrs {
   825  					log.Printf("[DEBUG] Local rule has more CIDR blocks, continuing (%d/%d)", numExpectedCidrs, numRemoteCidrs)
   826  					continue
   827  				}
   828  				if numExpectedIpv6Cidrs > numRemoteIpv6Cidrs {
   829  					log.Printf("[DEBUG] Local rule has more IPV6 CIDR blocks, continuing (%d/%d)", numExpectedIpv6Cidrs, numRemoteIpv6Cidrs)
   830  					continue
   831  				}
   832  				if numExpectedPrefixLists > numRemotePrefixLists {
   833  					log.Printf("[DEBUG] Local rule has more prefix lists, continuing (%d/%d)", numExpectedPrefixLists, numRemotePrefixLists)
   834  					continue
   835  				}
   836  				if numExpectedSGs > numRemoteSGs {
   837  					log.Printf("[DEBUG] Local rule has more Security Groups, continuing (%d/%d)", numExpectedSGs, numRemoteSGs)
   838  					continue
   839  				}
   840  
   841  				// match CIDRs by converting both to sets, and using Set methods
   842  				var localCidrs []interface{}
   843  				if lcRaw != nil {
   844  					localCidrs = lcRaw.([]interface{})
   845  				}
   846  				localCidrSet := schema.NewSet(schema.HashString, localCidrs)
   847  
   848  				// remote cidrs are presented as a slice of strings, so we need to
   849  				// reformat them into a slice of interfaces to be used in creating the
   850  				// remote cidr set
   851  				var remoteCidrs []string
   852  				if rcRaw != nil {
   853  					remoteCidrs = rcRaw.([]string)
   854  				}
   855  				// convert remote cidrs to a set, for easy comparisons
   856  				var list []interface{}
   857  				for _, s := range remoteCidrs {
   858  					list = append(list, s)
   859  				}
   860  				remoteCidrSet := schema.NewSet(schema.HashString, list)
   861  
   862  				// Build up a list of local cidrs that are found in the remote set
   863  				for _, s := range localCidrSet.List() {
   864  					if remoteCidrSet.Contains(s) {
   865  						matchingCidrs = append(matchingCidrs, s.(string))
   866  					}
   867  				}
   868  
   869  				//IPV6 CIDRs
   870  				var localIpv6Cidrs []interface{}
   871  				if liRaw != nil {
   872  					localIpv6Cidrs = liRaw.([]interface{})
   873  				}
   874  				localIpv6CidrSet := schema.NewSet(schema.HashString, localIpv6Cidrs)
   875  
   876  				var remoteIpv6Cidrs []string
   877  				if riRaw != nil {
   878  					remoteIpv6Cidrs = riRaw.([]string)
   879  				}
   880  				var listIpv6 []interface{}
   881  				for _, s := range remoteIpv6Cidrs {
   882  					listIpv6 = append(listIpv6, s)
   883  				}
   884  				remoteIpv6CidrSet := schema.NewSet(schema.HashString, listIpv6)
   885  
   886  				for _, s := range localIpv6CidrSet.List() {
   887  					if remoteIpv6CidrSet.Contains(s) {
   888  						matchingIpv6Cidrs = append(matchingIpv6Cidrs, s.(string))
   889  					}
   890  				}
   891  
   892  				// match prefix lists by converting both to sets, and using Set methods
   893  				var localPrefixLists []interface{}
   894  				if lpRaw != nil {
   895  					localPrefixLists = lpRaw.([]interface{})
   896  				}
   897  				localPrefixListsSet := schema.NewSet(schema.HashString, localPrefixLists)
   898  
   899  				// remote prefix lists are presented as a slice of strings, so we need to
   900  				// reformat them into a slice of interfaces to be used in creating the
   901  				// remote prefix list set
   902  				var remotePrefixLists []string
   903  				if rpRaw != nil {
   904  					remotePrefixLists = rpRaw.([]string)
   905  				}
   906  				// convert remote prefix lists to a set, for easy comparison
   907  				list = nil
   908  				for _, s := range remotePrefixLists {
   909  					list = append(list, s)
   910  				}
   911  				remotePrefixListsSet := schema.NewSet(schema.HashString, list)
   912  
   913  				// Build up a list of local prefix lists that are found in the remote set
   914  				for _, s := range localPrefixListsSet.List() {
   915  					if remotePrefixListsSet.Contains(s) {
   916  						matchingPrefixLists = append(matchingPrefixLists, s.(string))
   917  					}
   918  				}
   919  
   920  				// match SGs. Both local and remote are already sets
   921  				var localSGSet *schema.Set
   922  				if lsRaw == nil {
   923  					localSGSet = schema.NewSet(schema.HashString, nil)
   924  				} else {
   925  					localSGSet = lsRaw.(*schema.Set)
   926  				}
   927  
   928  				var remoteSGSet *schema.Set
   929  				if rsRaw == nil {
   930  					remoteSGSet = schema.NewSet(schema.HashString, nil)
   931  				} else {
   932  					remoteSGSet = rsRaw.(*schema.Set)
   933  				}
   934  
   935  				// Build up a list of local security groups that are found in the remote set
   936  				for _, s := range localSGSet.List() {
   937  					if remoteSGSet.Contains(s) {
   938  						matchingSGs = append(matchingSGs, s.(string))
   939  					}
   940  				}
   941  
   942  				// compare equalities for matches.
   943  				// If we found the number of cidrs and number of sgs, we declare a
   944  				// match, and then remove those elements from the remote rule, so that
   945  				// this remote rule can still be considered by other local rules
   946  				if numExpectedCidrs == len(matchingCidrs) {
   947  					if numExpectedIpv6Cidrs == len(matchingIpv6Cidrs) {
   948  						if numExpectedPrefixLists == len(matchingPrefixLists) {
   949  							if numExpectedSGs == len(matchingSGs) {
   950  								// confirm that self references match
   951  								var lSelf bool
   952  								var rSelf bool
   953  								if _, ok := l["self"]; ok {
   954  									lSelf = l["self"].(bool)
   955  								}
   956  								if _, ok := r["self"]; ok {
   957  									rSelf = r["self"].(bool)
   958  								}
   959  								if rSelf == lSelf {
   960  									delete(r, "self")
   961  									// pop local cidrs from remote
   962  									diffCidr := remoteCidrSet.Difference(localCidrSet)
   963  									var newCidr []string
   964  									for _, cRaw := range diffCidr.List() {
   965  										newCidr = append(newCidr, cRaw.(string))
   966  									}
   967  
   968  									// reassigning
   969  									if len(newCidr) > 0 {
   970  										r["cidr_blocks"] = newCidr
   971  									} else {
   972  										delete(r, "cidr_blocks")
   973  									}
   974  
   975  									//// IPV6
   976  									//// Comparison
   977  									diffIpv6Cidr := remoteIpv6CidrSet.Difference(localIpv6CidrSet)
   978  									var newIpv6Cidr []string
   979  									for _, cRaw := range diffIpv6Cidr.List() {
   980  										newIpv6Cidr = append(newIpv6Cidr, cRaw.(string))
   981  									}
   982  
   983  									// reassigning
   984  									if len(newIpv6Cidr) > 0 {
   985  										r["ipv6_cidr_blocks"] = newIpv6Cidr
   986  									} else {
   987  										delete(r, "ipv6_cidr_blocks")
   988  									}
   989  
   990  									// pop local prefix lists from remote
   991  									diffPrefixLists := remotePrefixListsSet.Difference(localPrefixListsSet)
   992  									var newPrefixLists []string
   993  									for _, pRaw := range diffPrefixLists.List() {
   994  										newPrefixLists = append(newPrefixLists, pRaw.(string))
   995  									}
   996  
   997  									// reassigning
   998  									if len(newPrefixLists) > 0 {
   999  										r["prefix_list_ids"] = newPrefixLists
  1000  									} else {
  1001  										delete(r, "prefix_list_ids")
  1002  									}
  1003  
  1004  									// pop local sgs from remote
  1005  									diffSGs := remoteSGSet.Difference(localSGSet)
  1006  									if len(diffSGs.List()) > 0 {
  1007  										r["security_groups"] = diffSGs
  1008  									} else {
  1009  										delete(r, "security_groups")
  1010  									}
  1011  
  1012  									saves = append(saves, l)
  1013  								}
  1014  							}
  1015  						}
  1016  
  1017  					}
  1018  				}
  1019  			}
  1020  		}
  1021  	}
  1022  	// Here we catch any remote rules that have not been stripped of all self,
  1023  	// cidrs, and security groups. We'll add remote rules here that have not been
  1024  	// matched locally, and let the graph sort things out. This will happen when
  1025  	// rules are added externally to Terraform
  1026  	for _, r := range remote {
  1027  		var lenCidr, lenIpv6Cidr, lenPrefixLists, lenSGs int
  1028  		if rCidrs, ok := r["cidr_blocks"]; ok {
  1029  			lenCidr = len(rCidrs.([]string))
  1030  		}
  1031  		if rIpv6Cidrs, ok := r["ipv6_cidr_blocks"]; ok {
  1032  			lenIpv6Cidr = len(rIpv6Cidrs.([]string))
  1033  		}
  1034  		if rPrefixLists, ok := r["prefix_list_ids"]; ok {
  1035  			lenPrefixLists = len(rPrefixLists.([]string))
  1036  		}
  1037  		if rawSGs, ok := r["security_groups"]; ok {
  1038  			lenSGs = len(rawSGs.(*schema.Set).List())
  1039  		}
  1040  
  1041  		if _, ok := r["self"]; ok {
  1042  			if r["self"].(bool) == true {
  1043  				lenSGs++
  1044  			}
  1045  		}
  1046  
  1047  		if lenSGs+lenCidr+lenIpv6Cidr+lenPrefixLists > 0 {
  1048  			log.Printf("[DEBUG] Found a remote Rule that wasn't empty: (%#v)", r)
  1049  			saves = append(saves, r)
  1050  		}
  1051  	}
  1052  
  1053  	return saves
  1054  }
  1055  
  1056  // Creates a unique hash for the type, ports, and protocol, used as a key in
  1057  // maps
  1058  func idHash(rType, protocol string, toPort, fromPort int64, self bool) string {
  1059  	var buf bytes.Buffer
  1060  	buf.WriteString(fmt.Sprintf("%s-", rType))
  1061  	buf.WriteString(fmt.Sprintf("%d-", toPort))
  1062  	buf.WriteString(fmt.Sprintf("%d-", fromPort))
  1063  	buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(protocol)))
  1064  	buf.WriteString(fmt.Sprintf("%t-", self))
  1065  
  1066  	return fmt.Sprintf("rule-%d", hashcode.String(buf.String()))
  1067  }
  1068  
  1069  // protocolStateFunc ensures we only store a string in any protocol field
  1070  func protocolStateFunc(v interface{}) string {
  1071  	switch v.(type) {
  1072  	case string:
  1073  		p := protocolForValue(v.(string))
  1074  		return p
  1075  	default:
  1076  		log.Printf("[WARN] Non String value given for Protocol: %#v", v)
  1077  		return ""
  1078  	}
  1079  }
  1080  
  1081  // protocolForValue converts a valid Internet Protocol number into it's name
  1082  // representation. If a name is given, it validates that it's a proper protocol
  1083  // name. Names/numbers are as defined at
  1084  // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
  1085  func protocolForValue(v string) string {
  1086  	// special case -1
  1087  	protocol := strings.ToLower(v)
  1088  	if protocol == "-1" || protocol == "all" {
  1089  		return "-1"
  1090  	}
  1091  	// if it's a name like tcp, return that
  1092  	if _, ok := sgProtocolIntegers()[protocol]; ok {
  1093  		return protocol
  1094  	}
  1095  	// convert to int, look for that value
  1096  	p, err := strconv.Atoi(protocol)
  1097  	if err != nil {
  1098  		// we were unable to convert to int, suggesting a string name, but it wasn't
  1099  		// found above
  1100  		log.Printf("[WARN] Unable to determine valid protocol: %s", err)
  1101  		return protocol
  1102  	}
  1103  
  1104  	for k, v := range sgProtocolIntegers() {
  1105  		if p == v {
  1106  			// guard against protocolIntegers sometime in the future not having lower
  1107  			// case ids in the map
  1108  			return strings.ToLower(k)
  1109  		}
  1110  	}
  1111  
  1112  	// fall through
  1113  	log.Printf("[WARN] Unable to determine valid protocol: no matching protocols found")
  1114  	return protocol
  1115  }
  1116  
  1117  // a map of protocol names and their codes, defined at
  1118  // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml,
  1119  // documented to be supported by AWS Security Groups
  1120  // http://docs.aws.amazon.com/fr_fr/AWSEC2/latest/APIReference/API_IpPermission.html
  1121  // Similar to protocolIntegers() used by Network ACLs, but explicitly only
  1122  // supports "tcp", "udp", "icmp", and "all"
  1123  func sgProtocolIntegers() map[string]int {
  1124  	var protocolIntegers = make(map[string]int)
  1125  	protocolIntegers = map[string]int{
  1126  		"udp":  17,
  1127  		"tcp":  6,
  1128  		"icmp": 1,
  1129  		"all":  -1,
  1130  	}
  1131  	return protocolIntegers
  1132  }
  1133  
  1134  // The AWS Lambda service creates ENIs behind the scenes and keeps these around for a while
  1135  // which would prevent SGs attached to such ENIs from being destroyed
  1136  func deleteLingeringLambdaENIs(conn *ec2.EC2, d *schema.ResourceData) error {
  1137  	// Here we carefully find the offenders
  1138  	params := &ec2.DescribeNetworkInterfacesInput{
  1139  		Filters: []*ec2.Filter{
  1140  			{
  1141  				Name:   aws.String("group-id"),
  1142  				Values: []*string{aws.String(d.Id())},
  1143  			},
  1144  			{
  1145  				Name:   aws.String("description"),
  1146  				Values: []*string{aws.String("AWS Lambda VPC ENI: *")},
  1147  			},
  1148  		},
  1149  	}
  1150  	networkInterfaceResp, err := conn.DescribeNetworkInterfaces(params)
  1151  	if err != nil {
  1152  		return err
  1153  	}
  1154  
  1155  	// Then we detach and finally delete those
  1156  	v := networkInterfaceResp.NetworkInterfaces
  1157  	for _, eni := range v {
  1158  		if eni.Attachment != nil {
  1159  			detachNetworkInterfaceParams := &ec2.DetachNetworkInterfaceInput{
  1160  				AttachmentId: eni.Attachment.AttachmentId,
  1161  			}
  1162  			_, detachNetworkInterfaceErr := conn.DetachNetworkInterface(detachNetworkInterfaceParams)
  1163  
  1164  			if detachNetworkInterfaceErr != nil {
  1165  				return detachNetworkInterfaceErr
  1166  			}
  1167  
  1168  			log.Printf("[DEBUG] Waiting for ENI (%s) to become detached", *eni.NetworkInterfaceId)
  1169  			stateConf := &resource.StateChangeConf{
  1170  				Pending: []string{"true"},
  1171  				Target:  []string{"false"},
  1172  				Refresh: networkInterfaceAttachedRefreshFunc(conn, *eni.NetworkInterfaceId),
  1173  				Timeout: 10 * time.Minute,
  1174  			}
  1175  			if _, err := stateConf.WaitForState(); err != nil {
  1176  				return fmt.Errorf(
  1177  					"Error waiting for ENI (%s) to become detached: %s", *eni.NetworkInterfaceId, err)
  1178  			}
  1179  		}
  1180  
  1181  		deleteNetworkInterfaceParams := &ec2.DeleteNetworkInterfaceInput{
  1182  			NetworkInterfaceId: eni.NetworkInterfaceId,
  1183  		}
  1184  		_, deleteNetworkInterfaceErr := conn.DeleteNetworkInterface(deleteNetworkInterfaceParams)
  1185  
  1186  		if deleteNetworkInterfaceErr != nil {
  1187  			return deleteNetworkInterfaceErr
  1188  		}
  1189  	}
  1190  
  1191  	return nil
  1192  }
  1193  
  1194  func networkInterfaceAttachedRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
  1195  	return func() (interface{}, string, error) {
  1196  
  1197  		describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesInput{
  1198  			NetworkInterfaceIds: []*string{aws.String(id)},
  1199  		}
  1200  		describeResp, err := conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
  1201  
  1202  		if err != nil {
  1203  			log.Printf("[ERROR] Could not find network interface %s. %s", id, err)
  1204  			return nil, "", err
  1205  		}
  1206  
  1207  		eni := describeResp.NetworkInterfaces[0]
  1208  		hasAttachment := strconv.FormatBool(eni.Attachment != nil)
  1209  		log.Printf("[DEBUG] ENI %s has attachment state %s", id, hasAttachment)
  1210  		return eni, hasAttachment, nil
  1211  	}
  1212  }