Reputation: 8411
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"]
}
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?
Upvotes: 35
Views: 42986
Reputation: 334
To trigger redeployment on any file changes here is solution:
resource "aws_api_gateway_deployment" "api_deployment" {
rest_api_id = aws_api_gateway_rest_api.url_shortner_api.id
triggers = {
redeployment = sha1(
jsonencode([
file("root_get_method.tf"),
file("url_shortner_post_method.tf"),
file("url_shortner_get_method.tf"),
])
)
}
lifecycle {
create_before_destroy = true
}
}
This will only trigger redeployment if anything changed in listed files.
Upvotes: 2
Reputation: 411
old question but this thread still comes up in google.
For a possible better solution in 2022 we use the following:
Create a SHA1 hash of all the referenced resources configuration, so that any change in any attribute of the referenced resources is detected and triggers a new deployment (rather than only the ID like in the official documentation).
I have found this comment which explains it in further detail: https://github.com/hashicorp/terraform-provider-aws/issues/162#issuecomment-532593939
resource "aws_api_gateway_deployment" "example" {
rest_api_id = aws_api_gateway_rest_api.example.id
triggers = {
redeployment = sha1(jsonencode([
aws_api_gateway_resource.example,
aws_api_gateway_method.example,
aws_api_gateway_integration.example,
]))
}
lifecycle {
create_before_destroy = true
}
}
Upvotes: 13
Reputation: 6423
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.
Upvotes: 7
Reputation: 43
Got the same issue. Had the following problem:
lifecycle { create_before_destroy = false }
I had to turn it to false, as I had an options method being added to the same uri as the get. When I tried running the build with flag as turned on was getting the following error
Error creating API Gateway Deployment: BadRequestException: No integration defined for method
BadRequestException: Active stages pointing to this deployment must be moved or deleted
This upon investigation, I found to be related to the custom domain mapping happening in my terraform script. Upon manual deletion of the mapping and running the Jenkins Job solved the issue.
/usr/local/bin/terraform destroy -target aws_api_gateway_base_path_mapping.<resource_name> -auto-approve
This solved the issue. I checked in multiple blogs where they mentioned on setting the life-cycle flag to true. But I couldn't do it as it created the above issue.
Upvotes: 0
Reputation: 16636
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
Upvotes: 13
Reputation: 947
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"))
}
Upvotes: 1
Reputation: 1236
There are a couple of things which you need to be careful about.
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()}"
}
}
Upvotes: 3
Reputation: 992
Generally the code change is at lambda level. So you may interpolate lambda code version as a variable and add it in same module where you have api deployment. This way it will deploy only when lambda code changes.
Upvotes: 2
Reputation: 3055
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
Upvotes: 23