Conditionally create a resource based on a variable being non-null

Hi,

I am trying to create a module for deploying workloads to AWS Fargate. The modul shall support optional configuration of AWS Service Discovery. For this, the module has a variable service_discovery_dns_namespace_id. Depending on whether the variable is null or not, a resource of type aws_service_discovery_service shall be created.

variable "service_discovery_dns_namespace_id" {
  type        = string
  nullable    = true
  default     = null
}

resource "aws_service_discovery_service" "main" {
  count = var.service_discovery_dns_namespace_id != null ? 1 : 0

  [...]
}

The Service Discovery DNS Namespace is created outside of the module, such that it can be reused. The relevant parts of calling the module looks like this:

resource "aws_service_discovery_private_dns_namespace" "main" {
  name = "${var.environment}.local"
  vpc  = var.vpc_id
}

module "fargate_service_agent" {
  source = "../fargate_service"

  [...]
  service_discovery_dns_namespace_id = aws_service_discovery_private_dns_namespace.main.id
}

When running terraform plan, this results in the following output:

  # module.XXXXXX.aws_service_discovery_private_dns_namespace.main will be created
  + resource "aws_service_discovery_private_dns_namespace" "main" {
      + arn         = (known after apply)
      + hosted_zone = (known after apply)
      + id          = (known after apply)
      + name        = "dev.local"
      + tags_all    = {
          + "environment" = "dev"
        }
      + vpc         = "vpc-XXXXXXXXXXXX"
    }

[...]

The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.

How does Terraform not know that the variable service_discovery_dns_namespace_id is non-null in this case? The resource aws_service_discovery_private_dns_namespace is clearly being defined exactly once and the plan intends to create it. The attribute id is a non-nullable string and passed directly to the module.

All help would be greatly appreciated.

Hi @christopher.klinge,

In current versions of Terraform (up to 1.5) there is no concept of “unknown but definitely not null”: if a value is unknown then Terraform can make no assumptions about it.

In forthcoming Terraform 1.6 there will be a concept of “unknown but definitely not null”, which is a first step towards making this work as you expected. However, it’s not yet sufficient: there will also need to be a mechanism for a provider to return “unknown but definitely not null” as part of its planning response, and then this particular resource type will need to be updated to use that mechanism to report that this attribute will never be null. Those other changes will be on the provider side of the protocol rather than the Terraform Core side, so providers will probably gradually adopt it over time once

In the meantime you can get a similar effect by separating the value whose nullness will decide the count from the actual id value. Here’s one way to do that:

variable "service_discovery_dns_namespace" {
  type = {
    id = string
  }
  nullable    = true
  default     = null
}

resource "aws_service_discovery_service" "main" {
  count = var.service_discovery_dns_namespace != null ? 1 : 0

  # ...
}

In this example, the input variable is an object which requires an id attribute. The nullness of the whole object decides the instance count. The value of id can be unknown without creating uncertainty about whether the surrounding object might be null.

Hi,

I am somewhat confused, because we use the same setup in a different module. Does Terraform differentiate between a variable not being provided at all and it being set to a value that might be null? The other module standardizes the creation of matching AWS egress and ingress security rules. It supports a special case where the target is not a security group but a prefix list. In that case, no ingress rule will be created.

We use this module both ways plenty of times and did not encounter this limitation. The only difference I see is that the respective other variable is omitted entirely.

variable "peer_security_group_id" {
  type        = string
  nullable    = true
  default     = null
}

variable "peer_prefix_list_id" {
  type        = string
  nullable    = true
  default     = null
}

resource "aws_vpc_security_group_ingress_rule" "in" {
  count = var.peer_prefix_list_id == null ? 1 : 0

  [...]
}

Hi @christopher.klinge,

This new example you showed would fail in the same way as your previous example if var.peer_prefix_list_id were an unknown value, and so I’d conclude that the value of that variable must have been already known at the time you first added that resource, and so Terraform was able to decide the count value unambiguously.