Orchestrating Scalable Shared Storage: A Deep Dive into AWS EFS with Terraform

Amazon Elastic File System (EFS) stands as the cornerstone of scalable, network-attached storage within the Amazon Web Services ecosystem. Designed to provide a shared, high-throughput file system accessible from multiple EC2 instances, EFS eliminates the traditional bottlenecks associated with local block storage by decoupling storage capacity from compute instances. As organizations migrate workloads to the cloud, the need for infrastructure that is not only scalable but also reproducible and auditable becomes critical. Terraform, an open-source infrastructure as code tool, provides the precise mechanism to declare these EFS resources in a deterministic manner. By leveraging Terraform, engineering teams can define the lifecycle of EFS file systems, mount targets, security groups, and access points through version-controlled code. This approach ensures that production environments remain consistent with staging and development setups, reducing the risk of configuration drift. This article provides a comprehensive technical guide to deploying AWS EFS using Terraform, covering prerequisites, resource configuration, security hardening, lifecycle policies, and advanced module integration.

Prerequisites and Project Architecture

Before initiating the provisioning of EFS resources via Terraform, specific environmental prerequisites must be met. The local development environment requires an installed and configured version of Terraform, along with the AWS Command Line Interface (AWS CLI) configured with valid credentials that possess permissions to manage EFS, VPC, and IAM resources. Network prerequisites are equally vital; a Virtual Private Cloud (VPC) and its associated subnets must already be configured, as EFS mount targets must reside within subnets that provide network connectivity to the EC2 instances requiring access to the file system. A fundamental understanding of Network File System (NFS) protocols is also assumed, as EFS utilizes NFS version 4.0 and 4.1 protocols, with TCP port 2049 being the standard port for NFS traffic.

The standard project structure for a dedicated EFS Terraform module typically includes four primary files: main.tf for core resource definitions, variables.tf for input parameters, outputs.tf for exposing resource attributes, and terraform.tfvars for local variable values. This separation of concerns allows for clean, modular code that can be easily reused across different environments.

aws-efs-terraform/ ├── main.tf ├── variables.tf ├── outputs.tf └── terraform.tfvars

Defining Core EFS Resources

The foundation of the configuration is the aws_efs_file_system resource. This resource defines the logical file system and its core properties. A critical attribute is the creation_token. Unlike other AWS resources where IDs are often generated randomly, EFS requires a creation_token to ensure idempotency. If a file system with the same creation_token already exists, Terraform will import the existing file system rather than attempting to create a duplicate or failing. This is essential for recovery scenarios where infrastructure may have been partially created or where external factors modified the state.

Encryption at rest is a best practice for any production file system. By setting the encrypted attribute to true, EFS integrates with AWS Key Management Service (KMS) to encrypt all data written to the file system. By default, EFS uses a service-managed KMS key, but for stricter compliance, a customer-managed key can be specified.

Lifecycle policies allow for cost optimization by automatically transitioning data between storage tiers. EFS offers two primary storage classes: Standard and Infrequent Access (IA). Files that have not been modified or accessed for a specified period can be transitioned to the IA class, which offers lower storage costs but higher access costs. This is ideal for backup data or logs that are rarely read.

The following configuration demonstrates a basic yet robust setup for the file system, including encryption and a 30-day transition policy to Infrequent Access:

```
provider "aws" {
region = var.aws_region
}

EFS File System

resource "awsefsfilesystem" "main" {
creation
token = "${var.project_name}-efs"
encrypted = true

lifecyclepolicy {
transition
toia = "AFTER30_DAYS"
}

tags = {
Name = "${var.project_name}-efs"
}
}
```

Network Connectivity and Mount Targets

An EFS file system does not have a public IP address by default; instead, it is accessible via DNS names within the VPC. To make the file system accessible, one or more mount targets must be created. A mount target associates the file system with a specific subnet. For high availability, it is standard practice to create mount targets in multiple Availability Zones (AZs) within the VPC.

The aws_efs_mount_target resource facilitates this. It requires the file_system_id (which references the ID of the aws_efs_file_system resource), a subnet_id, and the security_groups to apply. By using a count meta-argument, Terraform can dynamically create a mount target for each subnet ID provided in a variable list. This dynamic approach simplifies multi-AZ deployments.

```

Mount Targets

resource "awsefsmounttarget" "main" {
count = length(var.subnet
ids)
filesystemid = awsefsfilesystem.main.id
subnet
id = var.subnetids[count.index]
security
groups = [awssecuritygroup.efs.id]
}
```

Security Group Configuration

Security in a VPC is enforced through Security Groups (SGs). For EFS, the critical configuration involves the ingress rules, which must allow NFS traffic on TCP port 2049. Overly permissive rules, such as allowing traffic from 0.0.0.0/0 (the entire internet), should be strictly avoided for production workloads. Instead, ingress should be restricted to specific security groups or CIDR blocks that correspond to the EC2 instances or other AWS services that need to mount the file system.

The following configuration illustrates a secure ingress rule that allows NFS traffic only from instances associated with specified security groups, while allowing all egress traffic:

```

Security Group

resource "awssecuritygroup" "efs" {
name = "${var.projectname}-efs-sg"
description = "Allow EFS inbound traffic"
vpc
id = var.vpc_id

ingress {
description = "NFS from VPC"
fromport = 2049
to
port = 2049
protocol = "tcp"
securitygroups = var.allowedsecuritygroupids
}

egress {
fromport = 0
to
port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "${var.project_name}-efs-sg"
}
}
```

Advanced Features: Backup Policies and Access Points

Beyond basic file storage, EFS provides advanced features that can be managed via Terraform. One such feature is automated backups. The aws_efs_backup_policy resource enables automatic backup of the file system to AWS Backup. This is crucial for disaster recovery strategies.

```

Backup Policy (Optional)

resource "awsefsbackuppolicy" "policy" {
file
systemid = awsefsfilesystem.main.id

backup_policy {
status = "ENABLED"
}
}
```

Access points provide a way to manage user and group permissions for the file system. By creating access points with specific POSIX user IDs (UID) and group IDs (GID), you can enforce directory-level permissions. This is particularly useful in multi-tenant environments where different applications or users require isolated access to specific directories. An access point can be created to point to a specific root directory, ensuring that applications only see and interact with their designated portion of the file system.

```

Access Point (Optional)

resource "awsefsaccesspoint" "test" {
file
systemid = awsefsfilesystem.main.id
# Additional attributes such as rootdirectory and posixuser would be configured here
}
```

Leveraging Community Modules

While raw Terraform resources offer full control, community modules provide pre-configured, tested, and documented wrappers around AWS services. The terraform-aws-modules/efs/aws module is a widely adopted solution that simplifies the creation of EFS resources. It abstracts the complexity of mount targets, security groups, and KMS integration.

Using this module, developers can specify high-level parameters such as name, creation_token, encrypted, and kms_key_arn. The module handles the creation of the file system, mount targets for specified subnets, and associated security groups. This approach reduces boilerplate code and ensures that best practices are followed by default.

```
module "efs" {
source = "terraform-aws-modules/efs/aws"

# File system
name = "example"
creationtoken = "example-token"
encrypted = true
kms
key_arn = "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
}
```

Similarly, the AustinCloudGuru/efs/aws module offers a slightly different interface, allowing for more granular control over security group ingress rules and lifecycle policies. It accepts a security_group_ingress map, which allows for the definition of multiple inbound rules, such as NFS and SSH.

```
module "efs-0" {
source = "AustinCloudGuru/efs/aws"

vpcid = "vpc-0156c7c6959ba5858"
name = "dev-efs"
subnet
ids = ["subnet-05b1a3ffd786709d5", "subnet-0a35212c972a2af05", "subnet-0d0e78f696428aa28"]

securitygroupingress = {
default = {
description = "NFS Inbound"
fromport = 2049
protocol = "tcp"
to
port = 2049
self = true
cidrblocks = []
},
ssh = {
description = "ssh"
from
port = 22
protocol = "tcp"
toport = 22
self = true
cidr
blocks = []
}
}

lifecyclepolicy = [{
"transition
toia" = "AFTER30_DAYS"
}]

tags = {
Terraform = "true"
Environment = "development"
}
}
```

The following table compares the key variables available in these two popular modules, highlighting their differences in configuration flexibility.

Variable Name Description Type Default Source Module
backup_policy_status Enable/disable backup for EFS Filesystem. Value should be ENABLED/DISABLED. string "DISABLED" AustinCloudGuru
encrypted If true, the file system will be encrypted. bool true AustinCloudGuru
kms_key_id If set, use a specific KMS key. string null AustinCloudGuru
lifecycle_policy Lifecycle Policy for the EFS Filesystem. list(object({ transitiontoia = string })) [] AustinCloudGuru
name A unique name used as reference when creating the Elastic File System. string (Required) Both
creation_token A creation token to ensure idempotency. string (Required) Both
kms_key_arn The ARN of the KMS key to use for encryption. string null terraform-aws-modules
subnet_ids List of subnet IDs to create mount targets in. list(string) (Required) Both

Workflow: Initialization, Planning, and Application

Once the configuration files are written, the standard Terraform workflow is executed. The process begins with terraform init, which initializes the working directory, downloads the necessary providers (in this case, the AWS provider), and sets up the backend for state management. Successful initialization is indicated by a confirmation message in the CLI output.

The next step is terraform plan. This command analyzes the configuration and the current state of the AWS account to generate an execution plan. It displays the resources that will be added, changed, or destroyed. This step is critical for validating syntax and ensuring that the intended changes match the expected outcome. For a fresh deployment, the plan will typically indicate the number of resources to add, such as "Plan: 4 to add".

Finally, terraform apply --auto-approve executes the plan. The --auto-approve flag bypasses the confirmation prompt, allowing the command to run non-interactively. This is useful for CI/CD pipelines where human intervention is not desired. Upon completion, the EFS file system, mount targets, security group, and any other defined resources will be provisioned in the AWS account.

To verify the deployment, administrators should log in to the AWS Management Console, navigate to the EFS dashboard, and confirm that the file system is visible and healthy. The file system ID and DNS name can be copied from the console or from the Terraform state file for use in EC2 instance user data scripts to mount the file system.

Output Management and Integration

In a modularized infrastructure, outputs are essential for passing information from one module to another. For an EFS module, the most critical outputs are the file system ID and the DNS name, which are required for mounting the file system on EC2 instances. Additionally, if access points are created, their IDs should be exported to allow specific applications to mount to those isolated access points.

```
output "efsid" {
value = aws
efsfilesystem.main.id
description = "EFS file system ID"
}

output "efsdnsname" {
value = awsefsfilesystem.main.dnsname
description = "EFS DNS name for mounting"
}

output "accesspointids" {
value = {
api = awsefsaccesspoint.api.id
worker = aws
efsaccesspoint.worker.id
}
description = "Access point IDs by service"
}
```

These outputs allow downstream modules, such as those defining EC2 instances or Auto Scaling Groups, to dynamically reference the EFS endpoint without hardcoding values. This decoupling enhances the reusability and maintainability of the infrastructure code.

Conclusion

Implementing AWS EFS with Terraform transforms file storage management from a manual, error-prone process into a deterministic, automated, and auditable workflow. By defining file systems, mount targets, and security policies in code, organizations can ensure that their storage infrastructure is secure, cost-optimized through lifecycle policies, and resilient through multi-AZ mount targets. The ability to leverage community modules further accelerates development by providing pre-validated patterns for common use cases. Whether utilizing raw resources for maximum control or community modules for simplicity, the integration of Terraform with AWS EFS is a critical component of modern cloud infrastructure. It enables engineering teams to scale storage alongside their applications, maintain compliance through encryption and backup policies, and enforce granular access controls, all while ensuring that every change is version-controlled and reproducible. As cloud architectures continue to evolve, the synergy between infrastructure as code and managed services like EFS will remain a fundamental best practice for delivering reliable and secure applications at scale.

Sources

  1. Setting up AWS EFS with Terraform
  2. Terraform EFS
  3. terraform-aws-modules/terraform-aws-efs
  4. AustinCloudGuru/terraform-aws-efs
  5. Create EFS File Systems with Terraform

Related Posts