Merge pull request #7511 from hashicorp/pr-7319
provider/aws: AWS prefix lists to enable security group egress to a VPC Endpoint (supersedes #7319)
This commit is contained in:
commit
17931c7099
|
@ -153,6 +153,12 @@ func resourceAwsSecurityGroup() *schema.Resource {
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"prefix_list_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
"security_groups": &schema.Schema{
|
"security_groups": &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
@ -397,6 +403,18 @@ func resourceAwsSecurityGroupRuleHash(v interface{}) int {
|
||||||
buf.WriteString(fmt.Sprintf("%s-", v))
|
buf.WriteString(fmt.Sprintf("%s-", v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if v, ok := m["prefix_list_ids"]; 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["security_groups"]; ok {
|
if v, ok := m["security_groups"]; ok {
|
||||||
vs := v.(*schema.Set).List()
|
vs := v.(*schema.Set).List()
|
||||||
s := make([]string, len(vs))
|
s := make([]string, len(vs))
|
||||||
|
@ -449,6 +467,20 @@ func resourceAwsSecurityGroupIPPermGather(groupId string, permissions []*ec2.IpP
|
||||||
m["cidr_blocks"] = list
|
m["cidr_blocks"] = list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(perm.PrefixListIds) > 0 {
|
||||||
|
raw, ok := m["prefix_list_ids"]
|
||||||
|
if !ok {
|
||||||
|
raw = make([]string, 0, len(perm.PrefixListIds))
|
||||||
|
}
|
||||||
|
list := raw.([]string)
|
||||||
|
|
||||||
|
for _, pl := range perm.PrefixListIds {
|
||||||
|
list = append(list, *pl.PrefixListId)
|
||||||
|
}
|
||||||
|
|
||||||
|
m["prefix_list_ids"] = list
|
||||||
|
}
|
||||||
|
|
||||||
groups := flattenSecurityGroups(perm.UserIdGroupPairs, ownerId)
|
groups := flattenSecurityGroups(perm.UserIdGroupPairs, ownerId)
|
||||||
for i, g := range groups {
|
for i, g := range groups {
|
||||||
if *g.GroupId == groupId {
|
if *g.GroupId == groupId {
|
||||||
|
@ -658,9 +690,10 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface
|
||||||
// local rule we're examining
|
// local rule we're examining
|
||||||
rHash := idHash(rType, r["protocol"].(string), r["to_port"].(int64), r["from_port"].(int64), remoteSelfVal)
|
rHash := idHash(rType, r["protocol"].(string), r["to_port"].(int64), r["from_port"].(int64), remoteSelfVal)
|
||||||
if rHash == localHash {
|
if rHash == localHash {
|
||||||
var numExpectedCidrs, numExpectedSGs, numRemoteCidrs, numRemoteSGs int
|
var numExpectedCidrs, numExpectedPrefixLists, numExpectedSGs, numRemoteCidrs, numRemotePrefixLists, numRemoteSGs int
|
||||||
var matchingCidrs []string
|
var matchingCidrs []string
|
||||||
var matchingSGs []string
|
var matchingSGs []string
|
||||||
|
var matchingPrefixLists []string
|
||||||
|
|
||||||
// grab the local/remote cidr and sg groups, capturing the expected and
|
// grab the local/remote cidr and sg groups, capturing the expected and
|
||||||
// actual counts
|
// actual counts
|
||||||
|
@ -668,6 +701,10 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface
|
||||||
if ok {
|
if ok {
|
||||||
numExpectedCidrs = len(l["cidr_blocks"].([]interface{}))
|
numExpectedCidrs = len(l["cidr_blocks"].([]interface{}))
|
||||||
}
|
}
|
||||||
|
lpRaw, ok := l["prefix_list_ids"]
|
||||||
|
if ok {
|
||||||
|
numExpectedPrefixLists = len(l["prefix_list_ids"].([]interface{}))
|
||||||
|
}
|
||||||
lsRaw, ok := l["security_groups"]
|
lsRaw, ok := l["security_groups"]
|
||||||
if ok {
|
if ok {
|
||||||
numExpectedSGs = len(l["security_groups"].(*schema.Set).List())
|
numExpectedSGs = len(l["security_groups"].(*schema.Set).List())
|
||||||
|
@ -677,6 +714,10 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface
|
||||||
if ok {
|
if ok {
|
||||||
numRemoteCidrs = len(r["cidr_blocks"].([]string))
|
numRemoteCidrs = len(r["cidr_blocks"].([]string))
|
||||||
}
|
}
|
||||||
|
rpRaw, ok := r["prefix_list_ids"]
|
||||||
|
if ok {
|
||||||
|
numRemotePrefixLists = len(r["prefix_list_ids"].([]string))
|
||||||
|
}
|
||||||
|
|
||||||
rsRaw, ok := r["security_groups"]
|
rsRaw, ok := r["security_groups"]
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -688,6 +729,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)
|
log.Printf("[DEBUG] Local rule has more CIDR blocks, continuing (%d/%d)", numExpectedCidrs, numRemoteCidrs)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if numExpectedPrefixLists > numRemotePrefixLists {
|
||||||
|
log.Printf("[DEBUG] Local rule has more prefix lists, continuing (%d/%d)", numExpectedPrefixLists, numRemotePrefixLists)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if numExpectedSGs > numRemoteSGs {
|
if numExpectedSGs > numRemoteSGs {
|
||||||
log.Printf("[DEBUG] Local rule has more Security Groups, continuing (%d/%d)", numExpectedSGs, numRemoteSGs)
|
log.Printf("[DEBUG] Local rule has more Security Groups, continuing (%d/%d)", numExpectedSGs, numRemoteSGs)
|
||||||
continue
|
continue
|
||||||
|
@ -721,6 +766,34 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// match prefix lists by converting both to sets, and using Set methods
|
||||||
|
var localPrefixLists []interface{}
|
||||||
|
if lpRaw != nil {
|
||||||
|
localPrefixLists = lpRaw.([]interface{})
|
||||||
|
}
|
||||||
|
localPrefixListsSet := schema.NewSet(schema.HashString, localPrefixLists)
|
||||||
|
|
||||||
|
// remote prefix lists are presented as a slice of strings, so we need to
|
||||||
|
// reformat them into a slice of interfaces to be used in creating the
|
||||||
|
// remote prefix list set
|
||||||
|
var remotePrefixLists []string
|
||||||
|
if rpRaw != nil {
|
||||||
|
remotePrefixLists = rpRaw.([]string)
|
||||||
|
}
|
||||||
|
// convert remote prefix lists to a set, for easy comparison
|
||||||
|
list = nil
|
||||||
|
for _, s := range remotePrefixLists {
|
||||||
|
list = append(list, s)
|
||||||
|
}
|
||||||
|
remotePrefixListsSet := schema.NewSet(schema.HashString, list)
|
||||||
|
|
||||||
|
// Build up a list of local prefix lists that are found in the remote set
|
||||||
|
for _, s := range localPrefixListsSet.List() {
|
||||||
|
if remotePrefixListsSet.Contains(s) {
|
||||||
|
matchingPrefixLists = append(matchingPrefixLists, s.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// match SGs. Both local and remote are already sets
|
// match SGs. Both local and remote are already sets
|
||||||
var localSGSet *schema.Set
|
var localSGSet *schema.Set
|
||||||
if lsRaw == nil {
|
if lsRaw == nil {
|
||||||
|
@ -748,41 +821,57 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface
|
||||||
// match, and then remove those elements from the remote rule, so that
|
// match, and then remove those elements from the remote rule, so that
|
||||||
// this remote rule can still be considered by other local rules
|
// this remote rule can still be considered by other local rules
|
||||||
if numExpectedCidrs == len(matchingCidrs) {
|
if numExpectedCidrs == len(matchingCidrs) {
|
||||||
if numExpectedSGs == len(matchingSGs) {
|
if numExpectedPrefixLists == len(matchingPrefixLists) {
|
||||||
// confirm that self references match
|
if numExpectedSGs == len(matchingSGs) {
|
||||||
var lSelf bool
|
// confirm that self references match
|
||||||
var rSelf bool
|
var lSelf bool
|
||||||
if _, ok := l["self"]; ok {
|
var rSelf bool
|
||||||
lSelf = l["self"].(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 _, ok := r["self"]; ok {
|
||||||
// reassigning
|
rSelf = r["self"].(bool)
|
||||||
if len(newCidr) > 0 {
|
|
||||||
r["cidr_blocks"] = newCidr
|
|
||||||
} else {
|
|
||||||
delete(r, "cidr_blocks")
|
|
||||||
}
|
}
|
||||||
|
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 sgs from remote
|
// reassigning
|
||||||
diffSGs := remoteSGSet.Difference(localSGSet)
|
if len(newCidr) > 0 {
|
||||||
if len(diffSGs.List()) > 0 {
|
r["cidr_blocks"] = newCidr
|
||||||
r["security_groups"] = diffSGs
|
} else {
|
||||||
} else {
|
delete(r, "cidr_blocks")
|
||||||
delete(r, "security_groups")
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
saves = append(saves, l)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -795,11 +884,13 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface
|
||||||
// matched locally, and let the graph sort things out. This will happen when
|
// matched locally, and let the graph sort things out. This will happen when
|
||||||
// rules are added externally to Terraform
|
// rules are added externally to Terraform
|
||||||
for _, r := range remote {
|
for _, r := range remote {
|
||||||
var lenCidr, lenSGs int
|
var lenCidr, lenPrefixLists, lenSGs int
|
||||||
if rCidrs, ok := r["cidr_blocks"]; ok {
|
if rCidrs, ok := r["cidr_blocks"]; ok {
|
||||||
lenCidr = len(rCidrs.([]string))
|
lenCidr = len(rCidrs.([]string))
|
||||||
}
|
}
|
||||||
|
if rPrefixLists, ok := r["prefix_list_ids"]; ok {
|
||||||
|
lenPrefixLists = len(rPrefixLists.([]string))
|
||||||
|
}
|
||||||
if rawSGs, ok := r["security_groups"]; ok {
|
if rawSGs, ok := r["security_groups"]; ok {
|
||||||
lenSGs = len(rawSGs.(*schema.Set).List())
|
lenSGs = len(rawSGs.(*schema.Set).List())
|
||||||
}
|
}
|
||||||
|
@ -810,7 +901,7 @@ func matchRules(rType string, local []interface{}, remote []map[string]interface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if lenSGs+lenCidr > 0 {
|
if lenSGs+lenCidr+lenPrefixLists > 0 {
|
||||||
log.Printf("[DEBUG] Found a remote Rule that wasn't empty: (%#v)", r)
|
log.Printf("[DEBUG] Found a remote Rule that wasn't empty: (%#v)", r)
|
||||||
saves = append(saves, r)
|
saves = append(saves, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,13 @@ func resourceAwsSecurityGroupRule() *schema.Resource {
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"prefix_list_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
"security_group_id": &schema.Schema{
|
"security_group_id": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
@ -363,6 +370,19 @@ func findRuleMatch(p *ec2.IpPermission, rules []*ec2.IpPermission, isVPC bool) *
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remaining = len(p.PrefixListIds)
|
||||||
|
for _, pl := range p.PrefixListIds {
|
||||||
|
for _, rpl := range r.PrefixListIds {
|
||||||
|
if *pl.PrefixListId == *rpl.PrefixListId {
|
||||||
|
remaining--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if remaining > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
remaining = len(p.UserIdGroupPairs)
|
remaining = len(p.UserIdGroupPairs)
|
||||||
for _, ip := range p.UserIdGroupPairs {
|
for _, ip := range p.UserIdGroupPairs {
|
||||||
for _, rip := range r.UserIdGroupPairs {
|
for _, rip := range r.UserIdGroupPairs {
|
||||||
|
@ -413,6 +433,18 @@ func ipPermissionIDHash(sg_id, ruleType string, ip *ec2.IpPermission) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(ip.PrefixListIds) > 0 {
|
||||||
|
s := make([]string, len(ip.PrefixListIds))
|
||||||
|
for i, pl := range ip.PrefixListIds {
|
||||||
|
s[i] = *pl.PrefixListId
|
||||||
|
}
|
||||||
|
sort.Strings(s)
|
||||||
|
|
||||||
|
for _, v := range s {
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(ip.UserIdGroupPairs) > 0 {
|
if len(ip.UserIdGroupPairs) > 0 {
|
||||||
sort.Sort(ByGroupPair(ip.UserIdGroupPairs))
|
sort.Sort(ByGroupPair(ip.UserIdGroupPairs))
|
||||||
for _, pair := range ip.UserIdGroupPairs {
|
for _, pair := range ip.UserIdGroupPairs {
|
||||||
|
@ -494,6 +526,18 @@ func expandIPPerm(d *schema.ResourceData, sg *ec2.SecurityGroup) (*ec2.IpPermiss
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if raw, ok := d.GetOk("prefix_list_ids"); ok {
|
||||||
|
list := raw.([]interface{})
|
||||||
|
perm.PrefixListIds = make([]*ec2.PrefixListId, len(list))
|
||||||
|
for i, v := range list {
|
||||||
|
prefixListID, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("empty element found in prefix_list_ids - consider using the compact function")
|
||||||
|
}
|
||||||
|
perm.PrefixListIds[i] = &ec2.PrefixListId{PrefixListId: aws.String(prefixListID)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &perm, nil
|
return &perm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,6 +558,13 @@ func setFromIPPerm(d *schema.ResourceData, sg *ec2.SecurityGroup, rule *ec2.IpPe
|
||||||
// 'self' is false by default. Below, we range over the group ids and set true
|
// 'self' is false by default. Below, we range over the group ids and set true
|
||||||
// if the parent sg id is found
|
// if the parent sg id is found
|
||||||
d.Set("self", false)
|
d.Set("self", false)
|
||||||
|
|
||||||
|
var pl []string
|
||||||
|
for _, p := range rule.PrefixListIds {
|
||||||
|
pl = append(pl, *p.PrefixListId)
|
||||||
|
}
|
||||||
|
d.Set("prefix_list_ids", pl)
|
||||||
|
|
||||||
if len(rule.UserIdGroupPairs) > 0 {
|
if len(rule.UserIdGroupPairs) > 0 {
|
||||||
s := rule.UserIdGroupPairs[0]
|
s := rule.UserIdGroupPairs[0]
|
||||||
|
|
||||||
|
|
|
@ -416,6 +416,65 @@ func TestAccAWSSecurityGroupRule_Race(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSSecurityGroupRule_PrefixListEgress(t *testing.T) {
|
||||||
|
var group ec2.SecurityGroup
|
||||||
|
var endpoint ec2.VpcEndpoint
|
||||||
|
var p ec2.IpPermission
|
||||||
|
|
||||||
|
// This function creates the expected IPPermission with the prefix list ID from
|
||||||
|
// the VPC Endpoint created in the test
|
||||||
|
setupSG := func(*terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||||
|
prefixListInput := &ec2.DescribePrefixListsInput{
|
||||||
|
Filters: []*ec2.Filter{
|
||||||
|
{Name: aws.String("prefix-list-name"), Values: []*string{endpoint.ServiceName}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Reading VPC Endpoint prefix list: %s", prefixListInput)
|
||||||
|
prefixListsOutput, err := conn.DescribePrefixLists(prefixListInput)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
_, ok := err.(awserr.Error)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Error reading VPC Endpoint prefix list: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(prefixListsOutput.PrefixLists) != 1 {
|
||||||
|
return fmt.Errorf("There are multiple prefix lists associated with the service name '%s'. Unexpected", prefixListsOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
p = ec2.IpPermission{
|
||||||
|
IpProtocol: aws.String("-1"),
|
||||||
|
PrefixListIds: []*ec2.PrefixListId{
|
||||||
|
&ec2.PrefixListId{PrefixListId: prefixListsOutput.PrefixLists[0].PrefixListId},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSSecurityGroupRulePrefixListEgressConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSSecurityGroupRuleExists("aws_security_group.egress", &group),
|
||||||
|
// lookup info on the VPC Endpoint created, to populate the expected
|
||||||
|
// IP Perm
|
||||||
|
testAccCheckVpcEndpointExists("aws_vpc_endpoint.s3-us-west-2", &endpoint),
|
||||||
|
setupSG,
|
||||||
|
testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.egress_1", &group, &p, "egress"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckAWSSecurityGroupRuleDestroy(s *terraform.State) error {
|
func testAccCheckAWSSecurityGroupRuleDestroy(s *terraform.State) error {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
@ -549,6 +608,20 @@ func testAccCheckAWSSecurityGroupRuleAttributes(n string, group *ec2.SecurityGro
|
||||||
if remaining > 0 {
|
if remaining > 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remaining = len(p.PrefixListIds)
|
||||||
|
for _, pip := range p.PrefixListIds {
|
||||||
|
for _, rpip := range r.PrefixListIds {
|
||||||
|
if *pip.PrefixListId == *rpip.PrefixListId {
|
||||||
|
remaining--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if remaining > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
matchingRule = r
|
matchingRule = r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,3 +952,52 @@ var testAccAWSSecurityGroupRuleRace = func() string {
|
||||||
}
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
const testAccAWSSecurityGroupRulePrefixListEgressConfig = `
|
||||||
|
|
||||||
|
resource "aws_vpc" "tf_sg_prefix_list_egress_test" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf_sg_prefix_list_egress_test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table" "default" {
|
||||||
|
vpc_id = "${aws_vpc.tf_sg_prefix_list_egress_test.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc_endpoint" "s3-us-west-2" {
|
||||||
|
vpc_id = "${aws_vpc.tf_sg_prefix_list_egress_test.id}"
|
||||||
|
service_name = "com.amazonaws.us-west-2.s3"
|
||||||
|
route_table_ids = ["${aws_route_table.default.id}"]
|
||||||
|
policy = <<POLICY
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid":"AllowAll",
|
||||||
|
"Effect":"Allow",
|
||||||
|
"Principal":"*",
|
||||||
|
"Action":"*",
|
||||||
|
"Resource":"*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
POLICY
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "egress" {
|
||||||
|
name = "terraform_acceptance_test_prefix_list_egress"
|
||||||
|
description = "Used in the terraform acceptance tests"
|
||||||
|
vpc_id = "${aws_vpc.tf_sg_prefix_list_egress_test.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group_rule" "egress_1" {
|
||||||
|
type = "egress"
|
||||||
|
protocol = "-1"
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
prefix_list_ids = ["${aws_vpc_endpoint.s3-us-west-2.prefix_list_id}"]
|
||||||
|
security_group_id = "${aws_security_group.egress.id}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
@ -174,6 +174,18 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
&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")}},
|
||||||
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
||||||
|
// VPC
|
||||||
|
&ec2.UserIdGroupPair{
|
||||||
|
GroupId: aws.String("sg-22222"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local := []map[string]interface{}{
|
local := []map[string]interface{}{
|
||||||
|
@ -201,6 +213,15 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) {
|
||||||
"amazon-elb/amazon-elb-sg",
|
"amazon-elb/amazon-elb-sg",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"protocol": "-1",
|
||||||
|
"from_port": int64(0),
|
||||||
|
"to_port": int64(0),
|
||||||
|
"prefix_list_ids": []string{"pl-12345678"},
|
||||||
|
"security_groups": schema.NewSet(schema.HashString, []interface{}{
|
||||||
|
"sg-22222",
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
out := resourceAwsSecurityGroupIPPermGather("sg-11111", raw, aws.String("12345"))
|
out := resourceAwsSecurityGroupIPPermGather("sg-11111", raw, aws.String("12345"))
|
||||||
|
@ -504,7 +525,7 @@ func TestAccAWSSecurityGroup_generatedName(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccAWSSecurityGroup_DefaultEgress(t *testing.T) {
|
func TestAccAWSSecurityGroup_DefaultEgress_VPC(t *testing.T) {
|
||||||
|
|
||||||
// VPC
|
// VPC
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
|
@ -521,6 +542,9 @@ func TestAccAWSSecurityGroup_DefaultEgress(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSSecurityGroup_DefaultEgress_Classic(t *testing.T) {
|
||||||
|
|
||||||
// Classic
|
// Classic
|
||||||
var group ec2.SecurityGroup
|
var group ec2.SecurityGroup
|
||||||
|
@ -843,6 +867,27 @@ func TestAccAWSSecurityGroup_ingressWithCidrAndSGs_classic(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSSecurityGroup_egressWithPrefixList(t *testing.T) {
|
||||||
|
var group ec2.SecurityGroup
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSSecurityGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSSecurityGroupConfigPrefixListEgress,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSSecurityGroupExists("aws_security_group.egress", &group),
|
||||||
|
testAccCheckAWSSecurityGroupPrefixListAttributes(&group),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_security_group.egress", "egress.#", "1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckAWSSecurityGroupSGandCidrAttributes(group *ec2.SecurityGroup) resource.TestCheckFunc {
|
func testAccCheckAWSSecurityGroupSGandCidrAttributes(group *ec2.SecurityGroup) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
if *group.GroupName != "terraform_acceptance_test_example" {
|
if *group.GroupName != "terraform_acceptance_test_example" {
|
||||||
|
@ -880,6 +925,31 @@ func testAccCheckAWSSecurityGroupSGandCidrAttributes(group *ec2.SecurityGroup) r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSSecurityGroupPrefixListAttributes(group *ec2.SecurityGroup) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if *group.GroupName != "terraform_acceptance_test_prefix_list_egress" {
|
||||||
|
return fmt.Errorf("Bad name: %s", *group.GroupName)
|
||||||
|
}
|
||||||
|
if *group.Description != "Used in the terraform acceptance tests" {
|
||||||
|
return fmt.Errorf("Bad description: %s", *group.Description)
|
||||||
|
}
|
||||||
|
if len(group.IpPermissionsEgress) == 0 {
|
||||||
|
return fmt.Errorf("No egress IPPerms")
|
||||||
|
}
|
||||||
|
if len(group.IpPermissionsEgress) != 1 {
|
||||||
|
return fmt.Errorf("Expected 1 egress rule, got %d", len(group.IpPermissions))
|
||||||
|
}
|
||||||
|
|
||||||
|
p := group.IpPermissionsEgress[0]
|
||||||
|
|
||||||
|
if len(p.PrefixListIds) != 1 {
|
||||||
|
return fmt.Errorf("Expected 1 prefix list, got %d", len(p.PrefixListIds))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckAWSSecurityGroupAttributesChanged(group *ec2.SecurityGroup) resource.TestCheckFunc {
|
func testAccCheckAWSSecurityGroupAttributesChanged(group *ec2.SecurityGroup) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
p := []*ec2.IpPermission{
|
p := []*ec2.IpPermission{
|
||||||
|
@ -1629,3 +1699,49 @@ resource "aws_security_group_rule" "allow_all-1" {
|
||||||
security_group_id = "${aws_security_group.allow_all.id}"
|
security_group_id = "${aws_security_group.allow_all.id}"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testAccAWSSecurityGroupConfigPrefixListEgress = `
|
||||||
|
resource "aws_vpc" "tf_sg_prefix_list_egress_test" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf_sg_prefix_list_egress_test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table" "default" {
|
||||||
|
vpc_id = "${aws_vpc.tf_sg_prefix_list_egress_test.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc_endpoint" "s3-us-west-2" {
|
||||||
|
vpc_id = "${aws_vpc.tf_sg_prefix_list_egress_test.id}"
|
||||||
|
service_name = "com.amazonaws.us-west-2.s3"
|
||||||
|
route_table_ids = ["${aws_route_table.default.id}"]
|
||||||
|
policy = <<POLICY
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid":"AllowAll",
|
||||||
|
"Effect":"Allow",
|
||||||
|
"Principal":"*",
|
||||||
|
"Action":"*",
|
||||||
|
"Resource":"*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
POLICY
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "egress" {
|
||||||
|
name = "terraform_acceptance_test_prefix_list_egress"
|
||||||
|
description = "Used in the terraform acceptance tests"
|
||||||
|
vpc_id = "${aws_vpc.tf_sg_prefix_list_egress_test.id}"
|
||||||
|
|
||||||
|
egress {
|
||||||
|
protocol = "-1"
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
prefix_list_ids = ["${aws_vpc_endpoint.s3-us-west-2.prefix_list_id}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
@ -43,6 +43,10 @@ func resourceAwsVpcEndpoint() *schema.Resource {
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
Set: schema.HashString,
|
Set: schema.HashString,
|
||||||
},
|
},
|
||||||
|
"prefix_list_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,12 +105,36 @@ func resourceAwsVPCEndpointRead(d *schema.ResourceData, meta interface{}) error
|
||||||
|
|
||||||
vpce := output.VpcEndpoints[0]
|
vpce := output.VpcEndpoints[0]
|
||||||
|
|
||||||
|
// A VPC Endpoint is associated with exactly one prefix list name (also called Service Name).
|
||||||
|
// The prefix list ID can be used in security groups, so retrieve it to support that capability.
|
||||||
|
prefixListServiceName := *vpce.ServiceName
|
||||||
|
prefixListInput := &ec2.DescribePrefixListsInput{
|
||||||
|
Filters: []*ec2.Filter{
|
||||||
|
{Name: aws.String("prefix-list-name"), Values: []*string{aws.String(prefixListServiceName)}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Reading VPC Endpoint prefix list: %s", prefixListServiceName)
|
||||||
|
prefixListsOutput, err := conn.DescribePrefixLists(prefixListInput)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
_, ok := err.(awserr.Error)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Error reading VPC Endpoint prefix list: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(prefixListsOutput.PrefixLists) != 1 {
|
||||||
|
return fmt.Errorf("There are multiple prefix lists associated with the service name '%s'. Unexpected", prefixListServiceName)
|
||||||
|
}
|
||||||
|
|
||||||
d.Set("vpc_id", vpce.VpcId)
|
d.Set("vpc_id", vpce.VpcId)
|
||||||
d.Set("policy", normalizeJson(*vpce.PolicyDocument))
|
d.Set("policy", normalizeJson(*vpce.PolicyDocument))
|
||||||
d.Set("service_name", vpce.ServiceName)
|
d.Set("service_name", vpce.ServiceName)
|
||||||
if err := d.Set("route_table_ids", aws.StringValueSlice(vpce.RouteTableIds)); err != nil {
|
if err := d.Set("route_table_ids", aws.StringValueSlice(vpce.RouteTableIds)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
d.Set("prefix_list_id", prefixListsOutput.PrefixLists[0].PrefixListId)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
@ -25,6 +26,7 @@ func TestAccAWSVpcEndpoint_basic(t *testing.T) {
|
||||||
Config: testAccVpcEndpointWithRouteTableAndPolicyConfig,
|
Config: testAccVpcEndpointWithRouteTableAndPolicyConfig,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckVpcEndpointExists("aws_vpc_endpoint.second-private-s3", &endpoint),
|
testAccCheckVpcEndpointExists("aws_vpc_endpoint.second-private-s3", &endpoint),
|
||||||
|
testAccCheckVpcEndpointPrefixListAvailable("aws_vpc_endpoint.second-private-s3"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -118,6 +120,25 @@ func testAccCheckVpcEndpointExists(n string, endpoint *ec2.VpcEndpoint) resource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccCheckVpcEndpointPrefixListAvailable(n string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixListID := rs.Primary.Attributes["prefix_list_id"]
|
||||||
|
if prefixListID == "" {
|
||||||
|
return fmt.Errorf("Prefix list ID not available")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(prefixListID, "pl") {
|
||||||
|
return fmt.Errorf("Prefix list ID does not appear to be a valid value: '%s'", prefixListID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const testAccVpcEndpointWithRouteTableAndPolicyConfig = `
|
const testAccVpcEndpointWithRouteTableAndPolicyConfig = `
|
||||||
resource "aws_vpc" "foo" {
|
resource "aws_vpc" "foo" {
|
||||||
cidr_block = "10.0.0.0/16"
|
cidr_block = "10.0.0.0/16"
|
||||||
|
|
|
@ -208,6 +208,13 @@ func expandIPPerms(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if raw, ok := m["prefix_list_ids"]; ok {
|
||||||
|
list := raw.([]interface{})
|
||||||
|
for _, v := range list {
|
||||||
|
perm.PrefixListIds = append(perm.PrefixListIds, &ec2.PrefixListId{PrefixListId: aws.String(v.(string))})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
perms[i] = &perm
|
perms[i] = &perm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ resource "aws_security_group" "allow_all" {
|
||||||
to_port = 0
|
to_port = 0
|
||||||
protocol = "-1"
|
protocol = "-1"
|
||||||
cidr_blocks = ["0.0.0.0/0"]
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
prefix_list_ids = ["pl-12c4e678"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -96,6 +97,7 @@ The `ingress` block supports:
|
||||||
The `egress` block supports:
|
The `egress` block supports:
|
||||||
|
|
||||||
* `cidr_blocks` - (Optional) List of CIDR blocks.
|
* `cidr_blocks` - (Optional) List of 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")
|
* `from_port` - (Required) The start port (or ICMP type number if protocol is "icmp")
|
||||||
* `protocol` - (Required) The protocol. If you select a protocol of
|
* `protocol` - (Required) The protocol. If you select a protocol of
|
||||||
"-1", you must specify a "from_port" and "to_port" equal to 0.
|
"-1", you must specify a "from_port" and "to_port" equal to 0.
|
||||||
|
@ -119,6 +121,26 @@ be in place, you can use this `egress` block:
|
||||||
cidr_blocks = ["0.0.0.0/0"]
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## Usage with prefix list IDs
|
||||||
|
|
||||||
|
Prefix list IDs are manged by AWS internally. Prefix list IDs
|
||||||
|
are associated with a prefix list name, or service name, that is linked to a specific region.
|
||||||
|
Prefix list IDs are exported on VPC Endpoints, so you can use this format:
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
egress {
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
prefix_list_ids = ["${aws_vpc_endpoint.my_endpoint.prefix_list_id}"]
|
||||||
|
}
|
||||||
|
...
|
||||||
|
resource "aws_vpc_endpoint" "my_endpoint" {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Attributes Reference
|
## Attributes Reference
|
||||||
|
|
||||||
The following attributes are exported:
|
The following attributes are exported:
|
||||||
|
|
|
@ -29,6 +29,7 @@ resource "aws_security_group_rule" "allow_all" {
|
||||||
to_port = 65535
|
to_port = 65535
|
||||||
protocol = "tcp"
|
protocol = "tcp"
|
||||||
cidr_blocks = ["0.0.0.0/0"]
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
prefix_list_ids = ["pl-12c4e678"]
|
||||||
|
|
||||||
security_group_id = "sg-123456"
|
security_group_id = "sg-123456"
|
||||||
}
|
}
|
||||||
|
@ -41,6 +42,8 @@ The following arguments are supported:
|
||||||
* `type` - (Required) The type of rule being created. Valid options are `ingress` (inbound)
|
* `type` - (Required) The type of rule being created. Valid options are `ingress` (inbound)
|
||||||
or `egress` (outbound).
|
or `egress` (outbound).
|
||||||
* `cidr_blocks` - (Optional) List of CIDR blocks. Cannot be specified with `source_security_group_id`.
|
* `cidr_blocks` - (Optional) List of CIDR blocks. Cannot be specified with `source_security_group_id`.
|
||||||
|
* `prefix_list_ids` - (Optional) List of prefix list IDs (for allowing access to VPC endpoints).
|
||||||
|
Only valid with `egress`.
|
||||||
* `from_port` - (Required) The start port (or ICMP type number if protocol is "icmp").
|
* `from_port` - (Required) The start port (or ICMP type number if protocol is "icmp").
|
||||||
* `protocol` - (Required) The protocol.
|
* `protocol` - (Required) The protocol.
|
||||||
* `security_group_id` - (Required) The security group to apply this rule to.
|
* `security_group_id` - (Required) The security group to apply this rule to.
|
||||||
|
@ -50,6 +53,27 @@ or `egress` (outbound).
|
||||||
a source to this ingress rule.
|
a source to this ingress rule.
|
||||||
* `to_port` - (Required) The end range port.
|
* `to_port` - (Required) The end range port.
|
||||||
|
|
||||||
|
## Usage with prefix list IDs
|
||||||
|
|
||||||
|
Prefix list IDs are manged by AWS internally. Prefix list IDs
|
||||||
|
are associated with a prefix list name, or service name, that is linked to a specific region.
|
||||||
|
Prefix list IDs are exported on VPC Endpoints, so you can use this format:
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "aws_security_group_rule" "allow_all" {
|
||||||
|
type = "egress"
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
prefix_list_ids = ["${aws_vpc_endpoint.my_endpoint.prefix_list_id}"]
|
||||||
|
from_port = 0
|
||||||
|
security_group_id = "sg-123456"
|
||||||
|
}
|
||||||
|
...
|
||||||
|
resource "aws_vpc_endpoint" "my_endpoint" {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Attributes Reference
|
## Attributes Reference
|
||||||
|
|
||||||
The following attributes are exported:
|
The following attributes are exported:
|
||||||
|
|
|
@ -35,3 +35,4 @@ The following arguments are supported:
|
||||||
The following attributes are exported:
|
The following attributes are exported:
|
||||||
|
|
||||||
* `id` - The ID of the VPC endpoint.
|
* `id` - The ID of the VPC endpoint.
|
||||||
|
* `prefix_list_id` - The prefix list ID of the exposed service.
|
||||||
|
|
Loading…
Reference in New Issue