4
votes

I have a case where I have to create an aws_vpc resource if the user does not provide vpc id. After that I am supposed to create resources with that VPC.

Now, I am applying conditionals while creating an aws_vpc resource. For example, only create VPC if existing_vpc is false:

count                = "${var.existing_vpc ? 0 : 1}"

Next, for example, I have to create nodes in the VPC. If the existing_vpc is true, use the var.vpc_id, else use the computed VPC ID from aws_vpc resource.

But, the issue is, if existing_vpc is true, aws_vpc will not create a new resource and the ternary condition is anyways trying to check if the aws_vpc resource is being created or not. If it doesn't get created, terraform errors out.

An example of the error when using conditional operator on aws_subnet:

Resource 'aws_subnet.xyz-subnet' not found for variable 'aws_subnet.xyz-subnet.id'

The code resulting in the error is:

subnet_id = "${var.existing_vpc ? var.subnet_id : aws_subnet.xyz-subnet.id}"

If both things are dependent on each other, how can we create conditional resources and assign values to other configuration based on them?

2
The direct answer to your question is to use null in the upcoming 0.12 release. The indirect answer is that it sounds like the code conditionals and organization should be redone to avoid this issue, because that ternary for the subnet_id seems like it should be different.Matt Schuchard
Well, my provider currently does not support 0.12 as of now. So, I have to continue with 0.11.x. Any way I can make this work?Shantanu Deshpande

2 Answers

1
votes

The following example shows how to optionally specify whether a resource is created (using the conditional operator), and shows how to handle returning output when a resource is not created. This happens to be done using a module, and uses an object variable's element as a flag to indicate whether the resource should be created or not.

But to specifically answer your question, you can use the conditional operator as follows:


    output "module_id" {
       value = var.module_config.skip == true ? null : format("%v",null_resource.null.*.id)
    }

And access the output in the calling main.tf:

module "use_conditionals" {
    source = "../../scratch/conditionals-modules/m2" # << Change to your directory
    a = module.skipped_module.module_id # Doesn't exist, so might need to handle that.
    b = module.notskipped_module.module_id
    c = module.default_module.module_id
}

Full example follows. NOTE: this is using terraform v0.14.2


# root/main.tf
provider "null" {}

module "skipped_module" {
    source = "../../scratch/conditionals-modules/m1" # << Change to your directory
    module_config = { 
        skip = true         # explicitly skip this module.
        name = "skipped" 
    }
}

module "notskipped_module" {
    source = "../../scratch/conditionals-modules/m1" # << Change to your directory
    module_config = { 
        skip = false        # explicitly don't skip this module.
        name = "notskipped"
    }
}

module "default_module" {
    source = "../../scratch/conditionals-modules/m1" # << Change to your directory
    # The default position is, don't skip. see m1/variables.tf
}

module "use_conditionals" {
    source = "../../scratch/conditionals-modules/m2" # << Change to your directory
    a = module.skipped_module.module_id
    b = module.notskipped_module.module_id
    c = module.default_module.module_id
}

# root/outputs.tf
output skipped_module_name_and_id {
    value = module.skipped_module.module_name_and_id
}

output notskipped_module_name_and_id {
    value = module.notskipped_module.module_name_and_id
}

output default_module_name_and_id {
    value = module.default_module.module_name_and_id
}

the module


# m1/main.tf
resource "null_resource" "null" {
    count = var.module_config.skip ? 0 : 1 # If skip == true, then don't create the resource.

    provisioner "local-exec" {
        command = <<EOT
        #!/usr/bin/env bash
        echo "null resource, var.module_config.name: ${var.module_config.name}"
EOT
    }
}

# m1/variables.tf
variable "module_config" {
    type = object ({ 
        skip = bool, 
        name = string 
    })
    default = {
        skip = false
        name = "<NAME>"
        }
}

# m1/outputs.tf
output "module_name_and_id" {
   value = var.module_config.skip == true ? "SKIPPED" : format(
      "%s id:%v",
      var.module_config.name, 
      null_resource.null.*.id
   )
}

output "module_id" {
      value = var.module_config.skip == true ? null : format("%v",null_resource.null.*.id)
}

0
votes

You can access dynamically created modules and resources as follows

output "vpc_id" {
  value = length(module.vpc) > 0 ? module.vpc[*].id : null
}
  • If count = 0, output is null
  • If count > 0, output is list of vpc ids

If count = 1 and you want to receive a single vpc id you can specify:

output "vpc_id" {
  value = length(module.vpc) > 0 ? one(module.vpc).id : null
}