diff --git a/builtin/providers/aws/import_aws_security_group.go b/builtin/providers/aws/import_aws_security_group.go index 21b7e64e2..d802c75e2 100644 --- a/builtin/providers/aws/import_aws_security_group.go +++ b/builtin/providers/aws/import_aws_security_group.go @@ -66,13 +66,20 @@ func resourceAwsSecurityGroupImportStatePerm(sg *ec2.SecurityGroup, ruleType str p := &ec2.IpPermission{ FromPort: perm.FromPort, IpProtocol: perm.IpProtocol, - IpRanges: perm.IpRanges, PrefixListIds: perm.PrefixListIds, ToPort: perm.ToPort, UserIdGroupPairs: []*ec2.UserIdGroupPair{pair}, } + if perm.Ipv6Ranges != nil { + p.Ipv6Ranges = perm.Ipv6Ranges + } + + if perm.IpRanges != nil { + p.IpRanges = perm.IpRanges + } + r, err := resourceAwsSecurityGroupImportStatePermPair(sg, ruleType, p) if err != nil { return nil, err diff --git a/builtin/providers/aws/import_aws_security_group_test.go b/builtin/providers/aws/import_aws_security_group_test.go index d2bf91205..4b0597670 100644 --- a/builtin/providers/aws/import_aws_security_group_test.go +++ b/builtin/providers/aws/import_aws_security_group_test.go @@ -23,11 +23,39 @@ func TestAccAWSSecurityGroup_importBasic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig, }, - resource.TestStep{ + { + ResourceName: "aws_security_group.web", + ImportState: true, + ImportStateCheck: checkFn, + }, + }, + }) +} + +func TestAccAWSSecurityGroup_importIpv6(t *testing.T) { + checkFn := func(s []*terraform.InstanceState) error { + // Expect 3: group, 2 rules + if len(s) != 3 { + return fmt.Errorf("expected 3 states: %#v", s) + } + + return nil + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSecurityGroupConfigIpv6, + }, + + { ResourceName: "aws_security_group.web", ImportState: true, ImportStateCheck: checkFn, @@ -42,11 +70,11 @@ func TestAccAWSSecurityGroup_importSelf(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_importSelf, }, - resource.TestStep{ + { ResourceName: "aws_security_group.allow_all", ImportState: true, ImportStateVerify: true, @@ -61,11 +89,11 @@ func TestAccAWSSecurityGroup_importSourceSecurityGroup(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_importSourceSecurityGroup, }, - resource.TestStep{ + { ResourceName: "aws_security_group.test_group_1", ImportState: true, ImportStateVerify: true, diff --git a/builtin/providers/aws/resource_aws_security_group.go b/builtin/providers/aws/resource_aws_security_group.go index 4c34fea96..4598a4426 100644 --- a/builtin/providers/aws/resource_aws_security_group.go +++ b/builtin/providers/aws/resource_aws_security_group.go @@ -28,7 +28,7 @@ func resourceAwsSecurityGroup() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ + "name": { Type: schema.TypeString, Optional: true, Computed: true, @@ -44,7 +44,7 @@ func resourceAwsSecurityGroup() *schema.Resource { }, }, - "name_prefix": &schema.Schema{ + "name_prefix": { Type: schema.TypeString, Optional: true, ForceNew: true, @@ -58,7 +58,7 @@ func resourceAwsSecurityGroup() *schema.Resource { }, }, - "description": &schema.Schema{ + "description": { Type: schema.TypeString, Optional: true, ForceNew: true, @@ -73,49 +73,55 @@ func resourceAwsSecurityGroup() *schema.Resource { }, }, - "vpc_id": &schema.Schema{ + "vpc_id": { Type: schema.TypeString, Optional: true, ForceNew: true, Computed: true, }, - "ingress": &schema.Schema{ + "ingress": { Type: schema.TypeSet, Optional: true, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "from_port": &schema.Schema{ + "from_port": { Type: schema.TypeInt, Required: true, }, - "to_port": &schema.Schema{ + "to_port": { Type: schema.TypeInt, Required: true, }, - "protocol": &schema.Schema{ + "protocol": { Type: schema.TypeString, Required: true, StateFunc: protocolStateFunc, }, - "cidr_blocks": &schema.Schema{ + "cidr_blocks": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "security_groups": &schema.Schema{ + "ipv6_cidr_blocks": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "security_groups": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, - "self": &schema.Schema{ + "self": { Type: schema.TypeBool, Optional: true, Default: false, @@ -125,48 +131,54 @@ func resourceAwsSecurityGroup() *schema.Resource { Set: resourceAwsSecurityGroupRuleHash, }, - "egress": &schema.Schema{ + "egress": { Type: schema.TypeSet, Optional: true, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "from_port": &schema.Schema{ + "from_port": { Type: schema.TypeInt, Required: true, }, - "to_port": &schema.Schema{ + "to_port": { Type: schema.TypeInt, Required: true, }, - "protocol": &schema.Schema{ + "protocol": { Type: schema.TypeString, Required: true, StateFunc: protocolStateFunc, }, - "cidr_blocks": &schema.Schema{ + "cidr_blocks": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "prefix_list_ids": &schema.Schema{ + "ipv6_cidr_blocks": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "security_groups": &schema.Schema{ + "prefix_list_ids": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "security_groups": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, - "self": &schema.Schema{ + "self": { Type: schema.TypeBool, Optional: true, Default: false, @@ -176,7 +188,7 @@ func resourceAwsSecurityGroup() *schema.Resource { Set: resourceAwsSecurityGroupRuleHash, }, - "owner_id": &schema.Schema{ + "owner_id": { Type: schema.TypeString, Computed: true, }, @@ -252,11 +264,11 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er req := &ec2.RevokeSecurityGroupEgressInput{ GroupId: createResp.GroupId, IpPermissions: []*ec2.IpPermission{ - &ec2.IpPermission{ + { FromPort: aws.Int64(int64(0)), ToPort: aws.Int64(int64(0)), IpRanges: []*ec2.IpRange{ - &ec2.IpRange{ + { CidrIp: aws.String("0.0.0.0/0"), }, }, @@ -412,6 +424,18 @@ func resourceAwsSecurityGroupRuleHash(v interface{}) int { buf.WriteString(fmt.Sprintf("%s-", v)) } } + if v, ok := m["ipv6_cidr_blocks"]; ok { + vs := v.([]interface{}) + s := make([]string, len(vs)) + for i, raw := range vs { + s[i] = raw.(string) + } + sort.Strings(s) + + for _, v := range s { + buf.WriteString(fmt.Sprintf("%s-", v)) + } + } if v, ok := m["prefix_list_ids"]; ok { vs := v.([]interface{}) s := make([]string, len(vs)) @@ -476,6 +500,20 @@ func resourceAwsSecurityGroupIPPermGather(groupId string, permissions []*ec2.IpP m["cidr_blocks"] = list } + if len(perm.Ipv6Ranges) > 0 { + raw, ok := m["ipv6_cidr_blocks"] + if !ok { + raw = make([]string, 0, len(perm.Ipv6Ranges)) + } + list := raw.([]string) + + for _, ip := range perm.Ipv6Ranges { + list = append(list, *ip.CidrIpv6) + } + + m["ipv6_cidr_blocks"] = list + } + if len(perm.PrefixListIds) > 0 { raw, ok := m["prefix_list_ids"] if !ok { @@ -699,8 +737,9 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface // local rule we're examining rHash := idHash(rType, r["protocol"].(string), r["to_port"].(int64), r["from_port"].(int64), remoteSelfVal) if rHash == localHash { - var numExpectedCidrs, numExpectedPrefixLists, numExpectedSGs, numRemoteCidrs, numRemotePrefixLists, numRemoteSGs int + var numExpectedCidrs, numExpectedIpv6Cidrs, numExpectedPrefixLists, numExpectedSGs, numRemoteCidrs, numRemoteIpv6Cidrs, numRemotePrefixLists, numRemoteSGs int var matchingCidrs []string + var matchingIpv6Cidrs []string var matchingSGs []string var matchingPrefixLists []string @@ -710,6 +749,10 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface if ok { numExpectedCidrs = len(l["cidr_blocks"].([]interface{})) } + liRaw, ok := l["ipv6_cidr_blocks"] + if ok { + numExpectedIpv6Cidrs = len(l["ipv6_cidr_blocks"].([]interface{})) + } lpRaw, ok := l["prefix_list_ids"] if ok { numExpectedPrefixLists = len(l["prefix_list_ids"].([]interface{})) @@ -723,6 +766,10 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface if ok { numRemoteCidrs = len(r["cidr_blocks"].([]string)) } + riRaw, ok := r["ipv6_cidr_blocks"] + if ok { + numRemoteIpv6Cidrs = len(r["ipv6_cidr_blocks"].([]string)) + } rpRaw, ok := r["prefix_list_ids"] if ok { numRemotePrefixLists = len(r["prefix_list_ids"].([]string)) @@ -738,6 +785,10 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface log.Printf("[DEBUG] Local rule has more CIDR blocks, continuing (%d/%d)", numExpectedCidrs, numRemoteCidrs) continue } + if numExpectedIpv6Cidrs > numRemoteIpv6Cidrs { + log.Printf("[DEBUG] Local rule has more IPV6 CIDR blocks, continuing (%d/%d)", numExpectedIpv6Cidrs, numRemoteIpv6Cidrs) + continue + } if numExpectedPrefixLists > numRemotePrefixLists { log.Printf("[DEBUG] Local rule has more prefix lists, continuing (%d/%d)", numExpectedPrefixLists, numRemotePrefixLists) continue @@ -775,6 +826,29 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface } } + //IPV6 CIDRs + var localIpv6Cidrs []interface{} + if liRaw != nil { + localIpv6Cidrs = liRaw.([]interface{}) + } + localIpv6CidrSet := schema.NewSet(schema.HashString, localIpv6Cidrs) + + var remoteIpv6Cidrs []string + if riRaw != nil { + remoteIpv6Cidrs = riRaw.([]string) + } + var listIpv6 []interface{} + for _, s := range remoteIpv6Cidrs { + listIpv6 = append(listIpv6, s) + } + remoteIpv6CidrSet := schema.NewSet(schema.HashString, listIpv6) + + for _, s := range localIpv6CidrSet.List() { + if remoteIpv6CidrSet.Contains(s) { + matchingIpv6Cidrs = append(matchingIpv6Cidrs, s.(string)) + } + } + // match prefix lists by converting both to sets, and using Set methods var localPrefixLists []interface{} if lpRaw != nil { @@ -830,73 +904,93 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface // match, and then remove those elements from the remote rule, so that // this remote rule can still be considered by other local rules if numExpectedCidrs == len(matchingCidrs) { - if numExpectedPrefixLists == len(matchingPrefixLists) { - if numExpectedSGs == len(matchingSGs) { - // confirm that self references match - var lSelf bool - var rSelf bool - if _, ok := l["self"]; ok { - lSelf = l["self"].(bool) - } - if _, ok := r["self"]; ok { - rSelf = r["self"].(bool) - } - if rSelf == lSelf { - delete(r, "self") - // pop local cidrs from remote - diffCidr := remoteCidrSet.Difference(localCidrSet) - var newCidr []string - for _, cRaw := range diffCidr.List() { - newCidr = append(newCidr, cRaw.(string)) + if numExpectedIpv6Cidrs == len(matchingIpv6Cidrs) { + if numExpectedPrefixLists == len(matchingPrefixLists) { + if numExpectedSGs == len(matchingSGs) { + // confirm that self references match + var lSelf bool + var rSelf bool + if _, ok := l["self"]; ok { + lSelf = l["self"].(bool) } - - // reassigning - if len(newCidr) > 0 { - r["cidr_blocks"] = newCidr - } else { - delete(r, "cidr_blocks") + if _, ok := r["self"]; ok { + rSelf = r["self"].(bool) } + if rSelf == lSelf { + delete(r, "self") + // pop local cidrs from remote + diffCidr := remoteCidrSet.Difference(localCidrSet) + var newCidr []string + for _, cRaw := range diffCidr.List() { + newCidr = append(newCidr, cRaw.(string)) + } - // pop local prefix lists from remote - diffPrefixLists := remotePrefixListsSet.Difference(localPrefixListsSet) - var newPrefixLists []string - for _, pRaw := range diffPrefixLists.List() { - newPrefixLists = append(newPrefixLists, pRaw.(string)) + // reassigning + if len(newCidr) > 0 { + r["cidr_blocks"] = newCidr + } else { + delete(r, "cidr_blocks") + } + + //// IPV6 + //// Comparison + diffIpv6Cidr := remoteIpv6CidrSet.Difference(localIpv6CidrSet) + var newIpv6Cidr []string + for _, cRaw := range diffIpv6Cidr.List() { + newIpv6Cidr = append(newIpv6Cidr, cRaw.(string)) + } + + // reassigning + if len(newIpv6Cidr) > 0 { + r["ipv6_cidr_blocks"] = newIpv6Cidr + } else { + delete(r, "ipv6_cidr_blocks") + } + + // pop local prefix lists from remote + diffPrefixLists := remotePrefixListsSet.Difference(localPrefixListsSet) + var newPrefixLists []string + for _, pRaw := range diffPrefixLists.List() { + newPrefixLists = append(newPrefixLists, pRaw.(string)) + } + + // reassigning + if len(newPrefixLists) > 0 { + r["prefix_list_ids"] = newPrefixLists + } else { + delete(r, "prefix_list_ids") + } + + // pop local sgs from remote + diffSGs := remoteSGSet.Difference(localSGSet) + if len(diffSGs.List()) > 0 { + r["security_groups"] = diffSGs + } else { + delete(r, "security_groups") + } + + saves = append(saves, l) } - - // reassigning - if len(newPrefixLists) > 0 { - r["prefix_list_ids"] = newPrefixLists - } else { - delete(r, "prefix_list_ids") - } - - // pop local sgs from remote - diffSGs := remoteSGSet.Difference(localSGSet) - if len(diffSGs.List()) > 0 { - r["security_groups"] = diffSGs - } else { - delete(r, "security_groups") - } - - saves = append(saves, l) } } + } } } } } - // Here we catch any remote rules that have not been stripped of all self, // cidrs, and security groups. We'll add remote rules here that have not been // matched locally, and let the graph sort things out. This will happen when // rules are added externally to Terraform for _, r := range remote { - var lenCidr, lenPrefixLists, lenSGs int + var lenCidr, lenIpv6Cidr, lenPrefixLists, lenSGs int if rCidrs, ok := r["cidr_blocks"]; ok { lenCidr = len(rCidrs.([]string)) } + if rIpv6Cidrs, ok := r["ipv6_cidr_blocks"]; ok { + lenIpv6Cidr = len(rIpv6Cidrs.([]string)) + } if rPrefixLists, ok := r["prefix_list_ids"]; ok { lenPrefixLists = len(rPrefixLists.([]string)) } @@ -910,7 +1004,7 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface } } - if lenSGs+lenCidr+lenPrefixLists > 0 { + if lenSGs+lenCidr+lenIpv6Cidr+lenPrefixLists > 0 { log.Printf("[DEBUG] Found a remote Rule that wasn't empty: (%#v)", r) saves = append(saves, r) } @@ -1003,15 +1097,15 @@ func deleteLingeringLambdaENIs(conn *ec2.EC2, d *schema.ResourceData) error { // Here we carefully find the offenders params := &ec2.DescribeNetworkInterfacesInput{ Filters: []*ec2.Filter{ - &ec2.Filter{ + { Name: aws.String("group-id"), Values: []*string{aws.String(d.Id())}, }, - &ec2.Filter{ + { Name: aws.String("description"), Values: []*string{aws.String("AWS Lambda VPC ENI: *")}, }, - &ec2.Filter{ + { Name: aws.String("requester-id"), Values: []*string{aws.String("*:awslambda_*")}, }, diff --git a/builtin/providers/aws/resource_aws_security_group_test.go b/builtin/providers/aws/resource_aws_security_group_test.go index 4c4053770..37cf2503a 100644 --- a/builtin/providers/aws/resource_aws_security_group_test.go +++ b/builtin/providers/aws/resource_aws_security_group_test.go @@ -135,54 +135,54 @@ func TestProtocolForValue(t *testing.T) { func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) { raw := []*ec2.IpPermission{ - &ec2.IpPermission{ + { IpProtocol: aws.String("tcp"), FromPort: aws.Int64(int64(1)), ToPort: aws.Int64(int64(-1)), - IpRanges: []*ec2.IpRange{&ec2.IpRange{CidrIp: aws.String("0.0.0.0/0")}}, + IpRanges: []*ec2.IpRange{{CidrIp: aws.String("0.0.0.0/0")}}, UserIdGroupPairs: []*ec2.UserIdGroupPair{ - &ec2.UserIdGroupPair{ + { GroupId: aws.String("sg-11111"), }, }, }, - &ec2.IpPermission{ + { IpProtocol: aws.String("tcp"), FromPort: aws.Int64(int64(80)), ToPort: aws.Int64(int64(80)), UserIdGroupPairs: []*ec2.UserIdGroupPair{ // VPC - &ec2.UserIdGroupPair{ + { GroupId: aws.String("sg-22222"), }, }, }, - &ec2.IpPermission{ + { IpProtocol: aws.String("tcp"), FromPort: aws.Int64(int64(443)), ToPort: aws.Int64(int64(443)), UserIdGroupPairs: []*ec2.UserIdGroupPair{ // Classic - &ec2.UserIdGroupPair{ + { UserId: aws.String("12345"), GroupId: aws.String("sg-33333"), GroupName: aws.String("ec2_classic"), }, - &ec2.UserIdGroupPair{ + { UserId: aws.String("amazon-elb"), GroupId: aws.String("sg-d2c979d3"), GroupName: aws.String("amazon-elb-sg"), }, }, }, - &ec2.IpPermission{ + { IpProtocol: aws.String("-1"), FromPort: aws.Int64(int64(0)), ToPort: aws.Int64(int64(0)), - PrefixListIds: []*ec2.PrefixListId{&ec2.PrefixListId{PrefixListId: aws.String("pl-12345678")}}, + PrefixListIds: []*ec2.PrefixListId{{PrefixListId: aws.String("pl-12345678")}}, UserIdGroupPairs: []*ec2.UserIdGroupPair{ // VPC - &ec2.UserIdGroupPair{ + { GroupId: aws.String("sg-22222"), }, }, @@ -190,14 +190,14 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) { } local := []map[string]interface{}{ - map[string]interface{}{ + { "protocol": "tcp", "from_port": int64(1), "to_port": int64(-1), "cidr_blocks": []string{"0.0.0.0/0"}, "self": true, }, - map[string]interface{}{ + { "protocol": "tcp", "from_port": int64(80), "to_port": int64(80), @@ -205,7 +205,7 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) { "sg-22222", }), }, - map[string]interface{}{ + { "protocol": "tcp", "from_port": int64(443), "to_port": int64(443), @@ -214,7 +214,7 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) { "amazon-elb/amazon-elb-sg", }), }, - map[string]interface{}{ + { "protocol": "-1", "from_port": int64(0), "to_port": int64(0), @@ -263,7 +263,7 @@ func TestAccAWSSecurityGroup_basic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -288,6 +288,39 @@ func TestAccAWSSecurityGroup_basic(t *testing.T) { }) } +func TestAccAWSSecurityGroup_ipv6(t *testing.T) { + var group ec2.SecurityGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_security_group.web", + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSecurityGroupConfigIpv6, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), + resource.TestCheckResourceAttr( + "aws_security_group.web", "name", "terraform_acceptance_test_example"), + resource.TestCheckResourceAttr( + "aws_security_group.web", "description", "Used in the terraform acceptance tests"), + resource.TestCheckResourceAttr( + "aws_security_group.web", "ingress.2293451516.protocol", "tcp"), + resource.TestCheckResourceAttr( + "aws_security_group.web", "ingress.2293451516.from_port", "80"), + resource.TestCheckResourceAttr( + "aws_security_group.web", "ingress.2293451516.to_port", "8000"), + resource.TestCheckResourceAttr( + "aws_security_group.web", "ingress.2293451516.ipv6_cidr_blocks.#", "1"), + resource.TestCheckResourceAttr( + "aws_security_group.web", "ingress.2293451516.ipv6_cidr_blocks.0", "::/0"), + ), + }, + }, + }) +} + func TestAccAWSSecurityGroup_tagsCreatedFirst(t *testing.T) { var group ec2.SecurityGroup @@ -296,7 +329,7 @@ func TestAccAWSSecurityGroup_tagsCreatedFirst(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigForTagsOrdering, ExpectError: regexp.MustCompile("InvalidParameterValue"), Check: resource.ComposeTestCheckFunc( @@ -318,7 +351,7 @@ func TestAccAWSSecurityGroup_namePrefix(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupPrefixNameConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.baz", &group), @@ -353,7 +386,7 @@ func TestAccAWSSecurityGroup_self(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigSelf, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -393,7 +426,7 @@ func TestAccAWSSecurityGroup_vpc(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigVpc, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -446,7 +479,7 @@ func TestAccAWSSecurityGroup_vpcNegOneIngress(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigVpcNegOneIngress, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -488,7 +521,7 @@ func TestAccAWSSecurityGroup_vpcProtoNumIngress(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigVpcProtoNumIngress, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -521,7 +554,7 @@ func TestAccAWSSecurityGroup_MultiIngress(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigMultiIngress, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -540,13 +573,13 @@ func TestAccAWSSecurityGroup_Change(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), ), }, - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigChange, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -566,7 +599,7 @@ func TestAccAWSSecurityGroup_generatedName(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_generatedName, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -596,7 +629,7 @@ func TestAccAWSSecurityGroup_DefaultEgress_VPC(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigDefaultEgress, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExistsWithoutDefault("aws_security_group.worker"), @@ -616,7 +649,7 @@ func TestAccAWSSecurityGroup_DefaultEgress_Classic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigClassic, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -634,7 +667,7 @@ func TestAccAWSSecurityGroup_drift(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_drift(), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -664,7 +697,7 @@ func TestAccAWSSecurityGroup_drift_complex(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_drift_complex(), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -773,7 +806,7 @@ func testAccCheckAWSSecurityGroupAttributes(group *ec2.SecurityGroup) resource.T FromPort: aws.Int64(80), ToPort: aws.Int64(8000), IpProtocol: aws.String("tcp"), - IpRanges: []*ec2.IpRange{&ec2.IpRange{CidrIp: aws.String("10.0.0.0/8")}}, + IpRanges: []*ec2.IpRange{{CidrIp: aws.String("10.0.0.0/8")}}, } if *group.GroupName != "terraform_acceptance_test_example" { @@ -804,7 +837,7 @@ func testAccCheckAWSSecurityGroupAttributesNegOneProtocol(group *ec2.SecurityGro return func(s *terraform.State) error { p := &ec2.IpPermission{ IpProtocol: aws.String("-1"), - IpRanges: []*ec2.IpRange{&ec2.IpRange{CidrIp: aws.String("10.0.0.0/8")}}, + IpRanges: []*ec2.IpRange{{CidrIp: aws.String("10.0.0.0/8")}}, } if *group.GroupName != "terraform_acceptance_test_example" { @@ -839,7 +872,7 @@ func TestAccAWSSecurityGroup_tags(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigTags, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.foo", &group), @@ -847,7 +880,7 @@ func TestAccAWSSecurityGroup_tags(t *testing.T) { ), }, - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigTagsUpdate, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.foo", &group), @@ -868,7 +901,7 @@ func TestAccAWSSecurityGroup_CIDRandGroups(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupCombindCIDRandGroups, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.mixed", &group), @@ -887,7 +920,7 @@ func TestAccAWSSecurityGroup_ingressWithCidrAndSGs(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_ingressWithCidrAndSGs, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -913,7 +946,7 @@ func TestAccAWSSecurityGroup_ingressWithCidrAndSGs_classic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_ingressWithCidrAndSGs_classic, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), @@ -938,7 +971,7 @@ func TestAccAWSSecurityGroup_egressWithPrefixList(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfigPrefixListEgress, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.egress", &group), @@ -1016,21 +1049,21 @@ func testAccCheckAWSSecurityGroupPrefixListAttributes(group *ec2.SecurityGroup) func testAccCheckAWSSecurityGroupAttributesChanged(group *ec2.SecurityGroup) resource.TestCheckFunc { return func(s *terraform.State) error { p := []*ec2.IpPermission{ - &ec2.IpPermission{ + { FromPort: aws.Int64(80), ToPort: aws.Int64(9000), IpProtocol: aws.String("tcp"), - IpRanges: []*ec2.IpRange{&ec2.IpRange{CidrIp: aws.String("10.0.0.0/8")}}, + IpRanges: []*ec2.IpRange{{CidrIp: aws.String("10.0.0.0/8")}}, }, - &ec2.IpPermission{ + { FromPort: aws.Int64(80), ToPort: aws.Int64(8000), IpProtocol: aws.String("tcp"), IpRanges: []*ec2.IpRange{ - &ec2.IpRange{ + { CidrIp: aws.String("0.0.0.0/0"), }, - &ec2.IpRange{ + { CidrIp: aws.String("10.0.0.0/8"), }, }, @@ -1109,7 +1142,7 @@ func TestAccAWSSecurityGroup_failWithDiffMismatch(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSSecurityGroupConfig_failWithDiffMismatch, Check: resource.ComposeTestCheckFunc( testAccCheckAWSSecurityGroupExists("aws_security_group.nat", &group), @@ -1148,6 +1181,36 @@ resource "aws_security_group" "web" { } }` +const testAccAWSSecurityGroupConfigIpv6 = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_security_group" "web" { + name = "terraform_acceptance_test_example" + description = "Used in the terraform acceptance tests" + vpc_id = "${aws_vpc.foo.id}" + + ingress { + protocol = "6" + from_port = 80 + to_port = 8000 + ipv6_cidr_blocks = ["::/0"] + } + + egress { + protocol = "tcp" + from_port = 80 + to_port = 8000 + ipv6_cidr_blocks = ["::/0"] + } + + tags { + Name = "tf-acc-test" + } +} +` + const testAccAWSSecurityGroupConfig = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index 5fc791f8e..24aec4ed2 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -216,6 +216,12 @@ func expandIPPerms( perm.IpRanges = append(perm.IpRanges, &ec2.IpRange{CidrIp: aws.String(v.(string))}) } } + if raw, ok := m["ipv6_cidr_blocks"]; ok { + list := raw.([]interface{}) + for _, v := range list { + perm.Ipv6Ranges = append(perm.Ipv6Ranges, &ec2.Ipv6Range{CidrIpv6: aws.String(v.(string))}) + } + } if raw, ok := m["prefix_list_ids"]; ok { list := raw.([]interface{}) diff --git a/website/source/docs/providers/aws/r/security_group.html.markdown b/website/source/docs/providers/aws/r/security_group.html.markdown index 50aa38075..7404de588 100644 --- a/website/source/docs/providers/aws/r/security_group.html.markdown +++ b/website/source/docs/providers/aws/r/security_group.html.markdown @@ -85,6 +85,7 @@ assign a random, unique name The `ingress` block supports: * `cidr_blocks` - (Optional) List of CIDR blocks. +* `ipv6_cidr_blocks` - (Optional) List of IPv6 CIDR blocks. * `from_port` - (Required) The start port (or ICMP type number if protocol is "icmp") * `protocol` - (Required) The protocol. If you select a protocol of "-1" (semantically equivalent to `"all"`, which is not a valid value here), you must specify a "from_port" and "to_port" equal to 0. If not icmp, tcp, udp, or "-1" use the [protocol number](https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml) @@ -97,6 +98,7 @@ The `ingress` block supports: The `egress` block supports: * `cidr_blocks` - (Optional) List of CIDR blocks. +* `ipv6_cidr_blocks` - (Optional) List of IPv6 CIDR blocks. * `prefix_list_ids` - (Optional) List of prefix list IDs (for allowing access to VPC endpoints) * `from_port` - (Required) The start port (or ICMP type number if protocol is "icmp") * `protocol` - (Required) The protocol. If you select a protocol of