Terraform loop for files in folder

548 Views Asked by At

In terraform, I'm using user_data to initiate a windows server instance. Locally, I have a folder with several files. Each file contains a single line.

In my user_data PowerShell script, I would like to create a single file containing the concatenation of all the lines in my local files.

An example is better than words:

locally, I have:

$ ls my_folder
file1
file2

$ cat file1
foo

$ cat file2
bar

In terraform user_data:

PS> New-Item C:\my_file
for file in my_folder
    cat file in my_file

How can I do this in terraform?

Thanks

2

There are 2 best solutions below

0
On

@Matt's answer should do the trick for you. In case you need a more structured approach, you can also take advantage of HashiCorp's cloud-init provider, which gives you more flexibility when building the cloud-init config.

To illustrate an example, this is the boilerplate project structure I used:

.
├── cloud-config
│   └── base.yml.tftpl
├── files
│   ├── file1
│   └── file2
└── main.tf

3 directories, 4 files

In main.tf, I use the cloudinit_config resource do render the user_data from a template file base.yml.tftpl and pass the rendered contents to an instance.

terraform {
  required_version = ">=1.3"
  required_providers {
    cloudinit = {
      source = "hashicorp/cloudinit"
      version = "2.3.2"
    }
    aws = {
      source = "hashicorp/aws"
      version = "4.57.1"
    }
  }
}

resource "cloudinit_config" "user_data" {
  gzip          = false
  base64_encode = false

  part {
    filename     = "cloud-config.yml"
    content_type = "text/cloud-config"

    content = templatefile("${path.module}/cloud-config/base.yml.tftpl", {
      files = fileset(path.module, "files/*")
    })
  }
}

# Using an AWS instance, as an example user_data consumer.
resource "aws_instance" "instance" {
    ...
    user_data = cloudinit_config.user_data.rendered

    depends_on = [
      cloudinit_config.user_data
    ]
}

As I'm not too familiar with PowerShell, I kept the base.yml.tftpl quite generic. It demonstrates that you have access to both the filename and the file contents, should there be a need. I believe you can easily concat the file contents into a single string either by looping over the files and extracting the content or by using the join() approach, as mentioned in the previous answer.

#cloud-config
write_files:
  - content: |
%{ for file in files ~}
      filename: ${file}
      content: ${file(file)}
%{ endfor ~}
    path: /foo/bar/baz
0
On

It would be easier to accomplish this in Terraform than wrapped inside the Powershell:

user_data = join("", [for local_file in fileset(path.module, "my_folder/**/*") : file(local_file)])

Use a for expression to iterate through the output enumerable of all files in the folder return by fileset, and then collect their contents as a string return element with file. We then join the list into a single string.