Terraform Loop Iteration
Terraform can manage multiple similar resources by iterating through defined data structures. You reuse your Terraform code and keep similar resources managed consistently.
The Terraform for_each Meta-Argument is the mechanism for this. The example below uses it to manage multiple similar Files.com resources.
Repeated Client Resources
Suppose each client needs a folder under /Exchange/Clients, with Orders and Reports subfolders. Each client has an upload Group, while an internal collection process uses a download Group. Internal teams that contribute or retrieve files from a particular subfolder have separate Groups for those permissions.
The folder and Group arrangement is the same for every client. for_each creates an instance of each resource for every client name, and dependencies ensure folders and Groups exist before their permissions are assigned. Adding a client to the input set extends the same arrangement rather than requiring another copy of every resource definition.
This example demonstrates regular-user and Group resources. For external organizations, review the Partner User model before choosing account resources: Partner Users share Partner permissions and cannot join Groups.
for_each Usage Example
Use the for_each Meta-Argument to iterate through a variable when managing resources:
# Variable Containing List of Clients
variable "clients" {
type = set(string)
default = [
"Client A",
"Client B",
"Client C"
]
}
variable "client_passwords" {
type = map(string)
description = "A unique initial password for each client, keyed by client name."
sensitive = true
}
# Create User Accounts for each Client by using a loop
resource "files_user" "user_account" {
for_each = var.clients
authentication_method = "password"
name = each.value
company = each.value
password = var.client_passwords[each.key]
self_managed = true
username = replace(lower(each.value), " ", "_")
}
The code above defines a variable containing a list of client names. It uses the for_each meta-argument to iterate through the list and create corresponding user accounts on your Files.com site. The value for each client in the variable becomes the name, company, and username of the user account. For the username, the value is converted to lower case and any spaces are replaced with an underscore.
The client name is also the resource's identity key in this example. Removing a name from the set plans deletion of that client's resources; changing the name plans deletion under the old key and creation under the new key. For a production roster, use stable client identifiers as keys and keep display names as separate values. Preserve existing resource associations through a resource move when changing the key structure.
Supply a unique password for every client through a protected variable source. Keep the password map out of committed files and protect Terraform state, which can retain these values.
Client Resource Definitions
The implementation uses for_each loops to perform these steps for each client:
- Create client folder
- Create upload and download groups for the client folder
- Assign access permissions to the client folder groups
- Create subfolder(s)
- Create upload and download groups for the subfolder(s)
- Assign access permissions to the subfolder groups
Create Client Folder
Client folders go inside the /Exchange/Clients/ folder of the site. A Terraform best practice is to have Terraform manage all dependencies, including the existence of parent folders in the hierarchy. This avoids issues later when Terraform modifies or removes existing resources.
Configure Terraform to manage both the /Exchange/ and the /Exchange/Clients/ folders, and make the latter depend on the former. The following code does that:
# Create top level folder for Exchange
resource "files_folder" "exchange_folder" {
path = "Exchange"
}
# Create Clients folder within the Exchange folder
resource "files_folder" "exchange_clients_folder" {
path = "${files_folder.exchange_folder.path}/Clients"
depends_on = [ files_folder.exchange_folder ]
}
A for_each loop then creates and manages the client folders:
# Create home folders for each client
resource "files_folder" "client_folder" {
for_each = var.clients
path = "${files_folder.exchange_clients_folder.path}/${each.value}"
depends_on = [ files_folder.exchange_clients_folder ]
}
Create Groups for the Client Home Folder
Use similar for_each loops to create and manage the groups that are assigned upload (write only) and download (read only) access permissions.
The following code creates the 2 groups:
# Create group to upload to client home folder
resource "files_group" "client_upload_group" {
for_each = var.clients
notes = "Upload Group for ${each.value}."
ftp_permission = true
sftp_permission = true
dav_permission = true
restapi_permission = true
name = "${each.value} Upload Group"
}
# Create group to retrieve (download only) from client home folder
resource "files_group" "client_download_group" {
for_each = var.clients
notes = "Download Group for ${each.value}."
ftp_permission = true
sftp_permission = true
dav_permission = true
restapi_permission = true
name = "${each.value} Download Group"
}
Assign Access Permissions to the Home Folder Groups
Assign access permissions to the 2 home folder groups with the following code:
# Assign upload access permission to upload group
resource "files_permission" "client_group_upload_permission" {
for_each = var.clients
path = "${files_folder.exchange_clients_folder.path}/${each.value}"
group_name = "${each.value} Upload Group"
permission = "writeonly"
depends_on = [files_group.client_upload_group, files_folder.client_folder]
}
# Assign download access permission to retrieve group
resource "files_permission" "client_group_download_permission" {
for_each = var.clients
path = "${files_folder.exchange_clients_folder.path}/${each.value}"
group_name = "${each.value} Download Group"
permission = "readonly"
depends_on = [files_group.client_download_group, files_folder.client_folder]
}
The code above assigns access permissions to the previously created groups. It specifies dependencies so that the permissions are only assigned after the groups and their corresponding home folders are successfully created.
Create Client Subfolders
For each subfolder within the client home folder, repeat the following code, adjusting the folder name as needed:
# Create Orders subfolder for each client
resource "files_folder" "client_orders_folder" {
for_each = var.clients
path = "${files_folder.exchange_clients_folder.path}/${each.value}/Orders"
depends_on = [ files_folder.client_folder ]
}
The code above creates an Orders subfolder within the client home folder. It specifies a dependency so that the subfolder is only created after the home folder has been created.
Duplicate this step for each required subfolder. For the Reports subfolder, use reports in Terraform resource labels and Reports in folder paths and group names.
Create Groups for the Client Subfolders
As before, create 2 groups that are assigned upload (write only) and download (read only) access permissions to the subfolders:
# Create group to upload to client orders folder
resource "files_group" "client_orders_upload_group" {
for_each = var.clients
notes = "Orders Upload Group for ${each.value}."
ftp_permission = true
sftp_permission = true
dav_permission = true
restapi_permission = true
name = "${each.value} Orders Upload Group"
}
# Create group to download from client orders folder
resource "files_group" "client_orders_download_group" {
for_each = var.clients
notes = "Orders Download Group for ${each.value}."
ftp_permission = true
sftp_permission = true
dav_permission = true
restapi_permission = true
name = "${each.value} Orders Download Group"
}
Duplicate this step for each required subfolder. For the Reports subfolder, use reports in Terraform resource labels and Reports in folder paths and group names.
Assign Access Permissions to the Subfolder Groups
Assign access permissions to the 2 subfolder groups with the following code:
# Assign upload access permission to orders upload group
resource "files_permission" "client_group_orders_upload_permission" {
for_each = var.clients
path = "${files_folder.exchange_clients_folder.path}/${each.value}/Orders"
group_name = "${each.value} Orders Upload Group"
permission = "writeonly"
depends_on = [files_group.client_orders_upload_group, files_folder.client_orders_folder]
}
# Assign download access permission to orders download group
resource "files_permission" "client_group_orders_download_permission" {
for_each = var.clients
path = "${files_folder.exchange_clients_folder.path}/${each.value}/Orders"
group_name = "${each.value} Orders Download Group"
permission = "readonly"
depends_on = [files_group.client_orders_download_group, files_folder.client_orders_folder]
}
The code above assigns access permissions to the groups and specifies dependencies so that the permissions are only assigned after the groups and their corresponding subfolders are successfully created.
Duplicate this step for each required subfolder. For the Reports subfolder, use reports in Terraform resource labels and Reports in folder paths and group names.
Assign Group Membership
Add the following attribute to the files_user.user_account resource above so each client joins its upload group:
group_ids = tostring(files_group.client_upload_group[each.key].id)
Assign the appropriate internal system and support accounts to the download and subfolder groups. Creating groups and permissions does not automatically populate those groups.
Execution
When the code above runs, Terraform creates all these resources and manages the dependencies between them.
Each time you add a client to the variable, Terraform manages 16 corresponding resources for that client: 1 user account, 3 folders, 6 groups, and 6 access permissions. Add the corresponding password entry and configure internal group membership before granting access.
An onboarding system can maintain the client input set, credentials, and group membership. Review the Terraform plan before applying it, including any removals when a client leaves the set.