oracle-terraform-modules / terraform-oci-oke

The Terraform OKE Module Installer for Oracle Cloud Infrastructure provides a Terraform module that provisions the necessary resources for Oracle Container Engine.
https://oracle-terraform-modules.github.io/terraform-oci-oke/
Universal Permissive License v1.0
148 stars 203 forks source link

Subnets evaluation during terraform plan phase. #935

Open VioletCranberry opened 1 month ago

VioletCranberry commented 1 month ago

It looks like parsing of locals variables https://github.com/oracle-terraform-modules/terraform-oci-oke/blob/02aeaebbf7bcade5b01e5002c056410a214b03dd/modules/network/subnets.tf#L53 and subsequent https://github.com/oracle-terraform-modules/terraform-oci-oke/blob/02aeaebbf7bcade5b01e5002c056410a214b03dd/modules/network/subnets.tf#L82 https://github.com/oracle-terraform-modules/terraform-oci-oke/blob/02aeaebbf7bcade5b01e5002c056410a214b03dd/modules/network/subnets.tf#L102 https://github.com/oracle-terraform-modules/terraform-oci-oke/blob/02aeaebbf7bcade5b01e5002c056410a214b03dd/modules/network/subnets.tf#L123 is not working as expected.

I am planning to run the cluster on my own subnets and VPC, expecting the resources to be provisioned within these subnets. Terraform cannot fully evaluate mentioned variables during the plan phase, leading to errors.

Module settings:

create_operator        = false
create_bastion         = false
load_balancers         = "both" (does not really matter in this scenario). 

Terraform Version and Provider Version

❯ terraform --version
Terraform v1.5.7
on darwin_arm64
+ provider registry.terraform.io/hashicorp/cloudinit v2.3.4
+ provider registry.terraform.io/hashicorp/helm v2.14.0
+ provider registry.terraform.io/hashicorp/http v3.4.3
+ provider registry.terraform.io/hashicorp/null v3.2.2
+ provider registry.terraform.io/hashicorp/random v3.6.2
+ provider registry.terraform.io/hashicorp/time v0.11.2
+ provider registry.terraform.io/oracle/oci v5.46.0

Terraform Configuration Files

Subnets are to be created as a separate cloud resources during terraform apply run.

1st scenario - following the example here https://oracle-terraform-modules.github.io/terraform-oci-oke/guide/network_subnets.html (Use existing subnets)
subnets = {
    cp       = { id = oci_core_subnet.cp.id }
    int_lb   = { id = oci_core_subnet.int_lb.id }
    pub_lb   = { id = oci_core_subnet.pub_lb.id }
    workers  = { id = oci_core_subnet.workers.id }
    pods     = { id = oci_core_subnet.pods.id }
 }

2nd scenario - explicitely denying subnets' creation:
subnets = {
    cp       = { create = "never", id = oci_core_subnet.cp.id }
    int_lb   = { create = "never", id = oci_core_subnet.int_lb.id }
    pub_lb   = { create = "never", id = oci_core_subnet.pub_lb.id }
    workers  = { create = "never", id = oci_core_subnet.workers.id }
    pods     = { create = "never", id = oci_core_subnet.pods.id }
  }

Expected Behaviour

Second scenario:

  Error: Invalid for_each argument
│ 
│   on .terraform/modules/oke/modules/network/subnets.tf line 148, in resource "oci_core_security_list" "oke":
│  148:   for_each = {
│  149:     for k, v in local.subnets_to_create : k => v
│  150:     if tobool(lookup(v, "create_seclist", false))
│  151:   }
│     ├────────────────
│     │ local.subnets_to_create will be known only after apply
│ 
│ The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will
│ identify the instances of this resource.
│ 
│ When working with unknown values in for_each, it's better to define the map keys statically in your configuration and place apply-time results only in the map values.
│ 
│ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully
│ converge.

First scenario:

│ Error: Invalid count argument
│ 
│   on .terraform/modules/oke/modules/network/subnets.tf line 103, in resource "null_resource" "validate_subnets":
│  103:   count = anytrue([for k, v in local.subnet_cidrs_new : contains(["netnum", "newbits", "cidr"], v.type)
│  104:     if lookup(v, "create", "auto") != "never"
│  105:   ]) ? 1 : 0
│ 
│ 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.

+ the error from the second scenario.

Actual Behaviour

terraform plan errors, creating cluster is blocked. VCN and subnets are about to be created successfully.

Steps to Reproduce

Start from scratch, define own VCN and subnets resources, consume IDs with the module.

hyder commented 1 month ago

Hi, Thanks for logging this issue. Are you running apply by module?

VioletCranberry commented 1 month ago

Hi @hyder what do you mean?

hyder commented 1 month ago

I mean are you using terraform targets? e.g. terraform apply --target=module.network ..

Or just a straightforward terraform apply?

VioletCranberry commented 1 month ago

Ah got it.

This issue involves a straightforward single Terraform run where the subnet resources have yet to be created. The module then uses the ID attributes of these resources. This results in errors during the terraform plan/apply phases since Terraform cannot fully evaluate the mentioned variables until terraform apply is run for the subnets only.

Using --target in Terraform should generally be avoided, as it circumvents Terraform’s dependency graph. This can lead to incomplete or inconsistent infrastructure states. It’s preferable not to rely on targeted applies merely to use a module.

hyder commented 1 month ago

I've tested both of your scenarios and they work. When you are providing the values:

subnets = {
    cp       = { id = oci_core_subnet.cp.id }
    int_lb   = { id = oci_core_subnet.int_lb.id }
    pub_lb   = { id = oci_core_subnet.pub_lb.id }
    workers  = { id = oci_core_subnet.workers.id }
    pods     = { id = oci_core_subnet.pods.id }
 }

are you providing actual ocids, or just like the above? The ids have to be actual ocids strings within ""

alcampag commented 3 weeks ago

I am having the same issue too, specifying the ids generates:

╷
│ Error: Invalid for_each argument
│ 
│   on .terraform/modules/oke/modules/network/subnets.tf line 148, in resource "oci_core_security_list" "oke":
│  148:   for_each = {
│  149:     for k, v in local.subnets_to_create : k => v
│  150:     if tobool(lookup(v, "create_seclist", false))
│  151:   }
│     ├────────────────
│     │ local.subnets_to_create will be known only after apply
│ 
│ The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.
│ 
│ When working with unknown values in for_each, it's better to define the map keys statically in your configuration and place apply-time results only in the map values.
│ 
│ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge.

Note that I am creating the subnet through a standard terraform module, then I am creating the OKE cluster. Also, the first apply actually generates the infrastructure, then subsequent plan/apply fail with this error.