Skip to content

Creating Resources with Terraform (TF)Β€

Basic Mechanics

RequirementsΒ€

  • SSH key pair, authorized in DO
  • Valid API key, e.g. available via pass
  • terraform executable locally on your system

$ ls ~/.ssh/id_rsa* | grep terra
/home/gk/.ssh/id_rsa_terraform
/home/gk/.ssh/id_rsa_terraform.pub
$ pass show DO/pat | head -c 8
7ba23738
$ terraform -h | head
Usage: terraform [global options] <subcommand> [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration

InitializingΒ€

Then configure basic vars:

$ export D="$DT_PROJECT_ROOT/tmp/clusters/DO/basic"
$ export TF_VAR_do_token="$(pass show DO/pat)"
$ alias tf=terraform
$ mkdir -p "$D"
$ cd "$D"

Just go sure we are clean on DO and locally, at start of this tutorial:

$ tf destroy -auto-approve -lock=false
$ rm -rf *

$ tf destroy -auto-approve -lock=false

Changes to Outputs:

You can apply this plan to save these new output values to the Terraform state, 
without changing any real infrastructure.

Destroy complete! Resources: 0 destroyed.                                       
$
$ rm -rf *

Create a file for DO provider:

$ cat provider.tf
terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
  }
}

data "digitalocean_ssh_key" "terraform" {
      name = "terraform"
}
provider "digitalocean" {
    token = var.do_token
}


variable "do_token" {
    // have run: export TF_VAR_do_token=`pass show DO/pat`
    // cli also possible: -var=do_token=...
}

variable "pvt_key" {
  default = "~/.ssh/id_rsa_terraform"
}

variable "master_size" {
  default = "4gb"
}

variable "node_size" {
  default = "4gb"
}

variable "region" {
  default = "fra1"
}

Provider: This encapsulates the translation from the Terraform definition to the DigitalOcean API.

Variables

  • are defined like that to allow further attributes for the terraform UI, e.g. sensitivity, type.
  • are "doing nothing" - they just provide static constants for functions within .tf files, throughout the directory (see also var.pvt_key below)
  • usually defined in an own file or even hierarchy of files

The provider file allows to init terraform now:

$ tf init
$ tree -lta

$ tf init

Initializing the backend...

Initializing provider plugins...        
- Finding digitalocean/digitalocean versions matching "~> 2.0"...               
- Installing digitalocean/digitalocean v2.10.1...                               
- Installed digitalocean/digitalocean v2.10.1 (signed by a HashiCorp partner, key ID F82037E524B9C0E8)

Partner and community providers are signed by their developers.                 
If you'd like to know more about provider signing, you can read about it here:  
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider    
selections it made above. Include this file in your version control repository  
so that Terraform can guarantee to make the same selections by default when     
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see   
any changes that are required for your infrastructure. All Terraform commands   
should now work.

If you ever set or change modules or backend configuration for Terraform,       
rerun this command to reinitialize your working directory. If you forget, other 
commands will detect it and remind you to do so if necessary.
$ tree -lta   
.                   
β”œβ”€β”€ provider.tf     
β”œβ”€β”€ .terraform      
β”‚Β Β  └── providers   
β”‚Β Β      └── registry.terraform.io       
β”‚Β Β          └── digitalocean            
β”‚Β Β              └── digitalocean        
β”‚Β Β                  └── 2.10.1          
β”‚Β Β                      └── linux_amd64 
β”‚Β Β                          β”œβ”€β”€ CHANGELOG.md                                    
β”‚Β Β                          β”œβ”€β”€ LICENSE 
β”‚Β Β                          β”œβ”€β”€ README.md                                       
β”‚Β Β                          └── terraform-provider-digitalocean_v2.10.1         
└── .terraform.lock.hcl

7 directories, 6 files

ConfigurationΒ€

Worker NodesΒ€

Create two more files for worker nodes:

$ cat www-1.tf
resource "digitalocean_droplet" "www-1" {
  image = "ubuntu-20-04-x64"
  name = "www-1"
  region = "fra1"
  size = "s-1vcpu-1gb"
  private_networking = true
  ssh_keys = [
    data.digitalocean_ssh_key.terraform.id
  ]

  connection {
    host = self.ipv4_address
    user = "root"
    type = "ssh"
    private_key = file(var.pvt_key)
    timeout = "2m"
  }

  provisioner "remote-exec" {
    inline = [
      "export PATH=$PATH:/usr/bin",
      "sudo apt -qq update",
      "sudo apt -qq install -yq -o Dpkg::Use-Pty=0 nginx"
    ]
  }
}

Second worker:

$ sed 's/www-1/www-2/g' www-1.tf > www-2.tf

$ sed 's/www-1/www-2/g' www-1.tf > www-2.tf

Load BalancerΒ€

Also we configure a loadbalancer. Here you see cloud provider specific features of TF:

$ cat << EOF > loadbalancer.tf
resource "digitalocean_loadbalancer" "www-lb-gk-tftest" {
  name = "www-lb-gk-tftest"
  region = "fra1"

  forwarding_rule {
    entry_port = 80
    entry_protocol = "http"

    target_port = 80
    target_protocol = "http"
  }

  healthcheck {
    port = 80 
    protocol = "tcp"
  }

  droplet_ids = [digitalocean_droplet.www-1.id, digitalocean_droplet.www-2.id ]
}
EOF

$ cat << EOF > loadbalancer.tf          
> resource "digitalocean_loadbalancer" "www-lb-gk-tftest" {                     
>   name = "www-lb-gk-tftest"           
>   region = "fra1" 
>                   
>   forwarding_rule {                   
>     entry_port = 80                   
>     entry_protocol = "http"           
>                   
>     target_port = 80                  
>     target_protocol = "http"          
>   }               
>                   
>   healthcheck {   
>     port = 80     
>     protocol = "tcp"                  
>   }               
>                   
>   droplet_ids = [digitalocean_droplet.www-1.id, digitalocean_droplet.www-2.id ]                   
> }                 
> EOF               
$

Outputs DemoΒ€

This way we can information to the tf apply output, ready for others to consume:

$ cat <<'ENDL' > outputs.tf
output "ip1" {
    description = "The Droplet www-1 ipv4 address"
    value = "${digitalocean_droplet.www-1.ipv4_address}"
}

output "ip2" {
    description = "The Droplet www-2 ipv4 address"
    value = "${digitalocean_droplet.www-2.ipv4_address}"
}

ENDL

$ cat <<'ENDL' > outputs.tf             
> output "ip1" {    
>     description = "The Droplet www-1 ipv4 address"                            
>     value = "${digitalocean_droplet.www-1.ipv4_address}"                      
> }                 
>                   
> output "ip2" {    
>     description = "The Droplet www-2 ipv4 address"                            
>     value = "${digitalocean_droplet.www-2.ipv4_address}"                      
> }                 
>                   
> ENDL              
$

Plan & ApplyΒ€

tf plan shows current state:

$ tf plan

$ tf plan

Terraform used the selected providers to generate the following execution plan. 
Resource actions are indicated with the following symbols:                      
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.www-1 will be created                                  
  + resource "digitalocean_droplet" "www-1" {                                   
      + backups              = false    
      + created_at           = (known after apply)                              
      + disk                 = (known after apply)                              
      + id                   = (known after apply)                              
      + image                = "ubuntu-20-04-x64"                               
      + ipv4_address         = (known after apply)                              
      + ipv4_address_private = (known after apply)                              
      + ipv6                 = false    
      + ipv6_address         = (known after apply)                              
      + locked               = (known after apply)                              
      + memory               = (known after apply)                              
      + monitoring           = false    
      + name                 = "www-1"  
      + price_hourly         = (known after apply)                              
      + price_monthly        = (known after apply)                              
      + private_networking   = true     
      + region               = "fra1"   
      + resize_disk          = true     
      + size                 = "s-1vcpu-1gb"                                    
      + ssh_keys             = [        
          + "30935145",                 
        ]           
      + status               = (known after apply)                              
      + urn                  = (known after apply)                              
      + vcpus                = (known after apply)                              
      + volume_ids           = (known after apply)                              
      + vpc_uuid             = (known after apply)                              
    }

  # digitalocean_droplet.www-2 will be created                                  
  + resource "digitalocean_droplet" "www-2" {                                   
      + backups              = false    
      + created_at           = (known after apply)                              
      + disk                 = (known after apply)                              
      + id                   = (known after apply)                              
      + image                = "ubuntu-20-04-x64"                               
      + ipv4_address         = (known after apply)                              
      + ipv4_address_private = (known after apply)                              
      + ipv6                 = false    
      + ipv6_address         = (known after apply)                              
      + locked               = (known after apply)                              
      + memory               = (known after apply)                              
      + monitoring           = false    
      + name                 = "www-2"  
      + price_hourly         = (known after apply)                              
      + price_monthly        = (known after apply)                              
      + private_networking   = true     
      + region               = "fra1"   
      + resize_disk          = true     
      + size                 = "s-1vcpu-1gb"                                    
      + ssh_keys             = [        
          + "30935145",                 
        ]           
      + status               = (known after apply)                              
      + urn                  = (known after apply)                              
      + vcpus                = (known after apply)                              
      + volume_ids           = (known after apply)                              
      + vpc_uuid             = (known after apply)                              
    }

  # digitalocean_loadbalancer.www-lb-gk-tftest will be created                  
  + resource "digitalocean_loadbalancer" "www-lb-gk-tftest" {                   
      + algorithm                = "round_robin"                                
      + droplet_ids              = (known after apply)                          
      + enable_backend_keepalive = false
      + enable_proxy_protocol    = false
      + id                       = (known after apply)                          
      + ip                       = (known after apply)                          
      + name                     = "www-lb-gk-tftest"                           
      + redirect_http_to_https   = false
      + region                   = "fra1"                                       
      + size                     = "lb-small"                                   
      + status                   = (known after apply)                          
      + urn                      = (known after apply)                          
      + vpc_uuid                 = (known after apply)

      + forwarding_rule {               
          + certificate_id   = (known after apply)                              
          + certificate_name = (known after apply)                              
          + entry_port       = 80       
          + entry_protocol   = "http"   
          + target_port      = 80       
          + target_protocol  = "http"   
          + tls_passthrough  = false    
        }

      + healthcheck {                   
          + check_interval_seconds   = 10                                       
          + healthy_threshold        = 5
          + port                     = 80                                       
          + protocol                 = "tcp"                                    
          + response_timeout_seconds = 5
          + unhealthy_threshold      = 3
        }

      + sticky_sessions {               
          + cookie_name        = (known after apply)                            
          + cookie_ttl_seconds = (known after apply)                            
          + type               = (known after apply)                            
        }           
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs: 
  + ip1 = (known after apply)           
  + ip2 = (known after apply)

───────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't      
guarantee to take exactly these actions if you run "terraform apply" now.

Ready to apply, that command will now create the nodes (takes a minute):

$ time tf apply -auto-approve

$ time tf apply -auto-approve

Terraform used the selected providers to generate the following execution plan. 
Resource actions are indicated with the following symbols:                      
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.www-1 will be created                                  
  + resource "digitalocean_droplet" "www-1" {                                   
      + backups              = false    
      + created_at           = (known after apply)                              
      + disk                 = (known after apply)                              
      + id                   = (known after apply)                              
      + image                = "ubuntu-20-04-x64"                               
      + ipv4_address         = (known after apply)                              
      + ipv4_address_private = (known after apply)                              
      + ipv6                 = false    
      + ipv6_address         = (known after apply)                              
      + locked               = (known after apply)                              
      + memory               = (known after apply)                              
      + monitoring           = false    
      + name                 = "www-1"  
      + price_hourly         = (known after apply)                              
      + price_monthly        = (known after apply)                              
      + private_networking   = true     
      + region               = "fra1"   
      + resize_disk          = true     
      + size                 = "s-1vcpu-1gb"                                    
      + ssh_keys             = [        
          + "30935145",                 
        ]           
      + status               = (known after apply)                              
      + urn                  = (known after apply)                              
      + vcpus                = (known after apply)                              
      + volume_ids           = (known after apply)                              
      + vpc_uuid             = (known after apply)                              
    }

  # digitalocean_droplet.www-2 will be created                                  
  + resource "digitalocean_droplet" "www-2" {                                   
      + backups              = false    
      + created_at           = (known after apply)                              
      + disk                 = (known after apply)                              
      + id                   = (known after apply)                              
      + image                = "ubuntu-20-04-x64"                               
      + ipv4_address         = (known after apply)                              
      + ipv4_address_private = (known after apply)                              
      + ipv6                 = false    
      + ipv6_address         = (known after apply)                              
      + locked               = (known after apply)                              
      + memory               = (known after apply)                              
      + monitoring           = false    
      + name                 = "www-2"  
      + price_hourly         = (known after apply)                              
      + price_monthly        = (known after apply)                              
      + private_networking   = true     
      + region               = "fra1"   
      + resize_disk          = true     
      + size                 = "s-1vcpu-1gb"                                    
      + ssh_keys             = [        
          + "30935145",                 
        ]           
      + status               = (known after apply)                              
      + urn                  = (known after apply)                              
      + vcpus                = (known after apply)                              
      + volume_ids           = (known after apply)                              
      + vpc_uuid             = (known after apply)                              
    }

  # digitalocean_loadbalancer.www-lb-gk-tftest will be created                  
  + resource "digitalocean_loadbalancer" "www-lb-gk-tftest" {                   
      + algorithm                = "round_robin"                                
      + droplet_ids              = (known after apply)                          
      + enable_backend_keepalive = false
      + enable_proxy_protocol    = false
      + id                       = (known after apply)                          
      + ip                       = (known after apply)                          
      + name                     = "www-lb-gk-tftest"                           
      + redirect_http_to_https   = false
      + region                   = "fra1"                                       
      + size                     = "lb-small"                                   
      + status                   = (known after apply)                          
      + urn                      = (known after apply)                          
      + vpc_uuid                 = (known after apply)

      + forwarding_rule {               
          + certificate_id   = (known after apply)                              
          + certificate_name = (known after apply)                              
          + entry_port       = 80       
          + entry_protocol   = "http"   
          + target_port      = 80       
          + target_protocol  = "http"   
          + tls_passthrough  = false    
        }

      + healthcheck {                   
          + check_interval_seconds   = 10                                       
          + healthy_threshold        = 5
          + port                     = 80                                       
          + protocol                 = "tcp"                                    
          + response_timeout_seconds = 5
          + unhealthy_threshold      = 3
        }

      + sticky_sessions {               
          + cookie_name        = (known after apply)                            
          + cookie_ttl_seconds = (known after apply)                            
          + type               = (known after apply)                            
        }           
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs: 
  + ip1 = (known after apply)           
  + ip2 = (known after apply)           
digitalocean_droplet.www-2: Creating... 
digitalocean_droplet.www-1: Creating... 
digitalocean_droplet.www-1: Still creating... [10s elapsed]                     
digitalocean_droplet.www-2: Still creating... [10s elapsed]                     
digitalocean_droplet.www-2: Still creating... [20s elapsed]                     
digitalocean_droplet.www-1: Still creating... [20s elapsed]                     
digitalocean_droplet.www-2: Still creating... [30s elapsed]                     
digitalocean_droplet.www-1: Still creating... [30s elapsed]                     
digitalocean_droplet.www-1: Provisioning with 'remote-exec'...                  
digitalocean_droplet.www-1 (remote-exec): Connecting to remote host via SSH...  
digitalocean_droplet.www-1 (remote-exec):   Host: 161.35.194.251                
digitalocean_droplet.www-1 (remote-exec):   User: root                          
digitalocean_droplet.www-1 (remote-exec):   Password: false                     
digitalocean_droplet.www-1 (remote-exec):   Private key: true                   
digitalocean_droplet.www-1 (remote-exec):   Certificate: false                  
digitalocean_droplet.www-1 (remote-exec):   SSH Agent: true                     
digitalocean_droplet.www-1 (remote-exec):   Checking Host Key: false            
digitalocean_droplet.www-1 (remote-exec):   Target Platform: unix               
digitalocean_droplet.www-2: Provisioning with 'remote-exec'...                  
digitalocean_droplet.www-2 (remote-exec): Connecting to remote host via SSH...  
digitalocean_droplet.www-2 (remote-exec):   Host: 161.35.194.255                
digitalocean_droplet.www-2 (remote-exec):   User: root                          
digitalocean_droplet.www-2 (remote-exec):   Password: false                     
digitalocean_droplet.www-2 (remote-exec):   Private key: true                   
digitalocean_droplet.www-2 (remote-exec):   Certificate: false                  
digitalocean_droplet.www-2 (remote-exec):   SSH Agent: true                     
digitalocean_droplet.www-2 (remote-exec):   Checking Host Key: false            
digitalocean_droplet.www-2 (remote-exec):   Target Platform: unix               
digitalocean_droplet.www-2: Still creating... [40s elapsed]                     
digitalocean_droplet.www-1: Still creating... [40s elapsed]                     
digitalocean_droplet.www-1 (remote-exec): Connecting to remote host via SSH...  
digitalocean_droplet.www-1 (remote-exec):   Host: 161.35.194.251                
digitalocean_droplet.www-1 (remote-exec):   User: root                          
digitalocean_droplet.www-1 (remote-exec):   Password: false                     
digitalocean_droplet.www-1 (remote-exec):   Private key: true                   
digitalocean_droplet.www-1 (remote-exec):   Certificate: false                  
digitalocean_droplet.www-1 (remote-exec):   SSH Agent: true                     
digitalocean_droplet.www-1 (remote-exec):   Checking Host Key: false            
digitalocean_droplet.www-1 (remote-exec):   Target Platform: unix               
digitalocean_droplet.www-2 (remote-exec): Connecting to remote host via SSH...  
digitalocean_droplet.www-2 (remote-exec):   Host: 161.35.194.255                
digitalocean_droplet.www-2 (remote-exec):   User: root                          
digitalocean_droplet.www-2 (remote-exec):   Password: false                     
digitalocean_droplet.www-2 (remote-exec):   Private key: true                   
digitalocean_droplet.www-2 (remote-exec):   Certificate: false                  
digitalocean_droplet.www-2 (remote-exec):   SSH Agent: true                     
digitalocean_droplet.www-2 (remote-exec):   Checking Host Key: false            
digitalocean_droplet.www-2 (remote-exec):   Target Platform: unix               
digitalocean_droplet.www-1: Still creating... [50s elapsed]                     
digitalocean_droplet.www-2: Still creating... [50s elapsed]                     
digitalocean_droplet.www-1 (remote-exec): Connecting to remote host via SSH...  
digitalocean_droplet.www-1 (remote-exec):   Host: 161.35.194.251                
digitalocean_droplet.www-1 (remote-exec):   User: root                          
digitalocean_droplet.www-1 (remote-exec):   Password: false                     
digitalocean_droplet.www-1 (remote-exec):   Private key: true                   
digitalocean_droplet.www-1 (remote-exec):   Certificate: false                  
digitalocean_droplet.www-1 (remote-exec):   SSH Agent: true                     
digitalocean_droplet.www-1 (remote-exec):   Checking Host Key: false            
digitalocean_droplet.www-1 (remote-exec):   Target Platform: unix               
digitalocean_droplet.www-2 (remote-exec): Connected!                            
digitalocean_droplet.www-1 (remote-exec): Connected!                            
digitalocean_droplet.www-2: Still creating... [1m0s elapsed]                    
digitalocean_droplet.www-1: Still creating... [1m0s elapsed]                    
digitalocean_droplet.www-2 (remote-exec): Traceback (most recent call last):    
digitalocean_droplet.www-2 (remote-exec):   File "/usr/lib/cnf-update-db", line 26, in <module>     
digitalocean_droplet.www-2 (remote-exec):     col.create(db)                    
digitalocean_droplet.www-2 (remote-exec):   File "/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py", line 94, in create                             
digitalocean_droplet.www-2 (remote-exec):     self._fill_commands(con)          
digitalocean_droplet.www-2 (remote-exec):   File "/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py", line 137, in _fill_commands                    
digitalocean_droplet.www-2 (remote-exec):     with open(f) as fp:               
digitalocean_droplet.www-2 (remote-exec): FileNotFoundError: [Errno 2] No such file or directory: '/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_focal_restricted_cnf_Commands-amd64'              
digitalocean_droplet.www-2 (remote-exec): E: Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/lib/command-not-found/ -a -e /usr/lib/cnf-update-db; then /usr/lib/cnf-update-db > /dev/null; fi'             
digitalocean_droplet.www-2 (remote-exec): E: Sub-process returned an error code 
digitalocean_droplet.www-2 (remote-exec): Preconfiguring packages ...           
                                          Selecting previously unselected package fonts-dejavu-core..www-2 (remote-exec):                                       
digitalocean_droplet.www-1 (remote-exec): Traceback (most recent call last):    
digitalocean_droplet.www-1 (remote-exec):   File "/usr/lib/cnf-update-db", line 26, in <module>     
digitalocean_droplet.www-1 (remote-exec):     col.create(db)                    
digitalocean_droplet.www-1 (remote-exec):   File "/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py", line 94, in create                             
digitalocean_droplet.www-1 (remote-exec):     self._fill_commands(con)          
digitalocean_droplet.www-1 (remote-exec):   File "/usr/lib/python3/dist-packages/CommandNotFound/db/creator.py", line 137, in _fill_commands                    
digitalocean_droplet.www-1 (remote-exec):     with open(f) as fp:               
digitalocean_droplet.www-1 (remote-exec): FileNotFoundError: [Errno 2] No such file or directory: '/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_focal-backports_main_cnf_Commands-amd64'          
digitalocean_droplet.www-2 (remote-exec): (Reading database ...                 
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 5%              
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 10%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 15%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 20%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 25%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 30%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 35%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 40%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 45%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 50%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 55%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 60%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 65%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 70%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 75%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 80%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 85%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 90%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 95%             
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 100%            
digitalocean_droplet.www-2 (remote-exec): (Reading database ... 63520 files and directories currently installed.)       
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../00-fonts-dejavu-core_2.37-1_all.deb ...               
Progress: [  1%] [..................] c): Unpacking fonts-dejavu-core (2.37-1) ...                  
Progress: [  3%] [..................] c): Selecting previously unselected package fontconfig-config.                    
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../01-fontconfig-config_2.13.1-2ubuntu3_all.deb ...      
Progress: [  4%] [..................] c): Unpacking fontconfig-config (2.13.1-2ubuntu3) ...         
Progress: [  6%] [#.................] c): Selecting previously unselected package libfontconfig1:amd64.                 
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../02-libfontconfig1_2.13.1-2ubuntu3_amd64.deb ...       
Progress: [  7%] [#.................] c): Unpacking libfontconfig1:amd64 (2.13.1-2ubuntu3) ...      
Progress: [  9%] [#.................] c): Selecting previously unselected package libjpeg-turbo8:amd64.                 
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../03-libjpeg-turbo8_2.0.3-0ubuntu1.20.04.1_amd64.deb ...                                        
Progress: [ 10%] [#.................] c): Unpacking libjpeg-turbo8:amd64 (2.0.3-0ubuntu1.20.04.1) ...                   
Progress: [ 12%] [##................] c): Selecting previously unselected package libjpeg8:amd64.   
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../04-libjpeg8_8c-2ubuntu8_amd64.deb ...                 
Progress: [ 13%] [##................] c): Unpacking libjpeg8:amd64 (8c-2ubuntu8) ...                
Progress: [ 14%] [##................] c): Selecting previously unselected package libjbig0:amd64.   
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../05-libjbig0_2.1-3.1build1_amd64.deb ...               
Progress: [ 16%] [##................] c): Unpacking libjbig0:amd64 (2.1-3.1build1) ...              
Progress: [ 17%] [###...............] c): Selecting previously unselected package libwebp6:amd64.   
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../06-libwebp6_0.6.1-2ubuntu0.20.04.1_amd64.deb ...      
Progress: [ 19%] [###...............] c): Unpacking libwebp6:amd64 (0.6.1-2ubuntu0.20.04.1) ...     
Progress: [ 20%] [###...............] c): Selecting previously unselected package libtiff5:amd64.   
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../07-libtiff5_4.1.0+git191117-2ubuntu0.20.04.1_amd64.deb ...                                    
Progress: [ 22%] [###...............] c): Unpacking libtiff5:amd64 (4.1.0+git191117-2ubuntu0.20.04.1) ...               
Progress: [ 23%] [####..............] c): Selecting previously unselected package libxpm4:amd64.    
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../08-libxpm4_1%3a3.5.12-1_amd64.deb ...                 
Progress: [ 25%] [####..............] c): Unpacking libxpm4:amd64 (1:3.5.12-1) ...                  
Progress: [ 26%] [####..............] c): Selecting previously unselected package libgd3:amd64.     
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../09-libgd3_2.2.5-5.2ubuntu2_amd64.deb ...              
Progress: [ 28%] [####..............] c): Unpacking libgd3:amd64 (2.2.5-5.2ubuntu2) ...             
Progress: [ 29%] [#####.............] c): Selecting previously unselected package nginx-common.     
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../10-nginx-common_1.18.0-0ubuntu1.2_all.deb ...         
Progress: [ 30%] [#####.............] c): Unpacking nginx-common (1.18.0-0ubuntu1.2) ...            
digitalocean_droplet.www-1 (remote-exec): E: Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/lib/command-not-found/ -a -e /usr/lib/cnf-update-db; then /usr/lib/cnf-update-db > /dev/null; fi'             
Progress: [ 32%] [#####.............] c): Selecting previously unselected package libnginx-mod-http-image-filter.       
digitalocean_droplet.www-1 (remote-exec): E: Sub-process returned an error code 
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../11-libnginx-mod-http-image-filter_1.18.0-0ubuntu1.2_amd64.deb ...                             
Progress: [ 33%] [#####.............] c): Unpacking libnginx-mod-http-image-filter (1.18.0-0ubuntu1.2) ...              
Progress: [ 35%] [######............] c): Selecting previously unselected package libnginx-mod-http-xslt-filter.        
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../12-libnginx-mod-http-xslt-filter_1.18.0-0ubuntu1.2_amd64.deb ...                              
Progress: [ 36%] [######............] c): Unpacking libnginx-mod-http-xslt-filter (1.18.0-0ubuntu1.2) ...               
Progress: [ 38%] [######............] c): Selecting previously unselected package libnginx-mod-mail.                    
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../13-libnginx-mod-mail_1.18.0-0ubuntu1.2_amd64.deb ...  
Progress: [ 39%] [#######...........] c): Unpacking libnginx-mod-mail (1.18.0-0ubuntu1.2) ...       
Progress: [ 41%] [#######...........] c): Selecting previously unselected package libnginx-mod-stream.                  
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../14-libnginx-mod-stream_1.18.0-0ubuntu1.2_amd64.deb ...                                        
Progress: [ 42%] [#######...........] c): Unpacking libnginx-mod-stream (1.18.0-0ubuntu1.2) ...     
Progress: [ 43%] [#######...........] c): Selecting previously unselected package nginx-core.       
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../15-nginx-core_1.18.0-0ubuntu1.2_amd64.deb ...         
Progress: [ 45%] [########..........] c): Unpacking nginx-core (1.18.0-0ubuntu1.2) ...              
Progress: [ 46%] [########..........] c): Selecting previously unselected package nginx.            
digitalocean_droplet.www-2 (remote-exec): Preparing to unpack .../16-nginx_1.18.0-0ubuntu1.2_all.deb ...                
Progress: [ 48%] [########..........] c): Unpacking nginx (1.18.0-0ubuntu1.2) ...                   
Progress: [ 49%] [########..........] c): Setting up libxpm4:amd64 (1:3.5.12-1) ...                 
Progress: [ 52%] [#########.........] c): Setting up nginx-common (1.18.0-0ubuntu1.2) ...           
Progress: [ 54%] [#########.........] c):                                       
digitalocean_droplet.www-2 (remote-exec): Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service β†’ /lib/systemd/system/nginx.service.        
digitalocean_droplet.www-1: Still creating... [1m10s elapsed]                   
digitalocean_droplet.www-2: Still creating... [1m10s elapsed]                   
Progress: [ 55%] [#########.........] c): Setting up libjbig0:amd64 (2.1-3.1build1) ...             
Progress: [ 58%] [##########........] c): Setting up libnginx-mod-http-xslt-filter (1.18.0-0ubuntu1.2) ...              
Progress: [ 61%] [##########........] c): Setting up libwebp6:amd64 (0.6.1-2ubuntu0.20.04.1) ...    
Progress: [ 64%] [###########.......] c): Setting up fonts-dejavu-core (2.37-1) ...                 
Progress: [ 67%] [###########.......] c): Setting up libjpeg-turbo8:amd64 (2.0.3-0ubuntu1.20.04.1) ...                  
Progress: [ 70%] [############......] c): Setting up libjpeg8:amd64 (8c-2ubuntu8) ...               
Progress: [ 72%] [#############.....] c): Setting up libnginx-mod-mail (1.18.0-0ubuntu1.2) ...      
Progress: [ 75%] [#############.....] c): Setting up fontconfig-config (2.13.1-2ubuntu3) ...        
Progress: [ 78%] [##############....] c): Setting up libnginx-mod-stream (1.18.0-0ubuntu1.2) ...    
Progress: [ 81%] [##############....] c): Setting up libtiff5:amd64 (4.1.0+git191117-2ubuntu0.20.04.1) ...              
Progress: [ 84%] [###############...] c): Setting up libfontconfig1:amd64 (2.13.1-2ubuntu3) ...     
Progress: [ 87%] [###############...] c): Setting up libgd3:amd64 (2.2.5-5.2ubuntu2) ...            
Progress: [ 90%] [################..] c): Setting up libnginx-mod-http-image-filter (1.18.0-0ubuntu1.2) ...             
Progress: [ 93%] [################..] c): Setting up nginx-core (1.18.0-0ubuntu1.2) ...             
Progress: [ 94%] [################..] c):                                       
Progress: [ 96%] [#################.] c): Setting up nginx (1.18.0-0ubuntu1.2) ...                  
Progress: [ 99%] [#################.] c): Processing triggers for ufw (0.36-6) ...                  
digitalocean_droplet.www-2 (remote-exec): Processing triggers for systemd (245.4-4ubuntu3.6) ...    
digitalocean_droplet.www-2 (remote-exec): Processing triggers for man-db (2.9.1-1) ...              
digitalocean_droplet.www-2 (remote-exec): Processing triggers for libc-bin (2.31-0ubuntu9.2) ...

digitalocean_droplet.www-1 (remote-exec): Preconfiguring packages ...           
                                          Selecting previously unselected package fonts-dejavu-core..www-1 (remote-exec):                                       
digitalocean_droplet.www-1 (remote-exec): (Reading database ...                 
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 5%              
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 10%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 15%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 20%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 25%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 30%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 35%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 40%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 45%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 50%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 55%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 60%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 65%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 70%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 75%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 80%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 85%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 90%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 95%             
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 100%            
digitalocean_droplet.www-1 (remote-exec): (Reading database ... 63520 files and directories currently installed.)       
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../00-fonts-dejavu-core_2.37-1_all.deb ...               
Progress: [  1%] [..................] c): Unpacking fonts-dejavu-core (2.37-1) ...                  
Progress: [  3%] [..................] c): Selecting previously unselected package fontconfig-config.                    
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../01-fontconfig-config_2.13.1-2ubuntu3_all.deb ...      
Progress: [  4%] [..................] c): Unpacking fontconfig-config (2.13.1-2ubuntu3) ...         
Progress: [  6%] [#.................] c): Selecting previously unselected package libfontconfig1:amd64.                 
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../02-libfontconfig1_2.13.1-2ubuntu3_amd64.deb ...       
Progress: [  7%] [#.................] c): Unpacking libfontconfig1:amd64 (2.13.1-2ubuntu3) ...      
Progress: [  9%] [#.................] c): Selecting previously unselected package libjpeg-turbo8:amd64.                 
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../03-libjpeg-turbo8_2.0.3-0ubuntu1.20.04.1_amd64.deb ...                                        
Progress: [ 10%] [#.................] c): Unpacking libjpeg-turbo8:amd64 (2.0.3-0ubuntu1.20.04.1) ...                   
Progress: [ 12%] [##................] c): Selecting previously unselected package libjpeg8:amd64.   
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../04-libjpeg8_8c-2ubuntu8_amd64.deb ...                 
Progress: [ 13%] [##................] c): Unpacking libjpeg8:amd64 (8c-2ubuntu8) ...                
Progress: [ 14%] [##................] c): Selecting previously unselected package libjbig0:amd64.   
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../05-libjbig0_2.1-3.1build1_amd64.deb ...               
Progress: [ 16%] [##................] c): Unpacking libjbig0:amd64 (2.1-3.1build1) ...              
Progress: [ 17%] [###...............] c): Selecting previously unselected package libwebp6:amd64.   
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../06-libwebp6_0.6.1-2ubuntu0.20.04.1_amd64.deb ...      
Progress: [ 19%] [###...............] c): Unpacking libwebp6:amd64 (0.6.1-2ubuntu0.20.04.1) ...     
Progress: [ 20%] [###...............] c): Selecting previously unselected package libtiff5:amd64.   
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../07-libtiff5_4.1.0+git191117-2ubuntu0.20.04.1_amd64.deb ...                                    
Progress: [ 22%] [###...............] c): Unpacking libtiff5:amd64 (4.1.0+git191117-2ubuntu0.20.04.1) ...               
Progress: [ 23%] [####..............] c): Selecting previously unselected package libxpm4:amd64.    
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../08-libxpm4_1%3a3.5.12-1_amd64.deb ...                 
Progress: [ 25%] [####..............] c): Unpacking libxpm4:amd64 (1:3.5.12-1) ...                  
Progress: [ 26%] [####..............] c): Selecting previously unselected package libgd3:amd64.     
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../09-libgd3_2.2.5-5.2ubuntu2_amd64.deb ...              
Progress: [ 28%] [####..............] c): Unpacking libgd3:amd64 (2.2.5-5.2ubuntu2) ...             
Progress: [ 29%] [#####.............] c): Selecting previously unselected package nginx-common.     
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../10-nginx-common_1.18.0-0ubuntu1.2_all.deb ...         
Progress: [ 30%] [#####.............] c): Unpacking nginx-common (1.18.0-0ubuntu1.2) ...            
Progress: [ 32%] [#####.............] c): Selecting previously unselected package libnginx-mod-http-image-filter.       
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../11-libnginx-mod-http-image-filter_1.18.0-0ubuntu1.2_amd64.deb ...                             
Progress: [ 33%] [#####.............] c): Unpacking libnginx-mod-http-image-filter (1.18.0-0ubuntu1.2) ...              
Progress: [ 35%] [######............] c): Selecting previously unselected package libnginx-mod-http-xslt-filter.        
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../12-libnginx-mod-http-xslt-filter_1.18.0-0ubuntu1.2_amd64.deb ...                              
Progress: [ 36%] [######............] c): Unpacking libnginx-mod-http-xslt-filter (1.18.0-0ubuntu1.2) ...               
Progress: [ 38%] [######............] c): Selecting previously unselected package libnginx-mod-mail.                    
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../13-libnginx-mod-mail_1.18.0-0ubuntu1.2_amd64.deb ...  
Progress: [ 39%] [#######...........] c): Unpacking libnginx-mod-mail (1.18.0-0ubuntu1.2) ...       
Progress: [ 41%] [#######...........] c): Selecting previously unselected package libnginx-mod-stream.                  
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../14-libnginx-mod-stream_1.18.0-0ubuntu1.2_amd64.deb ...                                        
Progress: [ 42%] [#######...........] c): Unpacking libnginx-mod-stream (1.18.0-0ubuntu1.2) ...     
Progress: [ 43%] [#######...........] c): Selecting previously unselected package nginx-core.       
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../15-nginx-core_1.18.0-0ubuntu1.2_amd64.deb ...         
Progress: [ 45%] [########..........] c): Unpacking nginx-core (1.18.0-0ubuntu1.2) ...              
Progress: [ 46%] [########..........] c): Selecting previously unselected package nginx.            
digitalocean_droplet.www-1 (remote-exec): Preparing to unpack .../16-nginx_1.18.0-0ubuntu1.2_all.deb ...                
Progress: [ 48%] [########..........] c): Unpacking nginx (1.18.0-0ubuntu1.2) ...                   
Progress: [ 49%] [########..........] c): Setting up libxpm4:amd64 (1:3.5.12-1) ...                 
Progress: [ 52%] [#########.........] c): Setting up nginx-common (1.18.0-0ubuntu1.2) ...           
Progress: [ 54%] [#########.........] c):                                       
digitalocean_droplet.www-1 (remote-exec): Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service β†’ /lib/systemd/system/nginx.service.        
Progress: [ 55%] [#########.........] c): Setting up libjbig0:amd64 (2.1-3.1build1) ...             
Progress: [ 58%] [##########........] c): Setting up libnginx-mod-http-xslt-filter (1.18.0-0ubuntu1.2) ...              
Progress: [ 61%] [##########........] c): Setting up libwebp6:amd64 (0.6.1-2ubuntu0.20.04.1) ...    
Progress: [ 64%] [###########.......] c): Setting up fonts-dejavu-core (2.37-1) ...                 
Progress: [ 67%] [###########.......] c): Setting up libjpeg-turbo8:amd64 (2.0.3-0ubuntu1.20.04.1) ...                  
Progress: [ 70%] [############......] c): Setting up libjpeg8:amd64 (8c-2ubuntu8) ...               
Progress: [ 72%] [#############.....] c): Setting up libnginx-mod-mail (1.18.0-0ubuntu1.2) ...      
Progress: [ 75%] [#############.....] c): Setting up fontconfig-config (2.13.1-2ubuntu3) ...        
Progress: [ 78%] [##############....] c): Setting up libnginx-mod-stream (1.18.0-0ubuntu1.2) ...    
Progress: [ 81%] [##############....] c): Setting up libtiff5:amd64 (4.1.0+git191117-2ubuntu0.20.04.1) ...              
Progress: [ 84%] [###############...] c): Setting up libfontconfig1:amd64 (2.13.1-2ubuntu3) ...     
Progress: [ 87%] [###############...] c): Setting up libgd3:amd64 (2.2.5-5.2ubuntu2) ...            
Progress: [ 90%] [################..] c): Setting up libnginx-mod-http-image-filter (1.18.0-0ubuntu1.2) ...             
Progress: [ 93%] [################..] c): Setting up nginx-core (1.18.0-0ubuntu1.2) ...             
Progress: [ 94%] [################..] c):                                       
Progress: [ 96%] [#################.] c): Setting up nginx (1.18.0-0ubuntu1.2) ...                  
Progress: [ 99%] [#################.] c): Processing triggers for ufw (0.36-6) ...                  
digitalocean_droplet.www-1 (remote-exec): Processing triggers for systemd (245.4-4ubuntu3.6) ...    
digitalocean_droplet.www-1 (remote-exec): Processing triggers for man-db (2.9.1-1) ...              
digitalocean_droplet.www-1 (remote-exec): Processing triggers for libc-bin (2.31-0ubuntu9.2) ...    
digitalocean_droplet.www-2: Creation complete after 1m17s [id=257772107]

digitalocean_droplet.www-1: Still creating... [1m20s elapsed]                   
digitalocean_droplet.www-1: Creation complete after 1m22s [id=257772106]        
digitalocean_loadbalancer.www-lb-gk-tftest: Creating...                         
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [10s elapsed]     
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [20s elapsed]     
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [30s elapsed]     
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [40s elapsed]     
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [50s elapsed]     
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [1m0s elapsed]    
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [1m10s elapsed]   
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [1m20s elapsed]   
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [1m30s elapsed]   
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [1m40s elapsed]   
digitalocean_loadbalancer.www-lb-gk-tftest: Still creating... [1m50s elapsed]   
digitalocean_loadbalancer.www-lb-gk-tftest: Creation complete after 1m52s [id=9f829c83-7426-4a4c-97dd-ae4d8086d38f]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

ip1 = "161.35.194.251"                  
ip2 = "161.35.194.255"

real    3m14.975s   
user    0m1.834s    
sys     0m0.422s

Builtin Infrastructure Dependency Management

When you study the output of the apply you'll notice that

  1. The output vars are in deed output
  2. TF deployed the LB only AFTER the droplets were up(!) Why? Because the LB had droplet specific variables declared - i.e. the site won't be up until the resources are.

2. is a main feature of Terraform.

Concurrency

There is a parallelism flag with default 10. They claim they resolve interdependencies correctly and order accordingly.

Since we have a statefile the next apply is idempotent but also fast:

$ time tf apply -auto-approve -parallelism=5          
digitalocean_droplet.www-2: Refreshing state... [id=257772107]                  
digitalocean_droplet.www-1: Refreshing state... [id=257772106]                  
digitalocean_loadbalancer.www-lb-gk-tftest: Refreshing state... [id=9f829c83-7426-4a4c-97dd-ae4d8086d38f]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the    
last "terraform apply":

  # digitalocean_droplet.www-2 has been changed                                 
  ~ resource "digitalocean_droplet" "www-2" {                                   
        id                   = "257772107"                                      
        name                 = "www-2"  
      + tags                 = []       
        # (22 unchanged attributes hidden)                                      
    }               
  # digitalocean_droplet.www-1 has been changed                                 
  ~ resource "digitalocean_droplet" "www-1" {                                   
        id                   = "257772106"                                      
        name                 = "www-1"  
      + tags                 = []       
        # (22 unchanged attributes hidden)                                      
    }

Unless you have made equivalent changes to your configuration, or ignored the   
relevant attributes using ignore_changes, the following plan may include        
actions to undo or respond to these changes.

───────────────────────────────────────────────────────────────────────────────

No changes. Your infrastructure matches the configuration.

Your configuration already matches the changes detected above. If you'd like to 
update the Terraform state to match, create and apply a refresh-only plan:      
  terraform apply -refresh-only

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

ip1 = "161.35.194.251"                  
ip2 = "161.35.194.255"

real    0m2.233s    
user    0m0.877s    
sys     0m0.114s

Result so far: Nginx servers up:

$ tf show
$ tfvar () { tf state show -no-color 'digitalocean_droplet.'$1' ' | grep ''$2' '  | cut -d '=' -f 2 | xargs; }
$ wget "http://$(tfvar www-1 ipv4_address)/" -O - | grep -i nginx

$ tf show     
# data.digitalocean_ssh_key.terraform:  
data "digitalocean_ssh_key" "terraform" {                                       
    fingerprint = "ea:80:64:ab:58:83:14:48:90:4c:5f:ad:4b:6e:c7:9e"             
    id          = 30935145              
    name        = "terraform"           
    public_key  = <<-EOT                
        ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDF1bQhthBX6dsAc/mLY4JxJMOjlBnU0xCu4j+iZrNI4TcTnXO4ZsWRuBPch/zveofpsA4El6d9/xgeDk8OtSjS9yTT6ihnpyWmWUW5SA+Rgk6DXS1maDmcAjByz93PsEnw7I2yIe8M2dR/nFoXzRgaYXReHdAVlYDK7QtrPhZtlzqFfev0ZOU4wGgyui9KxFGlV9H1IEXbMQqe1NZ4mejcLz1xAeFwFBuhXHJS2LRis7QeGbVK+AvcP24lZzIIJ/eyA7uQZhYtLxqK6UVS0bJzgYQQ9VIXQ7FBr8GEX4cyKt7htM470OFq1gIdvXm2G7qx8dqY9EBWUsfiX+0JjkTYcggHAx5FdN4evjXOpg7NdurvNBIYlu8C+tN7hg1rwKH4Kc9A0erGbCoFIoX6hu2Goxx/UD9ofebQ1OugGrL42rnOn4SOyIYHlABmnzydr+UZhmW54a9+NVfg7hX5U2taPmIKFtLCkaKwLVRf6XELwwL6C/XEZsl3aSkCcc0kVb8= gk@axgk            
    EOT             
}

# digitalocean_droplet.www-1:           
resource "digitalocean_droplet" "www-1" {                                       
    backups              = false        
    created_at           = "2021-08-03T21:57:21Z"                               
    disk                 = 25           
    id                   = "257772106"  
    image                = "ubuntu-20-04-x64"                                   
    ipv4_address         = "161.35.194.251"                                     
    ipv4_address_private = "10.135.158.48"                                      
    ipv6                 = false        
    locked               = false        
    memory               = 1024         
    monitoring           = false        
    name                 = "www-1"      
    price_hourly         = 0.00744      
    price_monthly        = 5            
    private_networking   = true         
    region               = "fra1"       
    resize_disk          = true         
    size                 = "s-1vcpu-1gb"                                        
    ssh_keys             = [            
        "30935145", 
    ]               
    status               = "active"     
    tags                 = []           
    urn                  = "do:droplet:257772106"                               
    vcpus                = 1            
    volume_ids           = []           
    vpc_uuid             = "f71384e0-dc84-11e8-8b13-3cfdfea9f160"               
}

# digitalocean_droplet.www-2:           
resource "digitalocean_droplet" "www-2" {                                       
    backups              = false        
    created_at           = "2021-08-03T21:57:21Z"                               
    disk                 = 25           
    id                   = "257772107"  
    image                = "ubuntu-20-04-x64"                                   
    ipv4_address         = "161.35.194.255"                                     
    ipv4_address_private = "10.135.158.49"                                      
    ipv6                 = false        
    locked               = false        
    memory               = 1024         
    monitoring           = false        
    name                 = "www-2"      
    price_hourly         = 0.00744      
    price_monthly        = 5            
    private_networking   = true         
    region               = "fra1"       
    resize_disk          = true         
    size                 = "s-1vcpu-1gb"                                        
    ssh_keys             = [            
        "30935145", 
    ]               
    status               = "active"     
    tags                 = []           
    urn                  = "do:droplet:257772107"                               
    vcpus                = 1            
    volume_ids           = []           
    vpc_uuid             = "f71384e0-dc84-11e8-8b13-3cfdfea9f160"               
}

# digitalocean_loadbalancer.www-lb-gk-tftest:                                   
resource "digitalocean_loadbalancer" "www-lb-gk-tftest" {                       
    algorithm                = "round_robin"                                    
    droplet_ids              = [        
        257772106,  
        257772107,  
    ]               
    enable_backend_keepalive = false    
    enable_proxy_protocol    = false    
    id                       = "9f829c83-7426-4a4c-97dd-ae4d8086d38f"           
    ip                       = "165.227.245.46"                                 
    name                     = "www-lb-gk-tftest"                               
    redirect_http_to_https   = false    
    region                   = "fra1"   
    size                     = "lb-small"                                       
    status                   = "active" 
    urn                      = "do:loadbalancer:9f829c83-7426-4a4c-97dd-ae4d8086d38f"               
    vpc_uuid                 = "f71384e0-dc84-11e8-8b13-3cfdfea9f160"

    forwarding_rule {                   
        entry_port      = 80            
        entry_protocol  = "http"        
        target_port     = 80            
        target_protocol = "http"        
        tls_passthrough = false         
    }

    healthcheck {   
        check_interval_seconds   = 10   
        healthy_threshold        = 5    
        port                     = 80   
        protocol                 = "tcp"                                        
        response_timeout_seconds = 5    
        unhealthy_threshold      = 3    
    }

    sticky_sessions {                   
        cookie_ttl_seconds = 0          
        type               = "none"     
    }               
}

Outputs:

ip1 = "161.35.194.251"                  
ip2 = "161.35.194.255"
$ tfvar () { tf state show -no-color 'digitalocean_droplet.'$1' ' | grep ''$2' '  | cut -d '=' -f 2 | xargs; }          
$
$ wget "http://$(tfvar www-1 ipv4_address)/" -O - | grep -i nginx               
--2021-08-04 00:00:57--  http://161.35.194.251/                                 
Connecting to 161.35.194.251:80... connected.                                   
HTTP request sent, awaiting response... 200 OK                                  
Length: 612 [text/html]                 
Saving to: β€˜STDOUT’

-                   100%[===================>]     612  --.-KB/s    in 0s

2021-08-04 00:00:58 (177 MB/s) - written to stdout [612/612]

<title>Welcome to nginx!</title>        
<h1>Welcome to nginx!</h1>              
<p>If you see this page, the nginx web server is successfully installed and     
<a href="http://nginx.org/">nginx.org</a>.<br/>                                 
<a href="http://nginx.com/">nginx.com</a>.</p>                                  
<p><em>Thank you for using nginx.</em></p>                                      
$

The tfvar function just greps values from tf state show. We could have used the output vars of the apply as well.

This is all for this chapter, so we destroy everything again:

$ tf destroy -auto-approve -lock=false

$ tf destroy -auto-approve -lock=false                
digitalocean_droplet.www-2: Refreshing state... [id=257772107]                  
digitalocean_droplet.www-1: Refreshing state... [id=257772106]                  
digitalocean_loadbalancer.www-lb-gk-tftest: Refreshing state... [id=9f829c83-7426-4a4c-97dd-ae4d8086d38f]

Terraform used the selected providers to generate the following execution plan. 
Resource actions are indicated with the following symbols:                      
  - destroy

Terraform will perform the following actions:

  # digitalocean_droplet.www-1 will be destroyed                                
  - resource "digitalocean_droplet" "www-1" {                                   
      - backups              = false -> null                                    
      - created_at           = "2021-08-03T21:57:21Z" -> null                   
      - disk                 = 25 -> null                                       
      - id                   = "257772106" -> null                              
      - image                = "ubuntu-20-04-x64" -> null                       
      - ipv4_address         = "161.35.194.251" -> null                         
      - ipv4_address_private = "10.135.158.48" -> null                          
      - ipv6                 = false -> null                                    
      - locked               = false -> null                                    
      - memory               = 1024 -> null                                     
      - monitoring           = false -> null                                    
      - name                 = "www-1" -> null                                  
      - price_hourly         = 0.00744 -> null                                  
      - price_monthly        = 5 -> null
      - private_networking   = true -> null                                     
      - region               = "fra1" -> null                                   
      - resize_disk          = true -> null                                     
      - size                 = "s-1vcpu-1gb" -> null                            
      - ssh_keys             = [        
          - "30935145",                 
        ] -> null   
      - status               = "active" -> null                                 
      - tags                 = [] -> null                                       
      - urn                  = "do:droplet:257772106" -> null                   
      - vcpus                = 1 -> null
      - volume_ids           = [] -> null                                       
      - vpc_uuid             = "f71384e0-dc84-11e8-8b13-3cfdfea9f160" -> null   
    }

  # digitalocean_droplet.www-2 will be destroyed                                
  - resource "digitalocean_droplet" "www-2" {                                   
      - backups              = false -> null                                    
      - created_at           = "2021-08-03T21:57:21Z" -> null                   
      - disk                 = 25 -> null                                       
      - id                   = "257772107" -> null                              
      - image                = "ubuntu-20-04-x64" -> null                       
      - ipv4_address         = "161.35.194.255" -> null                         
      - ipv4_address_private = "10.135.158.49" -> null                          
      - ipv6                 = false -> null                                    
      - locked               = false -> null                                    
      - memory               = 1024 -> null                                     
      - monitoring           = false -> null                                    
      - name                 = "www-2" -> null                                  
      - price_hourly         = 0.00744 -> null                                  
      - price_monthly        = 5 -> null
      - private_networking   = true -> null                                     
      - region               = "fra1" -> null                                   
      - resize_disk          = true -> null                                     
      - size                 = "s-1vcpu-1gb" -> null                            
      - ssh_keys             = [        
          - "30935145",                 
        ] -> null   
      - status               = "active" -> null                                 
      - tags                 = [] -> null                                       
      - urn                  = "do:droplet:257772107" -> null                   
      - vcpus                = 1 -> null
      - volume_ids           = [] -> null                                       
      - vpc_uuid             = "f71384e0-dc84-11e8-8b13-3cfdfea9f160" -> null   
    }

  # digitalocean_loadbalancer.www-lb-gk-tftest will be destroyed                
  - resource "digitalocean_loadbalancer" "www-lb-gk-tftest" {                   
      - algorithm                = "round_robin" -> null                        
      - droplet_ids              = [    
          - 257772106,                  
          - 257772107,                  
        ] -> null   
      - enable_backend_keepalive = false -> null                                
      - enable_proxy_protocol    = false -> null                                
      - id                       = "9f829c83-7426-4a4c-97dd-ae4d8086d38f" -> null                   
      - ip                       = "165.227.245.46" -> null                     
      - name                     = "www-lb-gk-tftest" -> null                   
      - redirect_http_to_https   = false -> null                                
      - region                   = "fra1" -> null                               
      - size                     = "lb-small" -> null                           
      - status                   = "active" -> null                             
      - urn                      = "do:loadbalancer:9f829c83-7426-4a4c-97dd-ae4d8086d38f" -> null   
      - vpc_uuid                 = "f71384e0-dc84-11e8-8b13-3cfdfea9f160" -> null

      - forwarding_rule {               
          - entry_port      = 80 -> null
          - entry_protocol  = "http" -> null                                    
          - target_port     = 80 -> null
          - target_protocol = "http" -> null                                    
          - tls_passthrough = false -> null                                     
        }

      - healthcheck {                   
          - check_interval_seconds   = 10 -> null                               
          - healthy_threshold        = 5 -> null                                
          - port                     = 80 -> null                               
          - protocol                 = "tcp" -> null                            
          - response_timeout_seconds = 5 -> null                                
          - unhealthy_threshold      = 3 -> null                                
        }

      - sticky_sessions {               
          - cookie_ttl_seconds = 0 -> null                                      
          - type               = "none" -> null                                 
        }           
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Changes to Outputs: 
  - ip1 = "161.35.194.251" -> null      
  - ip2 = "161.35.194.255" -> null      
digitalocean_loadbalancer.www-lb-gk-tftest: Destroying... [id=9f829c83-7426-4a4c-97dd-ae4d8086d38f] 
digitalocean_loadbalancer.www-lb-gk-tftest: Destruction complete after 1s       
digitalocean_droplet.www-2: Destroying... [id=257772107]                        
digitalocean_droplet.www-1: Destroying... [id=257772106]                        
digitalocean_droplet.www-1: Still destroying... [id=257772106, 10s elapsed]     
digitalocean_droplet.www-2: Still destroying... [id=257772107, 10s elapsed]     
digitalocean_droplet.www-1: Still destroying... [id=257772106, 20s elapsed]     
digitalocean_droplet.www-2: Still destroying... [id=257772107, 20s elapsed]     
digitalocean_droplet.www-2: Destruction complete after 22s                      
digitalocean_droplet.www-1: Destruction complete after 22s

Destroy complete! Resources: 3 destroyed.

DiscussionΒ€

The remote-exec provisioning method, requiring to configure the private SSH key into TF is definitely ansible territory. See the ansible chapter about TF and Ansible interplaying.

Back to top