Terraform Loop Iteration
Terraform provides the ability to manage multiple similar resources by iterating through defined data structures. This enables you to reuse your Terraform code and ensures that similar resources are managed consistently.
In this article, we will use the Terraform for_each Meta-Argument to demonstrate managing multiple similar Files.com resources.
Example Use Case
In this example, we will use a fictional healthcare scenario to demonstrate processing Medicare claims.
My healthcare business needs to onboard multiple client customers, such as the healthcare providers which my business provides services to.
Each client will require a home folder within the Medicare clients' folder structure. Each client home folder needs to be provided with subfolders named Eligibility
and Prescription Drug Event
for that client.
Each client will need to have 2 user access groups assigned to their home folder. There will be 1 group that only has permission to upload and 1 group that only has permission to download. The customer client will be placed into the upload group while my internal systems will be placed into the download group. This ensures that only my clients can submit data to me and that my internal systems can only collect the submitted data.
For compliance reasons, I also need to allow each subfolder to be accessed by the relevant teams. The Eligibility
subfolder needs to be accessed (read only) by another of my internal systems as well as accessed (write only) by the Customer Eligibility Support Team whenever eligibility records need to be updated or changed. There will need to be 2 new user access groups assigned to the Eligibility
subfolder to meet these access needs.
The Prescription Drug Event
subfolder has similar requirements to the Eligibility
subfolder. There need to be 2 user access groups for this subfolder, 1 to upload (write only) and 1 to download (read only).
All new clients will need the above requirements to be similarly applied to their folders.
for_each Usage Example
The for_each
Meta-Argument can be used to iterate through a variable when managing resources, like this:
# 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 above code defines a variable containing a list of client names, and uses the for_each
meta-argument to iterate through the list and create corresponding user accounts in your Files.com site. The value for each client in the variable is used for 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 Our Example Use Case
To implement our example use case, we'll use 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
We want our client folders to be placed within the /Medicare/Clients/
folder of our site. A best practice in Terraform is to ensure that all dependencies, including the existence of parent folders in the hierarchy, are managed by Terraform. This helps mitigate later issues when Terraform is used to modify or remove existing resources.
We need to configure Terraform to manage both the /Medicare/
and the /Medicare/Clients/
folders. We also need to make sure that the latter folder is dependent on the former existing. Here is some code to achieve this:
# 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 ]
}
We can now implement a for_each
loop to create and manage 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
We can use similar for_each
loops to create and manage the groups that will be assigned upload (write only) and download (read only) access permissions.
The following code will create 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
To assign access permissions to the 2 home folder groups, use 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 above code assigns access permissions to the previously created groups and specifies dependencies to ensure the permissions are only assigned once 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 above code will create an Eligibility
subfolder within the client home folder. It also specifies a dependency so that the subfolder will only be created once the home folder has been created.
This step can be duplicated for each required subfolder. For our use case, we would repeat this code but replace all mentions of "eligibility" with "prescription drug event".
Create Groups for the Client Subfolders
Just as before, we can create 2 groups that will be 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"
}
This step can be duplicated for each required subfolder. For our use case, we would repeat this code but replace all mentions of "eligibility" with "prescription drug event".
Assign Access Permissions to the Subfolder Groups
To assign access permissions to the 2 subfolder groups, use 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 above code will assign access permissions to the groups as well as specifying dependencies so that the permissions are only assigned if the groups, and the corresponding subfolders, were successfully created.
This step can be duplicated for each required subfolder. For our use case, we would repeat this code but replace all mentions of "eligibility" with "prescription drug event".
Execution
When we execute the above code, Terraform will create all these resources and manage any dependencies between them.
Each time we add a client to the variable, Terraform will manage 16 corresponding resources (1 user account, 3 folders, 6 groups, and 6 access permissions) for that client. A single addition to the variable drives all the changes needed to onboard your clients.
In real world scenarios, the client variable would be updated programmatically by an onboarding system so that the entire process is automated, removing delays and human error.