Putting API Gateway Integration Response: NotFoundException: Invalid Integration identifier specified

Guys, im trying to create an api, but just can’t create an integration response for my post method

resource "aws_api_gateway_rest_api" "api" {
  name        = "example-api"
  description = "API integration for Lambda"
  
  endpoint_configuration {
    types = ["REGIONAL"]
  }
}

resource "aws_api_gateway_resource" "resource" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  parent_id   = aws_api_gateway_rest_api.api.root_resource_id
  path_part   = "lambda-function-s3"
}

resource "aws_api_gateway_method" "options" {
  rest_api_id   = aws_api_gateway_rest_api.api.id
  resource_id   = aws_api_gateway_resource.resource.id
  http_method   = "OPTIONS"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "options" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.options.http_method

  type = "MOCK"

  integration_http_method = "OPTIONS"
  passthrough_behavior    = "WHEN_NO_MATCH"
}

resource "aws_api_gateway_method_response" "options_200" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.options.http_method
  status_code = "200"

  response_models = {
    "application/json" = "Empty"
  }

  response_parameters = {
    "method.response.header.Access-Control-Allow-Headers" = true
    "method.response.header.Access-Control-Allow-Methods" = true
    "method.response.header.Access-Control-Allow-Origin"  = true
  }
}

resource "aws_api_gateway_integration_response" "options_200" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.options.http_method
  status_code = aws_api_gateway_method_response.options_200.status_code

  selection_pattern = "-"
  content_handling  = "CONVERT_TO_TEXT"

  response_parameters = {
    "method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
    "method.response.header.Access-Control-Allow-Methods" = "'GET,POST,OPTIONS'"
    "method.response.header.Access-Control-Allow-Origin"  = "'*'"
  }
}
resource "aws_api_gateway_method" "post" {
  rest_api_id   = aws_api_gateway_rest_api.api.id
  resource_id   = aws_api_gateway_resource.resource.id
  http_method   = "POST"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "post" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.post.http_method

  integration_http_method = "POST"
  type                    = "AWS"
  uri                     = "arn:aws:apigateway:${var.aws_region}:lambda:path/2015-03-31/functions/${aws_lambda_function.my_lambda.arn}/invocations"
  passthrough_behavior    = "WHEN_NO_MATCH"
  timeout_milliseconds    = 29000
}

resource "aws_api_gateway_method_response" "post_200" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.post.http_method
  status_code = "200"

  response_models = {
    "application/json" = "Empty"
  }

  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = true
  }
}
resource "aws_api_gateway_integration_response" "post_200" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.post.http_method
  status_code = aws_api_gateway_method_response.post_200.status_code

  selection_pattern = "-"
  content_handling  = "CONVERT_TO_TEXT"

  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = "'*'"
  }
}
resource "aws_api_gateway_deployment" "api_gateway" {
  depends_on = [
    aws_api_gateway_integration.post,
    aws_api_gateway_integration_response.post_200,
    aws_api_gateway_integration.options,
    aws_api_gateway_integration_response.options_200,
  ]
  rest_api_id = aws_api_gateway_rest_api.api.id
  stage_name  = "prod"
}

thats all the code

and this is the code returning error:

resource "aws_api_gateway_integration_response" "post_200" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.post.http_method
  status_code = aws_api_gateway_method_response.post_200.status_code

  selection_pattern = "-"
  content_handling  = "CONVERT_TO_TEXT"

  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = "'*'"
  }

it is returning this:

╷
│ Error: putting API Gateway Integration Response: NotFoundException: Invalid Integration identifier specified
│ 
│   with module.front-s3-upload.aws_api_gateway_integration_response.post_200,
│   on ../terraform/api.tf line 99, in resource "aws_api_gateway_integration_response" "post_200":
│   99: resource "aws_api_gateway_integration_response" "post_200" {
│ 

I believe that there is a race condition related to the aws_api_gateway_integration resources and the aws_api_gateway_integration_response objects. My first apply works, but subsequent apply would give me the following error which makes the race condition apparent:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_api_gateway_integration.options must be replaced
-/+ resource "aws_api_gateway_integration" "options" {
      - cache_key_parameters    = [] -> null
      ~ cache_namespace         = "5p7zyk" -> (known after apply)
      ~ id                      = "agi-q49vyk5jqa-5p7zyk-OPTIONS" -> (known after apply)
      + integration_http_method = "OPTIONS" # forces replacement
      - request_parameters      = {} -> null
      - request_templates       = {} -> null
        # (7 unchanged attributes hidden)
    }

  # aws_api_gateway_integration_response.options_200 will be updated in-place
  ~ resource "aws_api_gateway_integration_response" "options_200" {
        id                  = "agir-q49vyk5jqa-5p7zyk-OPTIONS-200"
      + selection_pattern   = "-"
        # (7 unchanged attributes hidden)
    }

  # aws_api_gateway_integration_response.post_200 will be updated in-place
  ~ resource "aws_api_gateway_integration_response" "post_200" {
        id                  = "agir-q49vyk5jqa-5p7zyk-POST-200"
      + selection_pattern   = "-"
        # (7 unchanged attributes hidden)
    }

Plan: 1 to add, 2 to change, 1 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_api_gateway_integration.options: Destroying... [id=agi-q49vyk5jqa-5p7zyk-OPTIONS]
aws_api_gateway_integration_response.post_200: Modifying... [id=agir-q49vyk5jqa-5p7zyk-POST-200]
aws_api_gateway_integration_response.options_200: Modifying... [id=agir-q49vyk5jqa-5p7zyk-OPTIONS-200]
aws_api_gateway_integration.options: Destruction complete after 1s
aws_api_gateway_integration.options: Creating...
aws_api_gateway_integration_response.post_200: Modifications complete after 1s [id=agir-q49vyk5jqa-5p7zyk-POST-200]
aws_api_gateway_integration.options: Creation complete after 0s [id=agi-q49vyk5jqa-5p7zyk-OPTIONS]
╷
│ Error: Provider produced inconsistent result after apply
│
│ When applying changes to aws_api_gateway_integration_response.options_200, provider "provider[\"registry.terraform.io/hashicorp/aws\"]" produced   
│ an unexpected new value: Root object was present, but now absent.
│
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.
╵

Sometimes I get a validation error from AWS instead but it should be the same root cause. To address the problem, instead of referencing aws_api_gateway_method for the http_method argument in your aws_api_gateway_integration_response, you should instead refer to the aws_api_gateway_integration resource instead. For example, see the http_method argument in this updated aws_api_gateway_integration_response.options_200 resource:

resource "aws_api_gateway_integration_response" "options_200" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_integration.options.http_method
  status_code = aws_api_gateway_method_response.options_200.status_code

  selection_pattern = "-"
  content_handling  = "CONVERT_TO_TEXT"

  response_parameters = {
    "method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
    "method.response.header.Access-Control-Allow-Methods" = "'GET,POST,OPTIONS'"
    "method.response.header.Access-Control-Allow-Origin"  = "'*'"
  }
}

This sets the proper implicit dependencies and the configuration should apply successfully. That being said, it seems like there are still some perpetual update issue since there are always changes when you re-apply the configuration.

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_api_gateway_integration.options must be replaced
-/+ resource "aws_api_gateway_integration" "options" {
      - cache_key_parameters    = [] -> null
      ~ cache_namespace         = "4rorjy" -> (known after apply)
      ~ id                      = "agi-aluruh1lig-4rorjy-OPTIONS" -> (known after apply)
      + integration_http_method = "OPTIONS" # forces replacement
      - request_parameters      = {} -> null
      - request_templates       = {} -> null
        # (7 unchanged attributes hidden)
    }

  # aws_api_gateway_integration_response.options_200 will be updated in-place
  ~ resource "aws_api_gateway_integration_response" "options_200" {
        id                  = "agir-aluruh1lig-4rorjy-OPTIONS-200"
      + selection_pattern   = "-"
        # (7 unchanged attributes hidden)
    }

  # aws_api_gateway_integration_response.post_200 will be updated in-place
  ~ resource "aws_api_gateway_integration_response" "post_200" {
        id                  = "agir-aluruh1lig-4rorjy-POST-200"
      + selection_pattern   = "-"
        # (7 unchanged attributes hidden)
    }

Plan: 1 to add, 2 to change, 1 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_api_gateway_integration.options: Destroying... [id=agi-aluruh1lig-4rorjy-OPTIONS]
aws_api_gateway_integration_response.post_200: Modifying... [id=agir-aluruh1lig-4rorjy-POST-200]
aws_api_gateway_integration.options: Destruction complete after 0s
aws_api_gateway_integration.options: Creating...
aws_api_gateway_integration_response.post_200: Modifications complete after 0s [id=agir-aluruh1lig-4rorjy-POST-200]
aws_api_gateway_integration.options: Creation complete after 0s [id=agi-aluruh1lig-4rorjy-OPTIONS]
aws_api_gateway_integration_response.options_200: Modifying... [id=agir-aluruh1lig-4rorjy-OPTIONS-200]
aws_api_gateway_integration_response.options_200: Modifications complete after 0s [id=agir-aluruh1lig-4rorjy-OPTIONS-200]

Apply complete! Resources: 1 added, 2 changed, 1 destroyed.

You can remove the selection_pattern argument from the aws_api_gateway_integration_response resources to work around the issue for these resources.

For aws_api_gateway_integration, maybe removing http_method would work since OPTION is likely a default value. There is something strange with the default state of the resource, so you might need to look more into it.

Anyway this should address the more serious issues in your config.