Amazon Elastic File System provides scalable file storage for use with Amazon EC2 instances. Deploying it with Terraform Infrastructure as Code gives repeatable, versioned control over file system creation, mount targets, security, lifecycle policies, and integration with container workloads. This guide covers a detailed Terraform implementation for EFS, from core resource definitions to module reuse and EKS integration patterns.
Overview of Amazon EFS and Terraform
Amazon Elastic File System is a network-attached NFS file system that scales elastically with demand. It is ideal for shared storage across multiple EC2 instances, container workloads on ECS and EKS, and lift-and-shift legacy applications that require a scalable, network-attached storage across multiple Availability Zones.
Using Terraform to provision EFS allows a single definition to create the file system, mount targets in each specified subnet, security groups for access control, and optional features such as encryption, backup policies, lifecycle transition to Infrequent Access, and access points. The approach is compatible with Terraform 1.0+ and AWS provider 5.x, and it requires existing VPC subnets and security groups.
Project Structure and Prerequisites
A typical EFS Terraform project is organized for clarity and reuse.
aws-efs-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Prerequisites for deployment include:
- AWS CLI configured
- Terraform installed
- VPC and subnets already configured
- Basic understanding of network file systems
These prerequisites ensure the network foundation for mount targets and security group rules is in place before Terraform applies the EFS resources.
Core Terraform Resources
The foundation of an EFS deployment is the file system resource with encryption and lifecycle policy.
```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"
}
}
```
Creation token provides an idempotent reference when creating the Elastic File System. Encryption is enabled by default in modern designs, with optional KMS key ARN support in module variants. The lifecycle policy transitions data to IA after 30 days.
Mount targets connect the file system to subnets.
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]
}
Mount targets are created in each specified subnet to enable multi-AZ architecture. The count pattern ensures one mount target per subnet ID.
Security Group and Mount Targets
Access to EFS occurs over NFS on TCP port 2049. A dedicated security group restricts inbound traffic.
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"
}
}
The ingress rule allows NFS from VPC security groups. Egress is open. Modules can accept custom security groups for access control and can be configured with securitygroupingress maps that allow NFS inbound and optionally SSH.
Lifecycle Policies and Backup
Cost optimization and durability are handled via lifecycle and backup resources.
hcl
resource "aws_efs_backup_policy" "policy" {
file_system_id = aws_efs_file_system.main.id
backup_policy {
status = "ENABLED"
}
}
Backup policy status can be ENABLED or DISABLED. A module parameter backuppolicystatus defaults to DISABLED and accepts ENABLE or DISABLED.
Lifecycle policy for the EFS file system is expressed as:
hcl
lifecycle_policy = [{
"transition_to_ia" = "AFTER_30_DAYS"
}]
The transitiontoia setting moves infrequently accessed data to Infrequent Access storage class after the defined period.
Access points provide application-level isolation.
hcl
resource "aws_efs_access_point" "test" {
file_system_id = aws_efs_file_system.main.id
}
In production EKS scenarios, dedicated access points are created per application, for example /data/podinfo, with Kubernetes StorageClass, PV, and PVC configured to use EFS.
Community Modules and Reusability
Terraform modules abstract EFS creation into reusable components. A module which creates AWS EFS elastic file system resources can be invoked as:
hcl
module "efs" {
source = "terraform-aws-modules/efs/aws"
name = "example"
creation_token = "example-token"
encrypted = true
kms_key_arn = "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
}
Another module creates an Elastic File System along with mount targets and a security group that allows access to 2049 to any instance that has the security group attached.
hcl
module "efs-0" {
source = "AustinCloudGuru/efs/aws"
vpc_id = "vpc-0156c7c6959ba5858"
name = "dev-efs"
subnet_ids = ["subnet-05b1a3ffd786709d5", "subnet-0a35212c972a2aaf05", "subnet-0d0e78f696428aa28"]
security_group_ingress = {
default = {
description = "NFS Inbound"
from_port = 2049
protocol = "tcp"
to_port = 2049
self = true
cidr_blocks = []
}
}
lifecycle_policy = [{
"transition_to_ia" = "AFTER_30_DAYS"
}]
tags = {
Terraform = "true"
Environment = "development"
}
}
Modules accept parameters such as encrypted, kmskeyid, lifecyclepolicy, name, backuppolicystatus, and securitygroup_ingress. Pinning module version is recommended for production stability.
Input Variables and Outputs
Standard module interfaces expose inputs and outputs for composition.
Input variables:
| Name | Type | Description |
|---|---|---|
| region | string | AWS region to deploy the EFS filesystem |
| environment | string | Tag to specify the deployment environment e.g., dev, staging, prod |
| subnet_ids | list | List of subnet IDs where mount targets will be created |
| securitygroupids | list | List of security group IDs to attach to mount targets |
Additional module inputs include:
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| backuppolicystatus | Enable/disable backup for EFS Filesystem. Value should be ENABLE/DISABLED. Defaults to DISABLED | string | "DISABLED" | no |
| encrypted | If true, the file system will be encrypted | bool | true | no |
| kmskeyid | If set, use a specific KMS key | string | null | no |
| lifecycle_policy | Lifecycle Policy for the EFS Filesystem | list(object({ transitiontoia = string })) | [] | no |
| name | A unique name used as reference when creating the Elastic File System to ensure idempotent file system creation | string |
Outputs for downstream modules:
| Name | Description |
|---|---|
| efs_id | The ID of the created EFS filesystem |
| mounttargetids | The list of EFS mount target IDs |
| efs_arn | The ARN of the created EFS filesystem |
Alternative outputs used in standalone configurations:
hcl
output "efs_id" {
value = aws_efs_file_system.main.id
description = "EFS file system ID"
}
output "efs_dns_name" {
value = aws_efs_file_system.main.dns_name
description = "EFS DNS name for mounting"
}
output "access_point_ids" {
value = {
api = aws_efs_access_point.api.id
worker = aws_efs_access_point.worker.id
}
description = "Access point IDs by service"
}
Exporting these values allows other modules to consume the file system identifier, DNS name, and access point IDs.
Integrating EFS with EKS Using Terraform
Integrating EFS with Amazon EKS using Terraform involves provisioning the file system, enabling encryption at rest with AWS KMS, configuring IRSA for EFS driver permissions, and creating Kubernetes PV/PVC for workload persistence.
Requirements for deployment:
| Requirement | Description |
|---|---|
| EKS Cluster | A running Amazon EKS cluster v1.28 or newer recommended |
| EFS CSI Driver Addon | The aws-efs-csi-driver addon must be installed either via Terraform aws-ia/eks-blueprints-addons or manually using kubectl / Helm |
| OIDC Provider | OIDC provider is enabled for the cluster required for IRSA |
By the end of the integration, the result is a KMS-encrypted EFS file system accessible to the EKS cluster, dedicated access points per application, Kubernetes StorageClass, PV, and PVC configured to use EFS, and verified pod read/write access to the mounted EFS volume.
This pattern is ideal for persistent storage for container workloads on ECS and EKS, shared file storage between EC2 instances, and mountable multi-AZ NFS storage for enterprise workloads.
Production Considerations
A production EFS setup involves more than just the file system resource. Considerations include tagging resources by environment and name for clarity, supporting multi-AZ architecture via mount targets in each specified subnet, accepting custom security groups for access control, and enabling backup policy and lifecycle transition for cost and durability.
Deployment readiness is verified by compatibility with Terraform 1.0+ and AWS provider 5.x, with requirements for existing VPC subnets and security groups.
Conclusion
Terraform provides a comprehensive and repeatable path to provision Amazon EFS with encryption, multi-AZ mount targets, security groups, lifecycle policies, backup policies, and access points. Using core resources gives fine-grained control over creation token, encryption, and NFS ingress on port 2049. Community modules accelerate adoption with standardized inputs for name, encrypted, kmskeyarn, lifecyclepolicy, backuppolicystatus, subnetids, and securitygroupids, while exposing outputs such as efsid, mounttargetids, and efsarn.
For container workloads, the integration with EKS via the EFS CSI driver, KMS encryption at rest, IRSA, and per-application access points delivers persistent, shared storage that scales with demand. The combination of infrastructure as code discipline and EFS elasticity makes Terraform the preferred method to build production-grade file storage on AWS.