Hello,
I am attempting to use a module output as a variable value my main.tf to be passed to a module. The module is set with a default value of null, so that if it doesn’t get said value, it moves along. However the output might not be available. How can I get TF to accept a null/missing output value in the module variable definition?
So essentially, if module_A is run, it is to output a result to be ingested by module_B with a variable definition of var_name = module.module_A.output. If module_A has not been run, that output will not be available.
I would like module_B to set var_name to null in the absence of the output.
Is this possible? If so, what would it look like?
Thanks,
Chris
The ternary operator or try() work.
module "B" {
var_name = condition_controlling_A_use ? module.module_A.output : null
var_name = try(module.module_A.output , null)
}
Thanks for the response Jeremy, but it looks like that doesn’t work.
When I set the condition_controlling_A_use to false, it still runs module A and then passes a null. When set to true, it runs module A and passes the output.
Try does the same thing.
My idea to let it run Module A, even when I don’t want it to run, and then destroy Module A results in TF wanting to destroy the Module B resources.
My actual use case is Dedicated Hosts or not. With the controlling variable set to false, it builds the DHs and then doesn’t put the instance on the DH. With it set to true, it creates the DHs and then puts the instance on the DH. Setting it to false and building the instances stand alone (Not on the DH) and then destroying the DHG/DHs wants to delete the stand alone instances…
I’m afraid you’ve lost me.
It would help a lot to have a code sample in addition to the natural language explanation.
module "region_a_dedicated_host_group_01" {
source = "./modules/dedicated_host_group/"
azure_client_id = var.azure_client_id
azure_client_secret = var.azure_client_secret
azure_dhg_fault_domain_count = var.azure_dhg_fault_domain_count_region_a
azure_dhg_name = var.azure_dhg01_name_region_a
azure_environment = var.azure_environment
azure_location = var.azure_location_region_a
azure_rg_name = var.azure_rg_name_region_a
azure_subscription_id = var.azure_subscription_id
azure_tenant_id = var.azure_tenant_id
azure_availability_zone = "1"
}
module "region_a_cns_nva_dedicated_host_01" {
source = "./modules/dedicated_host/"
azure_client_id = var.azure_client_id
azure_client_secret = var.azure_client_secret
azure_cns_dedicated_host_group_id = module.region_a_dedicated_host_group_01.dedicated_host_group_id
azure_cns_dedicated_host_name = join ("", list(var.azure_cns_nva_dedicated_hostname, "-01", ".", var.azure_pod_a, ".service-now.com"))
azure_cns_dedicated_host_sku = var.azure_cns_nva_dedicated_host_sku
azure_environment = var.azure_environment
azure_fault_domain = "0"
azure_location = var.azure_location_region_a
azure_subscription_id = var.azure_subscription_id
azure_tenant_id = var.azure_tenant_id
}
module "cisco_asa_nva_region_a" {
source = "./modules/azure_network_asa_nva"
azure_asa1_hostname = var.asa1_hostname
azure_asa1_management_offset = var.asa1_management_offset
azure_asa1_storage_account_name = var.asa1_storage_account_name
azure_asa2_hostname = var.asa2_hostname
azure_asa2_management_offset = var.asa2_management_offset
azure_asa2_storage_account_name = var.asa2_storage_account_name
azure_asa_hostname = var.asa_hostname
azure_asa_inside_subnet_id = module.infrastructure.subnet_id_03
azure_asa_inside_subnet_prefix = module.infrastructure.subnet_address_space_03_0
azure_asa_management_subnet_id = module.infrastructure.subnet_id_01
azure_asa_management_subnet_prefix = module.infrastructure.subnet_address_space_01_0
azure_asa_offer = var.asa_offer
azure_asa_outside_subnet_id = module.infrastructure.subnet_id_02
azure_asa_outside_subnet_prefix = module.infrastructure.subnet_address_space_02_0
azure_asa_publisher = var.asa_publisher
azure_asa_sku = var.asa_sku
azure_asa_vm_size = var.asa_vm_size
azure_client_id = var.azure_client_id
azure_client_secret = var.azure_client_secret
azure_environment = var.azure_environment
azure_infra_pod = var.infra_pod_a
azure_location = module.infrastructure.resource_group_location
# azure_ptr_zone = var.azure_ptr_zone_region_a
azure_rg = module.infrastructure.resource_group_name
azure_subscription_id = var.azure_subscription_id
azure_tenant_id = var.azure_tenant_id
azure_uhoh_secret = var.azure_uhoh_secret
# dns_key_name = var.dns_key_name
# dns_key_secret = var.dns_key_secret
# dns_server_address = var.dns_server_address
# asa1_zone = try(module.region_a_dedicated_host_group_01.dedicated_host_group_zone[0] , null)
# asa2_zone = try(module.region_a_dedicated_host_group_02.dedicated_host_group_zone[0] , null)
asa1_zone = var.use_dh ? module.region_a_dedicated_host_group_01.dedicated_host_group_zone : null
asa2_zone = var.use_dh ? module.region_a_dedicated_host_group_02.dedicated_host_group_zone : null
dedicated_host_id_01 = var.use_dh ? module.region_a_cns_nva_dedicated_host_01.dedicated_host_id : null
dedicated_host_id_02 = var.use_dh ? module.region_a_cns_nva_dedicated_host_02.dedicated_host_id : null
}
I have an infrastructure module that builds the RG/vNets/Subnets/NSGs, etc.
So I run a terraform apply -target=module.infrastructure
to build that…
If I then run the Cisco module with terraform apply -target=module.cisco_asa_nva_region_a
Since the cisco_asa_nva_region_a
module uses a module output to define a variable, it runs the DHG and DH modules.
It runs those modules with var.use_dh
set to true
or false
. When set to true
, the ASAs are built on the DHs. When set to false
, the DHs are built and the ASAs are built as stand alone instances.
Sorry for the large font. Those are commented out in the code… I guess a pound sign means bold here…
I think I understand the intent now.
To rephrase, given:
- Module A
- Module B with an optional dependency on A, via passing a variable from A
The goal is to only instantiate A if B will use it.
In that case, no, try() and ?: will not help.
Prior to terraform 0.13, the techniques that occur to me are
- generate the configuration, possibly copying from an external source
- conditionally point module A’s source to a dummy module that accepts the same inputs but does nothing. This seems to require rerunning
terraform init
each time a module’s source location changes.
module "A" {
source = var.use_A ? "./module_A" : "./module_A_dummy"
}
With terraform 0.13, setting count
to 0 will work. Using count (or for_each) likely means making changes to deal with the instance index and relocating nested providers.
Hey Jeremy,
I think I’m getting it. And in module_a_dummy, I would need to get the output to be null?
output “dedicated_host_id” {
value = null
}
Thanks,
Chris
That could work. Having the dummy supply a null output would eliminate the need for try() when used with the other module.