diff --git a/aws/resource_aws_apigatewayv2_integration.go b/aws/resource_aws_apigatewayv2_integration.go index c7c172108c2..c33a80b04ee 100644 --- a/aws/resource_aws_apigatewayv2_integration.go +++ b/aws/resource_aws_apigatewayv2_integration.go @@ -93,11 +93,10 @@ func resourceAwsApiGatewayV2Integration() *schema.Resource { apigatewayv2.PassthroughBehaviorWhenNoTemplates, }, false), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - // PassthroughBehavior not set for HTTP APIs + // Not set for HTTP APIs. if old == "" && new == apigatewayv2.PassthroughBehaviorWhenNoMatch { return true } - return false }, }, @@ -125,6 +124,20 @@ func resourceAwsApiGatewayV2Integration() *schema.Resource { Default: 29000, ValidateFunc: validation.IntBetween(50, 29000), }, + "tls_config": { + Type: schema.TypeList, + Optional: true, + MinItems: 0, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "server_name_to_verify": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, }, } } @@ -172,6 +185,9 @@ func resourceAwsApiGatewayV2IntegrationCreate(d *schema.ResourceData, meta inter if v, ok := d.GetOk("timeout_milliseconds"); ok { req.TimeoutInMillis = aws.Int64(int64(v.(int))) } + if v, ok := d.GetOk("tls_config"); ok { + req.TlsConfig = expandApiGateway2TlsConfig(v.([]interface{})) + } log.Printf("[DEBUG] Creating API Gateway v2 integration: %s", req) resp, err := conn.CreateIntegration(req) @@ -217,6 +233,9 @@ func resourceAwsApiGatewayV2IntegrationRead(d *schema.ResourceData, meta interfa } d.Set("template_selection_expression", resp.TemplateSelectionExpression) d.Set("timeout_milliseconds", resp.TimeoutInMillis) + if err := d.Set("tls_config", flattenApiGateway2TlsConfig(resp.TlsConfig)); err != nil { + return fmt.Errorf("error setting tls_config: %s", err) + } return nil } @@ -264,6 +283,9 @@ func resourceAwsApiGatewayV2IntegrationUpdate(d *schema.ResourceData, meta inter if d.HasChange("timeout_milliseconds") { req.TimeoutInMillis = aws.Int64(int64(d.Get("timeout_milliseconds").(int))) } + if d.HasChange("tls_config") { + req.TlsConfig = expandApiGateway2TlsConfig(d.Get("tls_config").([]interface{})) + } log.Printf("[DEBUG] Updating API Gateway v2 integration: %s", req) _, err := conn.UpdateIntegration(req) @@ -320,3 +342,28 @@ func resourceAwsApiGatewayV2IntegrationImport(d *schema.ResourceData, meta inter return []*schema.ResourceData{d}, nil } + +func expandApiGateway2TlsConfig(vConfig []interface{}) *apigatewayv2.TlsConfigInput { + config := &apigatewayv2.TlsConfigInput{} + + if len(vConfig) == 0 || vConfig[0] == nil { + return config + } + mConfig := vConfig[0].(map[string]interface{}) + + if vServerNameToVerify, ok := mConfig["server_name_to_verify"].(string); ok && vServerNameToVerify != "" { + config.ServerNameToVerify = aws.String(vServerNameToVerify) + } + + return config +} + +func flattenApiGateway2TlsConfig(config *apigatewayv2.TlsConfig) []interface{} { + if config == nil { + return []interface{}{} + } + + return []interface{}{map[string]interface{}{ + "server_name_to_verify": aws.StringValue(config.ServerNameToVerify), + }} +} diff --git a/aws/resource_aws_apigatewayv2_integration_test.go b/aws/resource_aws_apigatewayv2_integration_test.go index e429f24266a..a919af2ddd5 100644 --- a/aws/resource_aws_apigatewayv2_integration_test.go +++ b/aws/resource_aws_apigatewayv2_integration_test.go @@ -40,6 +40,7 @@ func TestAccAWSAPIGatewayV2Integration_basicWebSocket(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "request_templates.%", "0"), resource.TestCheckResourceAttr(resourceName, "template_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "29000"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), ), }, { @@ -146,6 +147,7 @@ func TestAccAWSAPIGatewayV2Integration_IntegrationTypeHttp(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "request_templates.application/json", ""), resource.TestCheckResourceAttr(resourceName, "template_selection_expression", "$request.body.name"), resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "28999"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), ), }, { @@ -168,6 +170,7 @@ func TestAccAWSAPIGatewayV2Integration_IntegrationTypeHttp(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "request_templates.application/xml", "#set($percent=$number/100)"), resource.TestCheckResourceAttr(resourceName, "template_selection_expression", "$request.body.id"), resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "51"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), ), }, { @@ -210,6 +213,7 @@ func TestAccAWSAPIGatewayV2Integration_Lambda(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "request_templates.%", "0"), resource.TestCheckResourceAttr(resourceName, "template_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "29000"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), ), }, { @@ -222,7 +226,7 @@ func TestAccAWSAPIGatewayV2Integration_Lambda(t *testing.T) { }) } -func TestAccAWSAPIGatewayV2Integration_VpcLink(t *testing.T) { +func TestAccAWSAPIGatewayV2Integration_VpcLinkWebSocket(t *testing.T) { var apiId string var v apigatewayv2.GetIntegrationOutput resourceName := "aws_apigatewayv2_integration.test" @@ -235,7 +239,7 @@ func TestAccAWSAPIGatewayV2Integration_VpcLink(t *testing.T) { CheckDestroy: testAccCheckAWSAPIGatewayV2IntegrationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayV2IntegrationConfig_vpcLink(rName), + Config: testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkWebSocket(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayV2IntegrationExists(resourceName, &apiId, &v), resource.TestCheckResourceAttrPair(resourceName, "connection_id", vpcLinkResourceName, "id"), @@ -252,6 +256,80 @@ func TestAccAWSAPIGatewayV2Integration_VpcLink(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "request_templates.%", "0"), resource.TestCheckResourceAttr(resourceName, "template_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "12345"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAPIGatewayV2IntegrationImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAPIGatewayV2Integration_VpcLinkHttp(t *testing.T) { + var apiId string + var v apigatewayv2.GetIntegrationOutput + resourceName := "aws_apigatewayv2_integration.test" + vpcLinkResourceName := "aws_apigatewayv2_vpc_link.test" + lbListenerResourceName := "aws_lb_listener.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayV2IntegrationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkHttp(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayV2IntegrationExists(resourceName, &apiId, &v), + resource.TestCheckResourceAttrPair(resourceName, "connection_id", vpcLinkResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "connection_type", "VPC_LINK"), + resource.TestCheckResourceAttr(resourceName, "content_handling_strategy", ""), + resource.TestCheckResourceAttr(resourceName, "credentials_arn", ""), + resource.TestCheckResourceAttr(resourceName, "description", "Test private integration"), + resource.TestCheckResourceAttr(resourceName, "integration_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "integration_response_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "integration_type", "HTTP_PROXY"), + resource.TestCheckResourceAttrPair(resourceName, "integration_uri", lbListenerResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "passthrough_behavior", ""), + resource.TestCheckResourceAttr(resourceName, "payload_format_version", "1.0"), + resource.TestCheckResourceAttr(resourceName, "request_templates.%", "0"), + resource.TestCheckResourceAttr(resourceName, "template_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "5001"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "tls_config.0.server_name_to_verify", "www.example.com"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAPIGatewayV2IntegrationImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkHttpUpdated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayV2IntegrationExists(resourceName, &apiId, &v), + resource.TestCheckResourceAttrPair(resourceName, "connection_id", vpcLinkResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "connection_type", "VPC_LINK"), + resource.TestCheckResourceAttr(resourceName, "content_handling_strategy", ""), + resource.TestCheckResourceAttr(resourceName, "credentials_arn", ""), + resource.TestCheckResourceAttr(resourceName, "description", "Test private integration updated"), + resource.TestCheckResourceAttr(resourceName, "integration_method", "POST"), + resource.TestCheckResourceAttr(resourceName, "integration_response_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "integration_type", "HTTP_PROXY"), + resource.TestCheckResourceAttrPair(resourceName, "integration_uri", lbListenerResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "passthrough_behavior", ""), + resource.TestCheckResourceAttr(resourceName, "payload_format_version", "1.0"), + resource.TestCheckResourceAttr(resourceName, "request_templates.%", "0"), + resource.TestCheckResourceAttr(resourceName, "template_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "4999"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "tls_config.0.server_name_to_verify", "www.example.org"), ), }, { @@ -355,12 +433,58 @@ resource "aws_apigatewayv2_api" "test" { func testAccAWSAPIGatewayV2IntegrationConfig_apiHttp(rName string) string { return fmt.Sprintf(` resource "aws_apigatewayv2_api" "test" { - name = %[1]q - protocol_type = "HTTP" + name = %[1]q + protocol_type = "HTTP" } `, rName) } +func testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkHttpBase(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2IntegrationConfig_apiHttp(rName), + testAccAWSAPIGatewayV2VpcLinkConfig_basic(rName), + fmt.Sprintf(` +resource "aws_lb" "test" { + name = %[1]q + + internal = true + load_balancer_type = "network" + subnets = ["${aws_subnet.test.*.id[0]}", "${aws_subnet.test.*.id[1]}"] + + tags = { + Name = %[1]q + } +} + +resource "aws_lb_target_group" "test" { + name = %[1]q + port = 80 + protocol = "TCP" + vpc_id = "${aws_vpc.test.id}" + + health_check { + port = 80 + protocol = "TCP" + } + + tags = { + Name = %[1]q + } +} + +resource "aws_lb_listener" "test" { + load_balancer_arn = "${aws_lb.test.arn}" + port = "80" + protocol = "TCP" + + default_action { + target_group_arn = "${aws_lb_target_group.test.arn}" + type = "forward" + } +} +`, rName)) +} + func testAccAWSAPIGatewayV2IntegrationConfig_basic(rName string) string { return testAccAWSAPIGatewayV2IntegrationConfig_apiWebSocket(rName) + ` resource "aws_apigatewayv2_integration" "test" { @@ -445,17 +569,63 @@ resource "aws_apigatewayv2_integration" "test" { func testAccAWSAPIGatewayV2IntegrationConfig_httpProxy(rName string) string { return testAccAWSAPIGatewayV2IntegrationConfig_apiHttp(rName) + fmt.Sprintf(` resource "aws_apigatewayv2_integration" "test" { - api_id = "${aws_apigatewayv2_api.test.id}" - integration_type = "HTTP_PROXY" + api_id = "${aws_apigatewayv2_api.test.id}" + integration_type = "HTTP_PROXY" - integration_method = "GET" - integration_uri = "https://example.com" + integration_method = "GET" + integration_uri = "https://example.com" } `) } -func testAccAWSAPIGatewayV2IntegrationConfig_vpcLink(rName string) string { - return testAccAWSAPIGatewayV2IntegrationConfig_apiWebSocket(rName) + fmt.Sprintf(` +func testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkHttp(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkHttpBase(rName), + fmt.Sprintf(` +resource "aws_apigatewayv2_integration" "test" { + api_id = "${aws_apigatewayv2_api.test.id}" + integration_type = "HTTP_PROXY" + + connection_type = "VPC_LINK" + connection_id = "${aws_apigatewayv2_vpc_link.test.id}" + description = "Test private integration" + integration_method = "GET" + integration_uri = "${aws_lb_listener.test.arn}" + timeout_milliseconds = 5001 + + tls_config { + server_name_to_verify = "www.example.com" + } +} +`)) +} + +func testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkHttpUpdated(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkHttpBase(rName), + fmt.Sprintf(` +resource "aws_apigatewayv2_integration" "test" { + api_id = "${aws_apigatewayv2_api.test.id}" + integration_type = "HTTP_PROXY" + + connection_type = "VPC_LINK" + connection_id = "${aws_apigatewayv2_vpc_link.test.id}" + description = "Test private integration updated" + integration_method = "POST" + integration_uri = "${aws_lb_listener.test.arn}" + timeout_milliseconds = 4999 + + tls_config { + server_name_to_verify = "www.example.org" + } +} +`)) +} + +func testAccAWSAPIGatewayV2IntegrationConfig_vpcLinkWebSocket(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2IntegrationConfig_apiWebSocket(rName), + fmt.Sprintf(` data "aws_availability_zones" "available" { state = "available" @@ -488,6 +658,10 @@ resource "aws_lb" "test" { internal = true load_balancer_type = "network" subnets = ["${aws_subnet.test.id}"] + + tags = { + Name = %[1]q + } } resource "aws_api_gateway_vpc_link" "test" { @@ -508,5 +682,5 @@ resource "aws_apigatewayv2_integration" "test" { passthrough_behavior = "NEVER" timeout_milliseconds = 12345 } -`, rName) +`, rName)) } diff --git a/website/docs/r/apigatewayv2_integration.html.markdown b/website/docs/r/apigatewayv2_integration.html.markdown index 386fd66d3da..a55cf3c989f 100644 --- a/website/docs/r/apigatewayv2_integration.html.markdown +++ b/website/docs/r/apigatewayv2_integration.html.markdown @@ -67,6 +67,11 @@ Valid values: `WHEN_NO_MATCH`, `WHEN_NO_TEMPLATES`, `NEVER`. Default is `WHEN_NO * `request_templates` - (Optional) A map of Velocity templates that are applied on the request payload based on the value of the Content-Type header sent by the client. Supported only for WebSocket APIs. * `template_selection_expression` - (Optional) The [template selection expression](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-selection-expressions.html#apigateway-websocket-api-template-selection-expressions) for the integration. * `timeout_milliseconds` - (Optional) Custom timeout between 50 and 29,000 milliseconds. The default value is 29,000 milliseconds or 29 seconds. +* `tls_config` - (Optional) The TLS configuration for a private integration. Supported only for HTTP APIs. + +The `tls_config` object supports the following: + +* `server_name_to_verify` - (Optional) If you specify a server name, API Gateway uses it to verify the hostname on the integration's certificate. The server name is also included in the TLS handshake to support Server Name Indication (SNI) or virtual hosting. ## Attribute Reference