Background
I am using the digitalocean_droplet resource. I would like to create multiple digitalocean droplets, each with their own SSH key.
The droplets will be created by reading locals
variable named droplets
at the top of the script for ease of modification, and each droplet must have its own SSH key. I am using the cloudposse/terraform-tls-ssh-key-pair module to create the key pair.
Problem
The digitalocean_droplet
resource requires an array of IDs under the ssh_keys
key. I know how to provide a single key here, but I am using for_each
for both the module and for the digitalocean_ssh_key
resource.
I don't know how to tell the digitalocean_droplet
resource to, for ssh_keys
, use multiple values from the digitalocean_ssh_key
resource.
To use a single key, ssh_keys
would look like this:
ssh_keys = [digitalocean_ssh_key.ssh_key.id]
To use multiple, I think that I need to tell ssh_keys
to "use each of the values generated by the digitalocean_ssh_key resource". But I don't know how to do this.
I expect (and hope) this to simply be a lack of knowledge on HCL.
What I have tried
I have tried what I think should be telling ssh_keys
to use each .id
from the digitalocean_ssh_key
resource:
ssh_keys = {
for index, v in digitalocean_ssh_key.ssh_key:
v => v.id
}
However this results in a cycle error on running terraform plan
:
Error: Cycle: module.ssh_key_pair.output.private_key (expand), module.ssh_key_pair.var.chmod_command (expand), module.ssh_key_pair.null_resource.chmod, module.ssh_key_pair.output.public_key (expand), module.ssh_key_pair.var.private_key_extension (expand) [etc etc]
I have also tried using module.ssh_key_pair
, but this does not provide me with an id
, which is what ssh_keys
requires.
Conclusion
What do I need to do to tell digitalocean_droplet
to, for each of the configuration in my locals
variable named "droplets", generate a new SSH key using my module and to assign that SSH key to each individual droplet?
Code
locals {
// An example of a single droplet. I will add multiple here in the future.
droplets = {
"DROPLET_NAME_AS_KEY" : {
image = "distro image here"
size = "droplet size here"
}
}
}
// For each droplet above, create a new digitalocean droplet
resource "digitalocean_droplet" "droplets" {
for_each = local.droplets
name = each.key
region = "fra1"
image = each.value.image
size = each.value.size
tags = [each.key]
// Next is my problem, what goes for ssh_keys??
ssh_keys = ???
}
// Create a new key pair for each droplet.
module "ssh_key_pair" {
for_each = digitalocean_droplet.droplets
source = "git::https://github.com/cloudposse/terraform-tls-ssh-key-pair.git?ref=master"
ssh_public_key_path = "/users/me/.ssh"
name = "${each.value.name}"
}
// The link between the key pair resource and the ssh_key for digital ocean.
resource "digitalocean_ssh_key" "ssh_key" {
for_each = module.ssh_key_pair
name = each.value.key_name
public_key = each.value.public_key
}
Bonus question if the above gets solved:
I actually use variables for all the values in the locals
array. These come from environment variables from docker-compose, as I'm running terraform in a container. Is there a simple way to provide an array in docker-compose which can become this array here without me having to update both the environment
key in docker-compose.yml and this droplets array, and instead just update an array in docker-compose.yml?
You should use local.droplets
in all three for_each
loops to avoid cycles.
You can then access module.ssh_key_pair
module in digitalocean_ssh_key
resource with module.ssh_key_pair[each.key]
.
The ssh_keys
field in digitalocean_droplet
resource would be ssh_keys = [digitalocean_ssh_key.ssh_key[each.key].fingerprint]
.
Regarding your bonus question - you could declare this variable:
variable "droplets" {
type = map(object({
image = string
size = string
}))
}
And then in for_each
use var.droplets
instead of locals.droplets
.
You can use env variables to set Terraform variables, however:
For readability, and to avoid the need to worry about shell escaping, we recommend always setting complex variable values via variable definitions files.
See details here - https://developer.hashicorp.com/terraform/language/values/variables#assigning-values-to-root-module-variables.