Skip to main content

Terraform Loop Iteration

Terraform can manage multiple similar resourcesExternal LinkThis link leads to an external website and will open in a new tab by iterating through defined data structures. You reuse your Terraform code and keep similar resources managed consistently.

The Terraform for_each Meta-ArgumentExternal LinkThis link leads to an external website and will open in a new tab is the mechanism for this. The example below uses it to manage multiple similar Files.com resources.

Example Use Case

The scenario is a fictional healthcare business that processes Medicare claims.

The healthcare business needs to onboard multiple client customers, such as the healthcare providers it serves.

Each client requires a home folder within the Medicare clients' folder structure. Each client home folder contains subfolders named Eligibility and Prescription Drug Event for that client.

Each client home folder is assigned 2 user access groups. One group has permission only to upload and the other has permission only to download. The customer client is placed in the upload group and the internal systems are placed in the download group. This way, only clients submit data and only internal systems collect it.

For compliance reasons, each subfolder also needs to be accessible by the relevant teams. The Eligibility subfolder is accessed (read only) by another internal system and accessed (write only) by the Customer Eligibility Support Team whenever eligibility records are updated or changed. The Eligibility subfolder gets 2 new user access groups assigned to meet these access needs.

The Prescription Drug Event subfolder has similar requirements to the Eligibility subfolder. It also gets 2 user access groups: 1 to upload (write only) and 1 to download (read only).

All new clients have these same requirements applied to their folders.

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"
  ]
}

# 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                     = "S0meRea11yLongP@ssw0rd"
  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.

Implementing the Example Use Case

The implementation uses for_each loops to perform these steps for each client:

Create Client Folder

Client folders go inside the /Medicare/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 /Medicare/ and the /Medicare/Clients/ folders, and make the latter depend on the former. The following code does that:

# Create top level folder for Medicare

resource "files_folder" "top_medicare_folder" {
  path = "Medicare"
}

# Create Clients folder within the Medicare folder

resource "files_folder" "medicare_clients_folder" {
  path = "${files_folder.top_medicare_folder.path}/Clients"
  depends_on = [ files_folder.top_medicare_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.medicare_clients_folder.path}/${each.value}"
  depends_on = [ files_folder.medicare_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.medicare_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.medicare_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 Eligibility subfolder for each client

resource "files_folder" "client_eligibility_folder" {
  for_each = var.clients
  path = "${files_folder.medicare_clients_folder.path}/${each.value}/Eligibility"
  depends_on = [ files_folder.client_folder ]
}

The code above creates an Eligibility 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 this use case, repeat the code but replace all mentions of "eligibility" with "prescription drug event".

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 eligibility folder

resource "files_group" "client_eligibility_upload_group" {
  for_each = var.clients
  notes              = "Eligibility Upload Group for ${each.value}."
  ftp_permission     = true
  sftp_permission    = true
  dav_permission     = true
  restapi_permission = true
  name               = "${each.value} Eligibility Upload Group"
}

# Create group to download from client eligibility folder

resource "files_group" "client_eligibility_download_group" {
  for_each = var.clients
  notes              = "Eligibility Download Group for ${each.value}."
  ftp_permission     = true
  sftp_permission    = true
  dav_permission     = true
  restapi_permission = true
  name               = "${each.value} Eligibility Download Group"
}

Duplicate this step for each required subfolder. For this use case, repeat the code but replace all mentions of "eligibility" with "prescription drug event".

Assign Access Permissions to the Subfolder Groups

Assign access permissions to the 2 subfolder groups with the following code:

# Assign upload access permission to eligibility upload group

resource "files_permission" "client_group_eligibility_upload_permission" {
  for_each = var.clients
  path = "${files_folder.medicare_clients_folder.path}/${each.value}/Eligibility"
  group_name = "${each.value} Eligibility Upload Group"
  permission = "writeonly"
  depends_on = [files_group.client_eligibility_upload_group, files_folder.client_eligibility_folder]
}

# Assign download access permission to eligibility download group

resource "files_permission" "client_group_eligibility_download_permission" {
  for_each = var.clients
  path = "${files_folder.medicare_clients_folder.path}/${each.value}/Eligibility"
  group_name = "${each.value} Eligibility Download Group"
  permission = "readonly"
  depends_on = [files_group.client_eligibility_download_group, files_folder.client_eligibility_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 this use case, repeat the code but replace all mentions of "eligibility" with "prescription drug event".

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. A single addition to the variable drives all the changes needed to onboard the client.

In real-world scenarios, an onboarding system updates the client variable programmatically so that the entire process is automated, removing delays and human error.