Amazon Elastic File System provides scalable file storage for use with Amazon EC2 instances. Setting up AWS EFS with Terraform is a detailed guide to deploying Amazon Elastic File System using Terraform Infrastructure as Code. The guide shows how to set up EFS using Terraform.
Prerequisites and Project Structure
Before provisioning an EFS file system with Terraform, the following prerequisites are required.
- AWS CLI configured
- Terraform installed
- VPC and subnets already configured
- Basic understanding of network file systems
A typical project structure used for this workflow is:
aws-efs-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Core Resource awsefsfile_system
Manages an Efs File System resource.
A minimal configuration to get started is:
hcl
resource "aws_efs_file_system" "example" {
# Required arguments
name = "my-efs-file-system"
}
Refer to the Terraform Registry docs for all available arguments.
In production configurations the file system resource is defined with additional arguments for idempotency, encryption and lifecycle management.
```hcl
provider "aws" {
region = var.aws_region
}
resource "awsefsfilesystem" "main" {
creationtoken = "${var.projectname}-efs"
encrypted = true
lifecyclepolicy {
transitiontoia = "AFTER30DAYS"
}
tags = {
Name = "${var.project_name}-efs"
}
}
```
The core of the storage layer is the awsefsfilesystem resource named appefs. It is initialized with a creation_token set to "appEFS" to ensure idempotency during the provisioning process. The system is tagged with the environment name e.g., staging-efs for resource tracking.
The file system ID is exported as a Terraform output, allowing other modules or external systems to reference the specific storage resource.
Mount Targets and Network Placement
To make the EFS volume accessible within the VPC, mount targets are created in every private subnet where application instances reside.
The example configuration uses a count based mount target:
hcl
resource "aws_efs_mount_target" "main" {
count = length(var.subnet_ids)
file_system_id = aws_efs_file_system.main.id
subnet_id = var.subnet_ids[count.index]
security_groups = [aws_security_group.efs.id]
}
In an Auto Scaling Group architecture, awsefsmounttarget resource uses a foreach meta-argument to iterate over the awssubnet.private collection. filesystemid is set to the specific subnetid and efs_sg security group.
This enables EC2 instances within the Auto Scaling Group to share data across multiple Availability Zones, ensuring that files stored by one instance are accessible to all others in the cluster.
Security Group and Least Privilege Access
Security for the file system is enforced through the efs_sg security group. It follows the principle of least privilege by restricting access to only the compute tier.
hcl
resource "aws_security_group" "efs" {
name = "${var.project_name}-efs-sg"
description = "Allow EFS inbound traffic"
vpc_id = var.vpc_id
ingress {
description = "NFS from VPC"
from_port = 2049
to_port = 2049
protocol = "tcp"
security_groups = var.allowed_security_group_ids
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-efs-sg"
}
}
| Rule Type | Port | Protocol | Source/Destination | Purpose |
|---|---|---|---|---|
| Ingress | 2049 | TCP | awssecuritygroup.ec2_sg.id | Allows NFS traffic only from application instances |
| Egress | All | -1 | 0.0.0.0/0 | Open egress |
The diagram illustrates how the Terraform resources bridge the networking layer to the storage layer.
Backup Policy and Access Points
A production EFS setup involves more than just the file system resource.
Backup Policy optional:
hcl
resource "aws_efs_backup_policy" "policy" {
file_system_id = aws_efs_file_system.main.id
backup_policy {
status = "ENABLED"
}
}
Access Point optional:
hcl
resource "aws_efs_access_point" "test" {
file_system_id =
Outputs should be exported for downstream modules.
```hcl
output "efsid" {
value = awsefsfilesystem.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 = awsefsaccesspoint.worker.id
}
description = "Access point IDs by service"
}
```
Don't forget to export the values other modules will need.
Terraform AWS Modules for EFS
A Terraform module which creates AWS EFS elastic file system resources is available. See examples directory for working examples to reference.
Example usage:
hcl
module "efs" {
source = "terraform-aws-modules/efs/aws"
# File system
name = "example"
creation_token = "example-token"
encrypted = true
kms_key_arn = "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
# performance_mode = "maxIO"
}
Module requirements:
| Name | Version |
|---|---|
| terraform | >= 1.5.7 |
| aws | >= 6.28 |
| Name | Version |
|---|---|
| aws | >= 6.28 |
Resources created by the module:
| Name | Type |
|---|---|
| awsefsaccess_point.this | resource |
| awsefsbackup_policy.this | resource |
| awsefsfile_system.this | resource |
| awsefsfilesystempolicy.this | resource |
| awsefsmount_target.this | resource |
| awsefsreplication_configuration.this | resource |
| awssecuritygroup.this | resource |
| awsvpcsecuritygroupegress_rule.this | resource |
| awsvpcsecuritygroupingress_rule.this | resource |
Data sources:
| Name | Type |
|---|---|
| awsiampolicy_document.policy | data source |
Module input variables include:
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| access_points | A map of access point definitions to create | map(object({ | {} | no |
| attach_policy | Determines whether a policy is attached to the file system | bool | true | no |
| availabilityzonename | The AWS Availability Zone in which to create the file system. Used to create a file system that uses One Zone storage classes | string | null | no |
| bypasspolicylockoutsafetycheck | A flag to indicate whether to bypass the awsefsfilesystempolicy lockout safety check |
Configuration Patterns and Idempotency
Creation token is used to ensure idempotent provisioning. The example sets creationtoken to "appEFS" and also to "${var.projectname}-efs". Tags such as Name = "${var.project_name}-efs" support resource tracking.
Encryption is enabled with encrypted = true. KMS key ARN can be provided via kmskeyarn for customer managed encryption.
Lifecycle policy transitiontoia = "AFTER30DAYS" enables Infrequent Access transition.
Performance mode can be set, with maxIO as a commented option.
Outputs and Cross Module Integration
The file system ID export enables other modules or external systems to reference the specific storage resource.
Key outputs to publish:
- efs_id
- efsdnsname
- accesspointids
These outputs allow mounting commands and application configuration to reference the EFS DNS name and access points by service.
Summary of Resource Relationships
| Resource | Purpose |
|---|---|
| awsefsfile_system | Core scalable file storage |
| awsefsmount_target | Network attachment per subnet |
| awssecuritygroup | NFS 2049 ingress control |
| awsefsbackup_policy | Automated backup enablement |
| awsefsaccess_point | POSIX user and permission isolation |
The Terraform resources in efs.tf bridge the networking layer to the storage layer.
Conclusion
A production EFS setup involves more than just the file system resource. The awsefsfilesystem resource forms the storage core, but effective deployment requires coordinated mount targets, security groups enforcing least privilege, optional backup policy, and access points for service isolation. Terraform enables repeatable creation of these components through variables, outputs, and modules. Creation token ensures idempotency, encryption and lifecycle policies provide operational guardrails, and security groups restrict NFS traffic to the compute tier only. Using module outputs for efsid, efsdnsname and accesspointids allows clean cross module composition in full stack AWS architectures.