Skip to content

Commit

Permalink
Merge pull request #37226 from ddericco/f-vpc_endpoint_user_defined_ip
Browse files Browse the repository at this point in the history
r/aws_vpc_endpoint: Add support for user-defined IP addresses
  • Loading branch information
ewbankkit authored Jun 13, 2024
2 parents e912262 + cce73a9 commit 9c6e723
Show file tree
Hide file tree
Showing 4 changed files with 546 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .changelog/37226.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_vpc_endpoint: Add `subnet_configuration` argument to support user defined IP addresses
```
154 changes: 153 additions & 1 deletion internal/service/ec2/vpc_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,28 @@ func resourceVPCEndpoint() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"subnet_configuration": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"ipv4": {
Type: schema.TypeString,
Optional: true,
},
"ipv6": {
Type: schema.TypeString,
Optional: true,
},
// subnet_id in subnet_configuration must have a corresponding subnet in subnet_ids attribute
names.AttrSubnetID: {
Type: schema.TypeString,
Optional: true,
},
},
},
},
names.AttrSubnetIDs: {
Type: schema.TypeSet,
Optional: true,
Expand Down Expand Up @@ -242,6 +264,10 @@ func resourceVPCEndpointCreate(ctx context.Context, d *schema.ResourceData, meta
input.SecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set))
}

if v, ok := d.GetOk("subnet_configuration"); ok && v.(*schema.Set).Len() > 0 {
input.SubnetConfigurations = expandSubnetConfigurations(v.(*schema.Set).List())
}

if v, ok := d.GetOk(names.AttrSubnetIDs); ok && v.(*schema.Set).Len() > 0 {
input.SubnetIds = flex.ExpandStringValueSet(v.(*schema.Set))
}
Expand Down Expand Up @@ -352,6 +378,16 @@ func resourceVPCEndpointRead(ctx context.Context, d *schema.ResourceData, meta i
d.Set("prefix_list_id", pl.PrefixListId)
}

subnetConfigurations, err := findSubnetConfigurationsByNetworkInterfaceIDs(ctx, conn, vpce.NetworkInterfaceIds)

if err != nil {
return sdkdiag.AppendErrorf(diags, "reading VPC Endpoint (%s) subnet configurations: %s", d.Id(), err)
}

if err := d.Set("subnet_configuration", flattenSubnetConfigurations(subnetConfigurations)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting subnet_configuration: %s", err)
}

policyToSet, err := verify.SecondJSONUnlessEquivalent(d.Get(names.AttrPolicy).(string), aws.ToString(vpce.PolicyDocument))

if err != nil {
Expand Down Expand Up @@ -381,7 +417,7 @@ func resourceVPCEndpointUpdate(ctx context.Context, d *schema.ResourceData, meta
}
}

if d.HasChanges("dns_options", names.AttrIPAddressType, names.AttrPolicy, "private_dns_enabled", names.AttrSecurityGroupIDs, "route_table_ids", names.AttrSubnetIDs) {
if d.HasChanges("dns_options", names.AttrIPAddressType, names.AttrPolicy, "private_dns_enabled", names.AttrSecurityGroupIDs, "route_table_ids", "subnet_configuration", names.AttrSubnetIDs) {
input := &ec2.ModifyVpcEndpointInput{
VpcEndpointId: aws.String(d.Id()),
}
Expand Down Expand Up @@ -430,6 +466,12 @@ func resourceVPCEndpointUpdate(ctx context.Context, d *schema.ResourceData, meta
}
}

if d.HasChange("subnet_configuration") {
if v, ok := d.GetOk("subnet_configuration"); ok && v.(*schema.Set).Len() > 0 {
input.SubnetConfigurations = expandSubnetConfigurations(v.(*schema.Set).List())
}
}

_, err := conn.ModifyVpcEndpoint(ctx, input)

if err != nil {
Expand Down Expand Up @@ -497,6 +539,32 @@ func vpcEndpointAccept(ctx context.Context, conn *ec2.Client, vpceID, serviceNam
return nil
}

type subnetConfiguration struct {
ipv4 *string
ipv6 *string
subnetID *string
}

func findSubnetConfigurationsByNetworkInterfaceIDs(ctx context.Context, conn *ec2.Client, networkInterfaceIDs []string) ([]subnetConfiguration, error) {
var output []subnetConfiguration

for _, v := range networkInterfaceIDs {
networkInterface, err := findNetworkInterfaceByID(ctx, conn, v)

if err != nil {
return nil, err
}

output = append(output, subnetConfiguration{
ipv4: networkInterface.PrivateIpAddress,
ipv6: networkInterface.Ipv6Address,
subnetID: networkInterface.SubnetId,
})
}

return output, nil
}

func isAmazonS3VPCEndpoint(serviceName string) bool {
ok, _ := regexp.MatchString("com\\.amazonaws\\.([a-z]+\\-[a-z]+\\-[0-9])\\.s3", serviceName)
return ok
Expand Down Expand Up @@ -534,6 +602,54 @@ func expandDNSOptionsSpecificationWithPrivateDNSOnly(tfMap map[string]interface{
return apiObject
}

func expandSubnetConfiguration(tfMap map[string]interface{}) *awstypes.SubnetConfiguration {
if tfMap == nil {
return nil
}

apiObject := &awstypes.SubnetConfiguration{}

if v, ok := tfMap["ipv4"].(string); ok && v != "" {
apiObject.Ipv4 = aws.String(v)
}

if v, ok := tfMap["ipv6"].(string); ok && v != "" {
apiObject.Ipv6 = aws.String(v)
}

if v, ok := tfMap[names.AttrSubnetID].(string); ok && v != "" {
apiObject.SubnetId = aws.String(v)
}

return apiObject
}

func expandSubnetConfigurations(tfList []interface{}) []awstypes.SubnetConfiguration {
if len(tfList) == 0 {
return nil
}

var apiObjects []awstypes.SubnetConfiguration

for _, tfMapRaw := range tfList {
tfMap, ok := tfMapRaw.(map[string]interface{})

if !ok {
continue
}

apiObject := expandSubnetConfiguration(tfMap)

if apiObject == nil {
continue
}

apiObjects = append(apiObjects, *apiObject)
}

return apiObjects
}

func flattenDNSEntry(apiObject *awstypes.DnsEntry) map[string]interface{} {
if apiObject == nil {
return nil
Expand Down Expand Up @@ -617,3 +733,39 @@ func flattenAddAndRemoveStringValueLists(d *schema.ResourceData, key string) ([]

return add, del
}

func flattenSubnetConfiguration(apiObject *subnetConfiguration) map[string]interface{} {
if apiObject == nil {
return nil
}

tfMap := map[string]interface{}{}

if v := apiObject.ipv4; v != nil {
tfMap["ipv4"] = aws.ToString(v)
}

if v := apiObject.ipv6; v != nil {
tfMap["ipv6"] = aws.ToString(v)
}

if v := apiObject.subnetID; v != nil {
tfMap[names.AttrSubnetID] = aws.ToString(v)
}

return tfMap
}

func flattenSubnetConfigurations(apiObjects []subnetConfiguration) []interface{} {
if len(apiObjects) == 0 {
return nil
}

var tfList []interface{}

for _, apiObject := range apiObjects {
tfList = append(tfList, flattenSubnetConfiguration(&apiObject))
}

return tfList
}
Loading

0 comments on commit 9c6e723

Please sign in to comment.