Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependency between modules ignored #17363

Closed
ghost opened this issue Feb 15, 2018 · 6 comments
Closed

Dependency between modules ignored #17363

ghost opened this issue Feb 15, 2018 · 6 comments

Comments

@ghost
Copy link

ghost commented Feb 15, 2018

Terraform Version

terraform --version
Terraform v0.11.3

Terraform Configuration Files

Module to create a VPC:

module "test_vpc" {
  source = "./modules/aws/network"

  vpc_cidr_block = "10.1.0.0/16"
  vpc_name       = "test_vpc"

  tags = {
    Project = "INFRA"
  }

  enable_dns_support = true
  enable_host_names  = true

  internet_gateway_name = "IGW"

  azs             = ["eu-west-2a", "eu-west-2b"]
  private_subnets = ["10.1.0.0/24", "10.1.1.0/24"]
  public_subnets  = ["10.1.2.0/24", "10.1.3.0/24"]
}

Module to create 2 Security Groups, where one is referenced by the other

module "security_group" {
  source = "./modules/aws/compute/security_group"

  security_group_description = "Instance Security Group"
  security_group_name        = "instance_test_sg"
  vpc_id                     = "${element(module.test_vpc.vpc_id, 0)}"

  security_group_rules_sg = [
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      type        = "ingress"
      description = "ALB"

      security_group_ids = "${module.alb_security_group.security_group_id}"
    }
  ]

  private_instance = true

  tags = {}

}

module "alb_security_group" {
  source = "./modules/aws/compute/security_group"

  security_group_description = "ALB Security Group"
  security_group_name        = "alb_test_sg"
  vpc_id                     = "${element(module.test_vpc.vpc_id, 0)}"

  security_group_rules_cidr = [
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      type        = "ingress"
      description = "Home Network."
      cidr_blocks = "X.X.X.X/32"
    }
  ]

  tags = {}
}

Instances in the private subnet

resource "aws_instance" "private_http_servers" {
  count = "${length(module.test_vpc.private_subnets)}"

  ami           = "${data.aws_ami.aws_ami.id}"
  instance_type = "t2.medium"

  subnet_id = "${element(module.test_vpc.private_subnets, count.index)}"

  vpc_security_group_ids = ["${module.security_group.security_group_id}"]

  key_name = "${var.keypair_name}"

  root_block_device {
    volume_size          = "${var.root_volume_size}"
    volume_type         = "${var.root_volume_type}"
    iops                       = "${var.root_iops}"
    delete_on_termination = "${var.root_volume_delete}"
  }

  user_data = "${file("${path.module}/userdata/startup.sh")}"
}

Expected Behavior

A VPC, with 2 private and 2 public subnets and 2 instances running in the private subnet.

Actual Behavior

It's impossible for Terraform to determine the dependencies between modules. I had to manually comment out resources and then gradually add them back in. That works for creating them, but now I wanted to destroy my test resources and it has been running for the past 10 minutes. From my past experiences I know that Terraform won't be able to complete.

This is really strange behaviour and I would consider modules quite vital to the framework.

Additional Context

I didn't use modules from the module registry, but created my own. There are 2 modules involved:

  • VPC:
    Create a VPC with a number of Private and Public subnets and Route Tables
  • Security Group:
    Create a Security Group and either add rules with Security Group ID or CIDR blocks

The module for the Security Group is used twice, so the result should be 2 SG.

@apparentlymart
Copy link
Contributor

Hi @bdebakker! Sorry this didn't work as expected.

On the surface I would expect the configuration you shared here to work as you wanted. I'd like to try to figure out what happened here.

Could you share what happened when you tried to apply the configuration you described here? I assume you got some sort of error; it'd be very helpful to see the exact text of that error so we can understand what failed. If that error message relates to a resource within one of your modules, it would also help to see the configuration of that resource to understand how it uses to the input variables defined for that module.


Some context that may be helpful to think about what's going on here is that modules themselves are not a node in the dependency graph; instead, the individual input variables and outputs of those modules are separate graph nodes, and this allows Terraform to achieve better parallelism by starting work early on aspects of modules that do not have dependencies.

As a concrete example, consider the vpc_id argument you pass in your module "security_group" block. The expression given there includes module.test_vpc.vpc_id which means that there's a dependency from the node module.security_group.var.vpc_id to the node module.test_vpc.output.vpc_id.

Since dependencies are transitive, any references in the definition of that module.test_vpc.output.vpc_id node are indirect dependencies of module.security_group.var.vpc_id. Similarly, anything that references var.vpc_id inside your security group module depends indirectly on all of the objects referenced by module.test_vpc.output.vpc_id.

If you have a resource in the security group module that doesn't reference var.vpc_id then Terraform will not know that it must wait until the VPC is ready before processing it.

If you run terraform graph on your configuration and run the result through one of the graphviz rendering tools (e.g. dot) then you can see how the variable and output nodes participate in the graph. Unfortunately that rendering tends to get pretty unusable quickly when you have a non-trivial configuration, but hopefully it helps to put the above statements in perspective.

@gustavosoares
Copy link

you might wanna take a look at #1046 it has been open since dec/2016 😩

@ghost
Copy link
Author

ghost commented Feb 21, 2018

Thanks for the explanation @apparentlymart . When I run a terraform plan, I get the following error message:

Error: Error refreshing state: 2 error(s) occurred:

* aws_instance.private_http_servers: aws_instance.private_http_servers: value of 'count' cannot be computed
* module.security_group.aws_security_group_rule.default_sg_id: aws_security_group_rule.default_sg_id: value of 'count' cannot be computed

When I googled this problem, I found an other GitHub issue that said something about problems calculating dependencies between modules. I think I'm missing something, but doesn't count implicitly mean a dependency, if the input variable is the output of a different module?

I moved this TF file to a separate directory and generated the graph. When I ran it on the full config, my IntelliJ crashed :). I've attached the image, so hopefully it gives you some insight into what is happening: graph

We are gradually building out the environment and I'm moving more towards a modularised approach. However, I'm not sure if we are going to continue to go down that path, if this proves to be too difficult.

@apparentlymart
Copy link
Contributor

The count argument is unfortunately a special case because Terraform needs to resolve it during the plan phase in order to know how many instances to plan.

It's harder to see this when module indirection is in play, but Terraform is trying to tell you here that the value of that output depends on a resource that isn't created yet (because we're still planning how it would get created) and so the count cannot be resolved.

The confusing user experience here is known, and our planned solution for it is discussed in #4149, along with some info on how it can be worked around today. In summary: you can use the -target argument with whatever resource(s) are needed for the count, which will cause Terraform to temporarily ignore your aws_instance.private_http_servers for this run, allowing the partial plan to be created. After that plan is applied successfully, you can then run normally again to complete the remaining resources.

The issue I referenced is currently on hold because the team at HashiCorp is focused on configuration language improvements at the time of writing, but we plan to work on that other issue later in the year when we can shift our focus over to CLI workflow and graph-related work.

@apparentlymart
Copy link
Contributor

Since we already have #4149 open capturing the planned fix here, I'm going to close this one just to consolidate the discussion over there. Thanks again for reporting this, @bdebakker!

@github-actions
Copy link

github-actions bot commented Jun 2, 2021

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants