3
votes

Terraform Version

Terraform v0.12.1

Terraform Configuration Files

main.tf in my root provider:

provider "google" {}

module "organisation_info" {
  source           = "../../modules/organisation-info"
  top_level_domain = "smoothteam.fi"
  region           = "us-central1"
}

module "stack_info" {
  source            = "../../modules/stack-info"
  organisation_info = "${module.organisation_info}"
}

Here's module 'organisation-info':

variable "top_level_domain" {}
variable "region" {}

data "google_organization" "organization" {
  domain = "${var.top_level_domain}"
}

locals {
  organization_id    = "${data.google_organization.organization.id}"
  ns = "${replace("${var.top_level_domain}", ".", "-")}-"
}

output "organization_id" {
  value = "${local.organization_id}"
}

output "ns" {
  value = "${local.ns}"
}

Then module 'stack-info':

variable "organisation_info" {
  type        = any
  description = "The organisation-scope this environment exists in."
}

module "project_info" {
  source            = "../project-info"
  organisation_info = "${var.organisation_info}"
  name              = "${local.project}"
}

locals {
  # Use the 'default' workspace for the 'staging' stack.
  name = "${terraform.workspace == "default" ? "staging" : terraform.workspace}"
  # In the 'production' stack, target the production project. Otherwise, target the staging project.
  project = "${local.name == "production" ? "production" : "staging"}"
}

output "project" {
  value = "${module.project_info}" # COMMENTING THIS OUTPUT REMOVES THE CYCLE.
}

And finally, the 'project-info' module:

variable "organisation_info" {
  type        = any
}
variable "name" {}

data "google_project" "project" {
  project_id = "${local.project_id}"
}

locals {
  project_id = "${var.organisation_info.ns}${var.name}"
}

output "org" {
  value = "${var.organisation_info}"
}

Debug Output

After doing terraform destroy -auto-approve, I get:

Error: Cycle: module.stack_info.module.project_info.local.project_id, module.stack_info.output.project, module.stack_info.module.project_info.data.google_project.project (destroy), module.organisation_info.data.google_organization.organization (destroy), module.stack_info.var.organisation_info, module.stack_info.module.project_info.var.organisation_info, module.stack_info.module.project_info.output.org

And terraform graph -verbose -draw-cycles -type=plan-destroy gives me this graph: image Source:

digraph {
        compound = "true"
        newrank = "true"
        subgraph "root" {
                "[root] module.organisation_info.data.google_organization.organization" [label = "module.organisation_info.data.google_organization.organization", shape = "box"]
                "[root] module.stack_info.module.project_info.data.google_project.project" [label = "module.stack_info.module.project_info.data.google_project.project", shape = "box"]
                "[root] provider.google" [label = "provider.google", shape = "diamond"]
                "[root] module.organisation_info.data.google_organization.organization" -> "[root] module.stack_info.module.project_info.data.google_project.project"
                "[root] module.organisation_info.data.google_organization.organization" -> "[root] provider.google"
                "[root] module.stack_info.module.project_info.data.google_project.project" -> "[root] provider.google"
        }
}

Expected Behavior

The idea is to use modules at the org, project and stack levels to set up naming conventions that can be re-used across all resources. Organisation-info loads organisation info, project-info about projects, and stack-info determines which project to target based on current workspace.

I have omitted a bunch of other logic in the modules in order to keep them clean for this issue.

According to terraform there are no cycles, and destroy should work fine.

Actual Behavior

We get the cycle I posted above, even though terraform shows no cycles.

Steps to Reproduce

  1. Set up the three modules, organisation-info, project-info, and stack-info as shown above.
  2. Set up a root provider as shown above.
  3. terraform init
  4. terraform destroy (it doesn't seem to matter if you've applied first)

Additional Context

The weird thing is that if I comment out this output in stack-info, the cycle stops:

output "project" {
  value = "${module.project_info}" # IT'S THIS... COMMENTING THIS OUT REMOVES THE CYCLE.
}

This seems really weird... I neither understand why outputting a variable should make a difference, nor why I'm getting a cycle error when there's no cycle.

Oddly, terraform plan -destroy does not reveal the cycle, only terraform destroy.

My spidey sense tells me evil is afoot.

Appreciate anyone who can tell me what's going on, whether this is a bug, and perhaps how to work around.

1
I'm having something similar, same version. Any ideas? stackoverflow.com/questions/56548780/… - Joe
I'm discussing this with the dudes at Hashicorp, feel free to chime in and describe your experience. github.com/hashicorp/terraform/issues/21662 - Sebastian Nemeth
Thanks, I'll follow that. - Joe
same issue. I upgraded to Terraform v0.13.2.. but still same issue Error: Cycle: ....destroy deposed :( - Abdennour TOUMI

1 Answers

0
votes

In my case the same cycle error was because the out of 3 key:pair one of the key:pair expected by map type variable inside variables.tf of a module not declared in main.tf nor in anywhere.