Apply function to one particular key pair item of all map objects inside a list

I have a map which contains a list of maps object (idea from stackoverflow). The OP used this in main.tf, so file() works just fine.

ssh_keys_user = {
  write_files = [
    {
      path        = "/home/ecarroll/.ssh/id_rsa"
      content     = file("./ssh/user/cp-terraform-user-id_rsa")
      owner       = "ecarroll:ecarroll"
      permissions = "0600"
      defer       = true
    },
    {
      path        = "/home/ecarroll/.ssh/id_rsa.pub"
      content     = file("./ssh/user/cp-terraform-user-id_rsa.pub")
      owner       = "ecarroll:ecarroll"
      permissions = "0644"
      defer       = true
    },
    {
      path        = "/home/ecarroll/.ssh/id_ecdsa"
      content     = file("./ssh/user/cp-terraform-user-id_ecdsa")
      owner       = "ecarroll:ecarroll"
      permissions = "0600"
      defer       = true
    },
    {
      path        = "/home/ecarroll/.ssh/id_ecdsa.pub"
      content     = file("./ssh/user/cp-terraform-user-id_ecdsa.pub")
      owner       = "ecarroll:ecarroll"
      permissions = "0644"
      defer       = true
    }
  ]
}

But I want to define this in terraform.tfvars, but function is not allowed there, so it has to be something like:

ssh_keys_user = {
  write_files = [
    {
      path        = "/home/ecarroll/.ssh/id_rsa"
      content     = "./ssh/user/cp-terraform-user-id_rsa"
      owner       = "ecarroll:ecarroll"
      permissions = "0600"
      defer       = true
    },
    {
      path        = "/home/ecarroll/.ssh/id_rsa.pub"
      content     = "./ssh/user/cp-terraform-user-id_rsa.pub"
      owner       = "ecarroll:ecarroll"
      permissions = "0644"
      defer       = true
    },
    {
      path        = "/home/ecarroll/.ssh/id_ecdsa"
      content     = "./ssh/user/cp-terraform-user-id_ecdsa"
      owner       = "ecarroll:ecarroll"
      permissions = "0600"
      defer       = true
    },
    {
      path        = "/home/ecarroll/.ssh/id_ecdsa.pub"
      content     = "./ssh/user/cp-terraform-user-id_ecdsa.pub"
      owner       = "ecarroll:ecarroll"
      permissions = "0644"
      defer       = true
    }
  ]
}

I am looking for a way to apply file() to “content” key pair in the main.tf.

Thanks in advance

Cheers
D

Hi @klin938,

I’m guessing from the similar names that you are intending this to be configuration for cloud-config’s write_files module, and so this input variable would presumably be defined to (mostly) match that module’s schema, like this:

variable "write_files" {
  type = list(object({
    # src_path and dst_path describe respectively the
    # local file to send and the remote path to
    # write its contents to.
    src_path = string
    dst_path = string

    # The remaining attributes all correspond exactly
    # to the cloud-init write_files schema.
    owner       = optional(string, "root:root")
    permissions = optional(string, "0644")
    defer       = optional(bool, false)
  }))
}

You can transform that into the form that cloud-init expects using a for expression, which is Terraform’s language feature for generating one collection based on another. I’ll show this in a local value just because it seems like your ssh_keys_user in your example is probably already a local value:

locals {
  ssh_keys_user = {
    write_files = tolist([
      for f in var.write_files : {
        path        = f.dst_path
        content     = file(f.src_path)
        owner       = f.owner
        permissions = f.permissions
        defer       = f.defer
      }
    ])
  }
}

You can then use local.ssh_keys_user.write_files to access the transformed list, which will have path and content attributes instead of dst_path and src_path attributes.


Separately, I’d note that uploading your local SSH private key onto a remote system is an unusual strategy, since usually you should have a unique private key for each system where you work so that you can more easily revoke keys that get compromised.

If your goal in doing this is to be able to log in to the newly-created system and then use your local SSH credentials to access additional data for setting up the system, the more common strategy for that is SSH Agent Forwarding. I’ve linked to GitHub’s documentation because it’s well-written and because accessing private Git repositories is a common reason to want to do this, but this is not GitHub-specific so the same strategy would work for any SSH server that’s configured to trust your key.

The trick with SSH Agent Forwarding is that your private key remains only on your local computer, and then the SSH client creates a temporary bridge from the remote host that allows it to perform authentication with your key without actually accessing the secret key directly. Once you disconnect from the server, that bridge is no longer available and so the remote system will no longer be able to make SSH connections using your key.

1 Like

Hi thanks! It works!

And I am very appreciated your tip about the Agent Forwarding (yes I need it for private Git access), I will definitely look into it.

Cheers