diff --git a/builtin/providers/aws/resource_aws_security_group_rule.go b/builtin/providers/aws/resource_aws_security_group_rule.go index 270b41ed24dd..cefeef96e841 100644 --- a/builtin/providers/aws/resource_aws_security_group_rule.go +++ b/builtin/providers/aws/resource_aws_security_group_rule.go @@ -263,6 +263,26 @@ func findResourceSecurityGroup(conn *ec2.EC2, id string) (*ec2.SecurityGroup, er return resp.SecurityGroups[0], nil } +// byUserIDAndGroup implements sort.Interface for []*ec2.UserIDGroupPairs based on +// UserID and then the GroupID or GroupName field (only one should be set). +type byUserIDAndGroup []*ec2.UserIDGroupPair + +func (b byUserIDAndGroup) Len() int { return len(b) } +func (b byUserIDAndGroup) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byUserIDAndGroup) Less(i, j int) bool { + if *b[i].UserID != *b[i].UserID { + return *b[i].UserID < *b[i].UserID + } + if b[i].GroupID != nil && b[j].GroupID != nil { + return *b[i].GroupID < *b[j].GroupID + } + if b[i].GroupName != nil && b[j].GroupName != nil { + return *b[i].GroupName < *b[j].GroupName + } + + panic("mismatched security group rules, may be a terraform bug") +} + func ipPermissionIDHash(ruleType string, ip *ec2.IPPermission) string { var buf bytes.Buffer // for egress rules, an TCP rule of -1 is automatically added, in which case @@ -288,6 +308,23 @@ func ipPermissionIDHash(ruleType string, ip *ec2.IPPermission) string { } } + if len(ip.UserIDGroupPairs) > 0 { + sort.Sort(byUserIDAndGroup(ip.UserIDGroupPairs)) + for _, pair := range ip.UserIDGroupPairs { + buf.WriteString(fmt.Sprintf("%s-", *pair.UserID)) + if pair.GroupID != nil { + buf.WriteString(fmt.Sprintf("%s-", *pair.GroupID)) + } else { + buf.WriteString("-") + } + if pair.GroupName != nil { + buf.WriteString(fmt.Sprintf("%s-", *pair.GroupName)) + } else { + buf.WriteString("-") + } + } + } + return fmt.Sprintf("sg-%d", hashcode.String(buf.String())) } diff --git a/builtin/providers/aws/resource_aws_security_group_rule_test.go b/builtin/providers/aws/resource_aws_security_group_rule_test.go index f322ba44eaf4..2b99b0c159f5 100644 --- a/builtin/providers/aws/resource_aws_security_group_rule_test.go +++ b/builtin/providers/aws/resource_aws_security_group_rule_test.go @@ -44,6 +44,46 @@ func TestIpPermissionIDHash(t *testing.T) { }, } + vpc_security_group_source := &ec2.IPPermission{ + IPProtocol: aws.String("tcp"), + FromPort: aws.Long(int64(80)), + ToPort: aws.Long(int64(8000)), + UserIDGroupPairs: []*ec2.UserIDGroupPair{ + &ec2.UserIDGroupPair{ + UserID: aws.String("987654321"), + GroupID: aws.String("sg-12345678"), + }, + &ec2.UserIDGroupPair{ + UserID: aws.String("123456789"), + GroupID: aws.String("sg-987654321"), + }, + &ec2.UserIDGroupPair{ + UserID: aws.String("123456789"), + GroupID: aws.String("sg-12345678"), + }, + }, + } + + security_group_source := &ec2.IPPermission{ + IPProtocol: aws.String("tcp"), + FromPort: aws.Long(int64(80)), + ToPort: aws.Long(int64(8000)), + UserIDGroupPairs: []*ec2.UserIDGroupPair{ + &ec2.UserIDGroupPair{ + UserID: aws.String("987654321"), + GroupName: aws.String("my-security-group"), + }, + &ec2.UserIDGroupPair{ + UserID: aws.String("123456789"), + GroupName: aws.String("my-security-group"), + }, + &ec2.UserIDGroupPair{ + UserID: aws.String("123456789"), + GroupName: aws.String("my-other-security-group"), + }, + }, + } + // hardcoded hashes, to detect future change cases := []struct { Input *ec2.IPPermission @@ -53,12 +93,14 @@ func TestIpPermissionIDHash(t *testing.T) { {simple, "ingress", "sg-82613597"}, {egress, "egress", "sg-363054720"}, {egress_all, "egress", "sg-857124156"}, + {vpc_security_group_source, "egress", "sg-1900174468"}, + {security_group_source, "egress", "sg-1409919872"}, } for _, tc := range cases { actual := ipPermissionIDHash(tc.Type, tc.Input) if actual != tc.Output { - t.Fatalf("input: %s - %#v\noutput: %s", tc.Type, tc.Input, actual) + t.Errorf("input: %s - %#v\noutput: %s", tc.Type, tc.Input, actual) } } }