diff --git a/aws/resource_aws_api_gateway_rest_api.go b/aws/resource_aws_api_gateway_rest_api.go index 71923f71da50..1953213f3f87 100644 --- a/aws/resource_aws_api_gateway_rest_api.go +++ b/aws/resource_aws_api_gateway_rest_api.go @@ -107,6 +107,14 @@ func resourceAwsApiGatewayRestApi() *schema.Resource { }, false), }, }, + "vpc_endpoint_ids": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, }, }, }, @@ -361,6 +369,30 @@ func resourceAwsApiGatewayRestApiUpdateOperations(d *schema.ResourceData) []*api } } + if d.HasChange("endpoint_configuration.0.vpc_endpoint_ids") { + o, n := d.GetChange("endpoint_configuration.0.vpc_endpoint_ids") + prefix := "/endpointConfiguration/vpcEndpointIds" + + old := o.([]interface{}) + new := n.([]interface{}) + + for _, v := range old { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("remove"), + Path: aws.String(prefix), + Value: aws.String(v.(string)), + }) + } + + for _, v := range new { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("add"), + Path: aws.String(prefix), + Value: aws.String(v.(string)), + }) + } + } + return operations } @@ -434,6 +466,10 @@ func expandApiGatewayEndpointConfiguration(l []interface{}) *apigateway.Endpoint Types: expandStringList(m["types"].([]interface{})), } + if endpointIds, ok := m["vpc_endpoint_ids"]; ok { + endpointConfiguration.VpcEndpointIds = expandStringList(endpointIds.([]interface{})) + } + return endpointConfiguration } @@ -446,5 +482,9 @@ func flattenApiGatewayEndpointConfiguration(endpointConfiguration *apigateway.En "types": flattenStringList(endpointConfiguration.Types), } + if len(endpointConfiguration.VpcEndpointIds) > 0 { + m["vpc_endpoint_ids"] = flattenStringList(endpointConfiguration.VpcEndpointIds) + } + return []interface{}{m} } diff --git a/aws/resource_aws_api_gateway_rest_api_test.go b/aws/resource_aws_api_gateway_rest_api_test.go index 4ea59bbcdc07..8dee784a47b8 100644 --- a/aws/resource_aws_api_gateway_rest_api_test.go +++ b/aws/resource_aws_api_gateway_rest_api_test.go @@ -322,6 +322,60 @@ func TestAccAWSAPIGatewayRestApi_EndpointConfiguration_Private(t *testing.T) { }) } +func TestAccAWSAPIGatewayRestApi_EndpointConfiguration_VPCEndpoint(t *testing.T) { + var restApi apigateway.RestApi + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_api_gateway_rest_api.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayRestAPIDestroy, + Steps: []resource.TestStep{ + { + PreConfig: func() { + // Ensure region supports PRIVATE endpoint + // This can eventually be moved to a PreCheck function + conn := testAccProvider.Meta().(*AWSClient).apigateway + output, err := conn.CreateRestApi(&apigateway.CreateRestApiInput{ + Name: aws.String(acctest.RandomWithPrefix("tf-acc-test-private-endpoint-precheck")), + EndpointConfiguration: &apigateway.EndpointConfiguration{ + Types: []*string{aws.String("PRIVATE")}, + }, + }) + if err != nil { + if isAWSErr(err, apigateway.ErrCodeBadRequestException, "Endpoint Configuration type PRIVATE is not supported in this region") { + t.Skip("Region does not support PRIVATE endpoint type") + } + t.Fatal(err) + } + + // Be kind and rewind. :) + _, err = conn.DeleteRestApi(&apigateway.DeleteRestApiInput{ + RestApiId: output.Id, + }) + if err != nil { + t.Fatal(err) + } + }, + Config: testAccAWSAPIGatewayRestAPIConfig_VPCEndpointConfiguration(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayRestAPIExists(resourceName, &restApi), + resource.TestCheckResourceAttr(resourceName, "endpoint_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_configuration.0.types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_configuration.0.types.0", "PRIVATE"), + resource.TestCheckResourceAttr(resourceName, "endpoint_configuration.0.vpc_endpoint_ids.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSAPIGatewayRestApi_api_key_source(t *testing.T) { expectedAPIKeySource := "HEADER" expectedUpdateAPIKeySource := "AUTHORIZER" @@ -609,6 +663,63 @@ resource "aws_api_gateway_rest_api" "test" { `, rName) } +func testAccAWSAPIGatewayRestAPIConfig_VPCEndpointConfiguration(rName string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "11.0.0.0/16" + enable_dns_support = true + enable_dns_hostnames = true + + tags = { + Name = %[1]q + } +} + +data "aws_security_group" "test" { + vpc_id = "${aws_vpc.test.id}" + name = "default" +} + +data "aws_availability_zones" "available" {} + +resource "aws_subnet" "test" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "${aws_vpc.test.cidr_block}" + availability_zone = "${data.aws_availability_zones.available.names[0]}" + + tags = { + Name = %[1]q + } +} + +data "aws_region" "current" {} + +resource "aws_vpc_endpoint" "test" { + vpc_id = "${aws_vpc.test.id}" + service_name = "com.amazonaws.${data.aws_region.current.name}.execute-api" + vpc_endpoint_type = "Interface" + private_dns_enabled = false + + subnet_ids = [ + "${aws_subnet.test.id}", + ] + + security_group_ids = [ + "${data.aws_security_group.test.id}", + ] +} + +resource "aws_api_gateway_rest_api" "test" { + name = %[1]q + + endpoint_configuration { + types = ["PRIVATE"] + vpc_endpoint_ids = ["${aws_vpc_endpoint.test.id}"] + } +} +`, rName) +} + func testAccAWSAPIGatewayRestAPIConfigTags1(rName, tagKey1, tagValue1 string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { diff --git a/website/docs/r/api_gateway_rest_api.html.markdown b/website/docs/r/api_gateway_rest_api.html.markdown index b46c7eca6dc4..4ac9e3be50df 100644 --- a/website/docs/r/api_gateway_rest_api.html.markdown +++ b/website/docs/r/api_gateway_rest_api.html.markdown @@ -61,6 +61,7 @@ __Note__: If the `body` argument is provided, the OpenAPI specification will be ### endpoint_configuration * `types` - (Required) A list of endpoint types. This resource currently only supports managing a single value. Valid values: `EDGE`, `REGIONAL` or `PRIVATE`. If unspecified, defaults to `EDGE`. Must be declared as `REGIONAL` in non-Commercial partitions. Refer to the [documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/create-regional-api.html) for more information on the difference between edge-optimized and regional APIs. +* `vpc_endpoint_ids` - (Optional) A list of VPC Endpoint Ids. It is only supported for PRIVATE endpoint type. ## Attributes Reference