Delay provider configuration before passing provider to the child module

Hello and good day,

I am trying to understand how provider passing between root and child modules work in terraform.

This is my simple project structure:

./
├── docker
│   ├── main.tf
│   └── variables.tf
├── main.tf
└── variables.tf

I have my root/base module in ./main.tf , and I am calling another module located in ./docker/main.tf.

In my base module I try to create and provision a simple DigitalOcean droplet, and then from my child module I try to create a new docker network using the Docker provider.

Here is how my base module ./main.tf looks like:

provider "docker" {
  alias = "docker_config"
  host  = digitalocean_droplet.server.ipv4_address
  ssh_opts = [
    "-o StrictHostKeyChecking=no",
    "-o UserKnownHostsFile=/dev/null",
    "-o LogLevel=ERROR",
  ]
}

resource "digitalocean_droplet" "server" {
  image = "ubuntu-20-04-x64"
  name  = "server"
  region = "nyc3"
  size = "s-1vcpu-1gb"
  # Make sure docker is installed via user_data
  user_data = "apt-get update && apt-get install -y docker.io"
}

resource "null_resource" "wait_for_droplet" {
  depends_on = [digitalocean_droplet.server]
}

# call the docker module and pass the provider configuration with the droplet ip address as host
module "docker" {
  source = "./docker"
  depends_on = [digitalocean_droplet.server]
  providers = {
   docker = docker.docker_config
   }
}

And here is how the child module ./docker/main.tf looks like:

resource "docker_network" "test" {
  name = "test"
}

The above code is valid via terraform validate however when I try to apply the configuration, I get the following (expected) error:

╷
│ Error: Error initializing Docker client: unable to parse docker host ``
│ 
│   with provider["registry.terraform.io/kreuzwerker/docker"].docker_config,
│   on main.tf line 18, in provider "docker":
│   18: provider "docker" {
│ 
╵

The provider configuration for docker fails as the host IP address is not available just yet until I the configuration is applied and the DigitalOcean droplet is created.

I tried to remove the provider configuration from the base ./main.tf module and tried to move all the provider configuration in the module block as follows:

module "docker" {
  source = "./docker"
  depends_on = [digitalocean_droplet.server]
  providers = {
   docker = {
     host  = digitalocean_droplet.server.ipv4_address
     ssh_opts = [
       "-o StrictHostKeyChecking=no",
       "-o UserKnownHostsFile=/dev/null",
       "-o LogLevel=ERROR",
     ]
   }
   }
}

However then the code is no longer valid.

TL;DR, I would like to call the docker child module only after the DigitalOcean droplet is created, so I can pass the IP address of the droplet to the docker provider, so that terraform can communicate with the docker daemon on the server.

Any help is super appreciated.

Thanks.

Terraform fundamentally does not support this. That is because the architecture is to perform a “plan” first, and only then optionally “apply” the plan.

Since in your setup, the docker portions cannot be planned until the droplet apply has finished, it is necessary that you split apart your droplet and docker configuration into two separate Terraform configurations, in which you can invoke separate terraform runs.

Thanks a lot for your answer, that makes sense. I will then use another terraform configuration and not a module for this configuration.

Cheers.