terraform-provider-aws: Error creating API Gateway Stage: ConflictException: Stage already exists

(See #1153. Since this issue was closed by the submitter and I can’t re-open it, I’m duplicating it.)

Using resource "aws_api_gateway_deployment" together with a resource "aws_api_gateway_stage" of the same name results in a Error creating API Gateway Stage: ConflictException: Stage already exists.

Apart from the obvious issues with the documentation (as discussed in #1153), this seems to be caused by AWS creating a new stage for each create-deployment, rather than referencing it. I take this from the documentation:

--stage-name (string)

The name of the Stage resource for the Deployment resource to create.

--stage-description (string)

The description of the Stage resource for the Deployment resource to create.

Together with a stage of the same name (which may be required e.g. to assign a certificate to the stage), this leads to a conflict.

According to the documentation, the --stage-name and --stage-description parameters are optional. Terraform, however, treats them as required parameters. The stage_name parameter of the aws_api_gateway_deployment resource should be optional.

Minimal example to reproduce the problem:

resource "aws_api_gateway_rest_api" "my_api" {
  name = "My API"
}

resource "aws_api_gateway_stage" "prod" {
  stage_name            = "prod"
  rest_api_id           = "${aws_api_gateway_rest_api.my_api.id}"
  deployment_id         = "${aws_api_gateway_deployment.my_deployment.id}"
  client_certificate_id = "${var.client_cert_id}" // This is why I need the explicit resource and cannot rely on the one implicitly created by `aws_api_gateway_deployment.my_deployment`.
  description           = "foo"
}

resource "aws_api_gateway_deployment" "my_deployment" {
  rest_api_id = "${aws_api_gateway_rest_api.my_api.id}"
  stage_name  = "prod" // This tries to *create* a stage, which then conflicts with `aws_api_gateway_stage.prod`. This parameter should be optional!
}

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 19
  • Comments: 22 (10 by maintainers)

Commits related to this issue

Most upvoted comments

@Puneeth-n Interesting, I haven’t tried via the AWS CLI, just had a look at the docs. However, I have found a workaround: Just leave stage_name empty (i.e. stage_name = ""), and aws_api_gateway_deployment will not create an additional stage.

Full example:

resource "aws_api_gateway_rest_api" "my_api" {
  name = "My API"
}

resource "aws_api_gateway_stage" "prod" {
  stage_name            = "prod"
  rest_api_id           = "${aws_api_gateway_rest_api.my_api.id}"
  deployment_id         = "${aws_api_gateway_deployment.my_deployment.id}"
  client_certificate_id = "${var.client_cert_id}"
  description           = "foo"
}

resource "aws_api_gateway_deployment" "my_deployment" {
  rest_api_id = "${aws_api_gateway_rest_api.my_api.id}"
  stage_name  = "" // Works for me: No error, does not create a stage
}

So even if the AWS API (I’m talking about the Go SDK) indeed requires the StageName to be present, it apparently can still be left empty and have the desired effect.

So I’m still thinking that the field should be optional, maybe by defaulting a missing stage_name to an empty string in https://github.com/terraform-providers/terraform-provider-aws/blob/ddd2205c6b6cc547ce5a940f9a16bf66f3276c3a/aws/resource_aws_api_gateway_deployment.go#L84

@fmthoma I’m working on a PR to fix this.

@fmthoma I encountered this issue few months ago. The way I handled it is to deploy to an intermediate stage and use the deployment id to create the final stage.

Elaborating my answer here

resource "aws_api_gateway_deployment" "zendesk-deployment" {
  rest_api_id       = "${aws_api_gateway_rest_api.zendesk.id}"
  stage_name        = "intermediate"
  stage_description = "Deployed at: ${timestamp()}"

  depends_on = ["aws_api_gateway_integration.zendesk-lambda-integration", "aws_api_gateway_method.zendesk-log-method"]
}

resource "aws_api_gateway_stage" "stage" {
  stage_name    = "${var.environment}"
  rest_api_id   = "${aws_api_gateway_rest_api.zendesk.id}"
  deployment_id = "${aws_api_gateway_deployment.zendesk-deployment.id}"
}

The change to make stage_name optional has been merged and will release with version 2.3.0 of the Terraform AWS Provider, likely middle of next week.

The workaround of creating a deployment with no stage name, then a stage with the desired stage name that references the initial deployment doesn’t work for me, for the following reasons:

  • the stage of the desired name gets created, but the API doesn’t get deployed in a workable form under that name - this needs to be corrected with a deploy action in the CLI or the console, outside of terraform. In particular, when browsing the API in the console, the stage tree is devoid of the expected resource path nodes - these only reappear with a separate deployment action
  • the next time terraform apply runs, terraform resets the stage’s deployment id to refer to the original deployment id which doesn’t have a workable deployment of the API.

The workaround that did work for me was to apply the deployment resource first, then import the stage resource.

@zioproto : I opened a PR for my line change 😃

@fmthoma I think the aws documentation is misleading. I tried it on an existing RESTAPI I had in a test environment and deployment failed. Meaning, cannot deploy without specifying stage name.

====> aws apigateway create-deployment --rest-api-id jhefbjhwfbcw

An error occurred (BadRequestException) when calling the CreateDeployment operation: Invalid deployment content specified.CreateDeploymentInput should not be null.

====> aws apigateway create-deployment --rest-api-id jhefbjhwfbcw --stage-name foo

{
    "id": "jhbfrhrb",
    "createdDate": 1515598231
}