Terraform not deploying api gateway stage

20,644

Solution 1

TF does not deploy the API, this link might help you: https://medium.com/coryodaniel/til-forcing-terraform-to-deploy-a-aws-api-gateway-deployment-ed36a9f60c1a

I've fixed mine by adding the variable deployed_at:

resource "aws_api_gateway_deployment" "api_ingest_deployment" {
  depends_on = ["aws_api_gateway_method.xxx",
                "aws_api_gateway_integration.yyy",
                "aws_api_gateway_integration.zzz",
                "aws_api_gateway_integration.www",
  ]
  rest_api_id = "${aws_api_gateway_rest_api.foo.id}"
  stage_name = "${var.environment}"

  variables {
    deployed_at = "${timestamp()}"
  }
}

the downside is that if done this way it will always deploy, even if there's no change

Solution 2

None of the other solutions here worked for me as I get this error:

BadRequestException: Active stages pointing to this deployment must be moved or deleted

This is the solution that worked for me:

resource "aws_api_gateway_deployment" "api_deployment" {
  rest_api_id       = aws_api_gateway_rest_api.api.id
  stage_name        = "default"
  stage_description = "Deployed at ${timestamp()}"

  lifecycle {
    create_before_destroy = true
  }
}

Tested on Terraform 0.12.24

Solution 3

From the aws_api_gateway_deployment:

resource "aws_api_gateway_deployment" "example" {
  rest_api_id = aws_api_gateway_rest_api.example.id

  triggers = {
    # NOTE: The configuration below will satisfy ordering considerations,
    #       but not pick up all future REST API changes. More advanced patterns
    #       are possible, such as using the filesha1() function against the
    #       Terraform configuration file(s) or removing the .id references to
    #       calculate a hash against whole resources. Be aware that using whole
    #       resources will show a difference after the initial implementation.
    #       It will stabilize to only change when resources change afterwards.
    redeployment = sha1(jsonencode([
      aws_api_gateway_resource.example.id,
      aws_api_gateway_method.example.id,
      aws_api_gateway_integration.example.id,
    ]))
  }

  lifecycle {
    create_before_destroy = true
  }
}

You need to define the triggers that should trigger a new deployment, I used without the id and it works great.

Solution 4

There are a couple of things which you need to be careful about.

  1. Mention the depends on properly to make sure the deployment block is executed after the integration and method block is executed(especially when you have any authorizers in place).
  2. Add the timestamp function in variables to deploy it immediately once all the dependent blocks are executed.

      resource "aws_api_gateway_deployment" "mydeployment" {
      depends_on =["aws_api_gateway_method.mymethod","aws_api_gateway_integration.myintegration"]
      rest_api_id = "${aws_api_gateway_rest_api.myapi.id}"
      stage_name = "dev"
      variables = {
         deployed_at = "${timestamp()}"
      }
    }
    

Solution 5

If you are using swagger template and terraform ver >= 0.12 then you can give swagger file for MD5 computation like below. This work perfectly.

resource "aws_api_gateway_deployment" "deploy_stage" {
  rest_api_id = aws_api_gateway_rest_api.product_api.id
  stage_name  = var.stage_name
  stage_description = md5(file("swagger_api.yml"))
}
Share:
20,644

Related videos on Youtube

sethu
Author by

sethu

SOreadytohelp I am a java developer and have worked a lot on Swing and core java.

Updated on July 09, 2022

Comments

  • sethu
    sethu almost 2 years

    I have been trying to create an API Gateway endpoint using terraform. Everything seems to be working except the last part of deploying a stage.

    After I run terraform apply I go into the console and I find that the deployment has not happened. I need to manually click on Deploy Api in order to get it working.

    Here's the terraform file for the api gateway.

    variable "region" {}
    variable "account_id" {}
    
    resource "aws_api_gateway_rest_api" "online_tax_test_client_report_endpoint_api" {
      name = "online_tax_test_client_report_endpoint_api"
      description = "The endpoint that test has to hit when new client reports are available."
      depends_on = ["aws_lambda_function.onlinetax_test_endpoint_lambda"]
    }
    
    resource "aws_api_gateway_resource" "test_client_report_resource" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      parent_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.root_resource_id}"
      path_part = "test_client_report"
    }
    
    resource "aws_api_gateway_method" "test_client_report_method" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      resource_id = "${aws_api_gateway_resource.test_client_report_resource.id}"
      http_method = "POST"
      authorization = "NONE"
    }
    
    resource "aws_api_gateway_integration" "test_client_report_resource_integration" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      resource_id = "${aws_api_gateway_resource.test_client_report_resource.id}"
      http_method = "${aws_api_gateway_method.test_client_report_method.http_method}"
      type = "AWS"
      integration_http_method = "${aws_api_gateway_method.test_client_report_method.http_method}"
      uri = "arn:aws:apigateway:${var.region}:lambda:path/2015-03-31/functions/${aws_lambda_function.onlinetax_test_endpoint_lambda.arn}/invocations"
      request_templates = {
        "application/json" = "${file("${path.module}/generic_request_mapping_template.vm")}"
      }
    }
    
    resource "aws_api_gateway_method_response" "200" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      resource_id = "${aws_api_gateway_resource.test_client_report_resource.id}"
      http_method = "${aws_api_gateway_method.test_client_report_method.http_method}"
      status_code = "200"
    }
    
    resource "aws_api_gateway_integration_response" "test_client_report_resource_integration_default_response" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      resource_id = "${aws_api_gateway_resource.test_client_report_resource.id}"
      http_method = "${aws_api_gateway_method.test_client_report_method.http_method}"
      status_code = "${aws_api_gateway_method_response.200.status_code}"
      selection_pattern = ""
      depends_on = ["aws_api_gateway_integration.test_client_report_resource_integration"]
    }
    
    resource "aws_api_gateway_method_response" "500" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      resource_id = "${aws_api_gateway_resource.test_client_report_resource.id}"
      http_method = "${aws_api_gateway_method.test_client_report_method.http_method}"
      status_code = "500"
    }
    
    resource "aws_api_gateway_integration_response" "test_client_report_resource_integration_error_response" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      resource_id = "${aws_api_gateway_resource.test_client_report_resource.id}"
      http_method = "${aws_api_gateway_method.test_client_report_method.http_method}"
      status_code = "${aws_api_gateway_method_response.500.status_code}"
      selection_pattern = ".*?Error.*"
      depends_on = ["aws_api_gateway_integration.test_client_report_resource_integration"]
    }
    
    resource "aws_lambda_permission" "allow_api_gateway" {
        statement_id = "AllowExecutionFromAPIGateway"
        action = "lambda:InvokeFunction"
        function_name = "${aws_lambda_function.onlinetax_test_endpoint_lambda.arn}"
        principal = "apigateway.amazonaws.com"
        source_arn = "arn:aws:execute-api:${var.region}:${var.account_id}:${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}/*/${aws_api_gateway_integration.test_client_report_resource_integration.integration_http_method}${aws_api_gateway_resource.test_client_report_resource.path}"
        depends_on = ["aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api"]
    }
    
    #This is the part that doesn't seem to work. 
    resource "aws_api_gateway_deployment" "qa5" {
      rest_api_id = "${aws_api_gateway_rest_api.online_tax_test_client_report_endpoint_api.id}"
      stage_name = "qa5"
      depends_on = ["aws_api_gateway_method.test_client_report_method"]
    }
    

    Edit

    Added the graph in :

        digraph {
            compound = "true"
            newrank = "true"
            subgraph "root" {
                "[root] module.lambda.aws_api_gateway_deployment.qa5" [label = "aws_api_gateway_deployment.qa5", shape = "box"]
                "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration" [label = "aws_api_gateway_integration.sbr_client_report_resource_integration", shape = "box"]
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response" [label = "aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response", shape = "box"]
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response" [label = "aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response", shape = "box"]
                "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method" [label = "aws_api_gateway_method.sbr_client_report_method", shape = "box"]
                "[root] module.lambda.aws_api_gateway_method_response.200" [label = "aws_api_gateway_method_response.200", shape = "box"]
                "[root] module.lambda.aws_api_gateway_method_response.500" [label = "aws_api_gateway_method_response.500", shape = "box"]
                "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource" [label = "aws_api_gateway_resource.sbr_client_report_resource", shape = "box"]
                "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api" [label = "aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api", shape = "box"]
                "[root] module.lambda.aws_iam_role.onlinetax_sbr_endpoint_role" [label = "aws_iam_role.onlinetax_sbr_endpoint_role", shape = "box"]
                "[root] module.lambda.aws_iam_role_policy.publish_to_sns_policy" [label = "aws_iam_role_policy.publish_to_sns_policy", shape = "box"]
                "[root] module.lambda.aws_iam_role_policy.write_to_cloudwatch_policy" [label = "aws_iam_role_policy.write_to_cloudwatch_policy", shape = "box"]
                "[root] module.lambda.aws_lambda_function.onlinetax_sbr_endpoint_lambda" [label = "aws_lambda_function.onlinetax_sbr_endpoint_lambda", shape = "box"]
                "[root] module.lambda.aws_lambda_permission.allow_api_gateway" [label = "aws_lambda_permission.allow_api_gateway", shape = "box"]
                "[root] module.lambda.provider.aws" [label = "provider.aws", shape = "diamond"]
                "[root] module.sns.aws_sns_topic.online_tax_qa5_sbr_client_report" [label = "aws_sns_topic.online_tax_qa5_sbr_client_report", shape = "box"]
                "[root] module.sns.provider.aws" [label = "provider.aws", shape = "diamond"]
                "[root] provider.aws (disabled)" [label = "provider.aws (disabled)", shape = "diamond"]
                "[root] module.lambda.aws_api_gateway_deployment.qa5" -> "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method"
                "[root] module.lambda.aws_api_gateway_deployment.qa5" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_deployment.qa5" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration" -> "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method"
                "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration" -> "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource"
                "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration" -> "[root] module.lambda.aws_lambda_function.onlinetax_sbr_endpoint_lambda"
                "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response" -> "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response" -> "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response" -> "[root] module.lambda.aws_api_gateway_method_response.200"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response" -> "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_default_response" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response" -> "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response" -> "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response" -> "[root] module.lambda.aws_api_gateway_method_response.500"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response" -> "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_integration_response.sbr_client_report_resource_integration_error_response" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method" -> "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource"
                "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_method_response.200" -> "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method"
                "[root] module.lambda.aws_api_gateway_method_response.200" -> "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource"
                "[root] module.lambda.aws_api_gateway_method_response.200" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_method_response.200" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_method_response.500" -> "[root] module.lambda.aws_api_gateway_method.sbr_client_report_method"
                "[root] module.lambda.aws_api_gateway_method_response.500" -> "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource"
                "[root] module.lambda.aws_api_gateway_method_response.500" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_method_response.500" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api" -> "[root] module.lambda.aws_lambda_function.onlinetax_sbr_endpoint_lambda"
                "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_iam_role.onlinetax_sbr_endpoint_role" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_iam_role_policy.publish_to_sns_policy" -> "[root] module.lambda.aws_iam_role.onlinetax_sbr_endpoint_role"
                "[root] module.lambda.aws_iam_role_policy.publish_to_sns_policy" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_iam_role_policy.publish_to_sns_policy" -> "[root] module.sns.aws_sns_topic.online_tax_qa5_sbr_client_report"
                "[root] module.lambda.aws_iam_role_policy.write_to_cloudwatch_policy" -> "[root] module.lambda.aws_iam_role.onlinetax_sbr_endpoint_role"
                "[root] module.lambda.aws_iam_role_policy.write_to_cloudwatch_policy" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_lambda_function.onlinetax_sbr_endpoint_lambda" -> "[root] module.lambda.aws_iam_role.onlinetax_sbr_endpoint_role"
                "[root] module.lambda.aws_lambda_function.onlinetax_sbr_endpoint_lambda" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.aws_lambda_permission.allow_api_gateway" -> "[root] module.lambda.aws_api_gateway_integration.sbr_client_report_resource_integration"
                "[root] module.lambda.aws_lambda_permission.allow_api_gateway" -> "[root] module.lambda.aws_api_gateway_resource.sbr_client_report_resource"
                "[root] module.lambda.aws_lambda_permission.allow_api_gateway" -> "[root] module.lambda.aws_api_gateway_rest_api.online_tax_sbr_client_report_endpoint_api"
                "[root] module.lambda.aws_lambda_permission.allow_api_gateway" -> "[root] module.lambda.aws_lambda_function.onlinetax_sbr_endpoint_lambda"
                "[root] module.lambda.aws_lambda_permission.allow_api_gateway" -> "[root] module.lambda.provider.aws"
                "[root] module.lambda.provider.aws" -> "[root] provider.aws (disabled)"
                "[root] module.sns.aws_sns_topic.online_tax_qa5_sbr_client_report" -> "[root] module.sns.provider.aws"
                "[root] module.sns.provider.aws" -> "[root] provider.aws (disabled)"
            }
    }
    

    The Graph has other resources which I have not provided in the tf file above. Its only the API GW that has issues. By the way, I am able to test the API from the console and it works fine. I am not able to execute it from my localbox or postman.

    Any idea on what I am doing wrong?

    • ydaetskcoR
      ydaetskcoR over 7 years
      Is this the case for the first deployment or just future deployments when things change? There's an open issue on GH for the latter: github.com/hashicorp/terraform/issues/6613
    • sethu
      sethu over 7 years
      Even the first deployment didn't work I guess. I destroyed the entire stack and applied it again. Still don't see the deployment. When terraform plan lists all the changes that need to be applied, the deployment resource is listed at the very top. Does that mean that gets deployed first?
    • ydaetskcoR
      ydaetskcoR over 7 years
      It absolutely won't be run first because it is dependent on at least 2 resources (aws_api_gateway_rest_api and aws_api_gateway_method) which in turn may well be dependent on other things. I'd have to run your TF to see but that will have to wait until tonight unfortunately. It might be interesting to see the output of terraform graph if you could edit that into your question?
  • Antonio Terreno
    Antonio Terreno over 6 years
    Actually, I just checked also this GitHub issue github.com/hashicorp/terraform/issues/6613 Doing it this way: resource "aws_api_gateway_deployment" "default" { ... stage_description = "${md5(file("api_gateway.tf"))}" ... } is more clever and will force you to deploy less
  • Christopher Thomas
    Christopher Thomas almost 5 years
    I wasted hours on this problem. Thank you for letting me know!
  • babis21
    babis21 about 4 years
    Antonio's answer helped me, the solution on this post seems 'better' (workaround, but works). Should be an accepted answer?
  • user1297406
    user1297406 almost 4 years
    Thank you, your solution worked for me too on TF 0.12.24
  • d8aninja
    d8aninja about 2 years
    there no longer appears to be a variables block available to the resource, and the EXPORT TF_VAR_foo doesn't work anymore, either... FYI