Introduction
When deploying applications on AWS, managing persistent storage is crucial for stateful workloads. Amazon Elastic File System (EFS) provides scalable, shared file storage that integrates seamlessly with AWS services like Lambda, Kubernetes (EKS), and EC2.
In this guide, we’ll use Terraform to:
Create an AWS EFS file system
Configure security groups for secure access
Set up EFS mount targets for private subnets
Define access points for applications
Integrate with Kubernetes Persistent Volumes (PV) and Persistent Volume Claims (PVC)
Output essential EFS parameters for further integrations
By the end of this tutorial, you'll have a fully functional EFS setup, ready to be used in a Kubernetes cluster or other AWS environments.
Step 1: Create an AWS Security Group for EFS
The security group defines access rules for EFS connections from private subnets.
resource "aws_security_group" "efs_sg" {
name = "efs-app-${var.environment}-sg"
vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id
ingress {
description = "Allow access from Lambda & VPC subnets"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = concat(
data.terraform_remote_state.vpc.outputs.private_subnets_cidr,
data.terraform_remote_state.vpc.outputs.db_subnets_cidr
)
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
✅ Why?
Ensures secure access to EFS from private subnets.
Outbound connections are unrestricted for seamless communication.
Step 2: Create AWS EFS File System
This block provisions an encrypted EFS instance with lifecycle policies to optimize costs.
resource "aws_efs_file_system" "aws_efs_file_system" {
creation_token = "${var.environment}-efs-app"
encrypted = true
lifecycle_policy {
transition_to_ia = "AFTER_14_DAYS"
}
lifecycle_policy {
transition_to_primary_storage_class = "AFTER_1_ACCESS"
}
tags = {
Name = "${var.environment}-efs-app"
}
}
✅ Why?
- Uses lifecycle policies to reduce storage costs.
- Encryption enabled for data security.
Step 3: Create EFS Mount Targets
Mount targets enable EC2 instances, Kubernetes pods, and Lambda functions to access EFS.
resource "aws_efs_mount_target" "efs_mt" {
count = length(data.terraform_remote_state.vpc.outputs.db_subnets)
file_system_id = aws_efs_file_system.aws_efs_file_system.id
subnet_id = data.terraform_remote_state.vpc.outputs.db_subnets[count.index]
security_groups = [aws_security_group.efs_sg.id]
}
✅ Why?
- Creates mount targets in each private subnet.
- Ensures secure communication via the security group.
Step 4: Create an EFS Access Point
Access points define specific access rules for applications using EFS.
resource "aws_efs_access_point" "aws_efs_access_point" {
file_system_id = aws_efs_file_system.aws_efs_file_system.id
posix_user {
gid = 1000
uid = 1000
}
root_directory {
path = "/app"
creation_info {
owner_gid = 1000
owner_uid = 1000
permissions = "777"
}
}
}
✅ Why?
- Defines POSIX permissions for secure file access.
- Ensures controlled multi-user access to the file system.
Step 5: Kubernetes Integration with PV & PVC
To use EFS as a Persistent Volume (PV) in Kubernetes, define a PersistentVolume (PV) and a PersistentVolumeClaim (PVC) using the kubectl_manifest resource.
Persistent Volume (PV)
Create a file (for example, pv-pvc.tf) with the following code:
resource "kubectl_manifest" "persistent_volume" {
yaml_body = <<YAML
apiVersion: v1
kind: PersistentVolume
metadata:
name: var.name
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 10Gi
csi:
driver: efs.csi.aws.com
volumeHandle: ${data.terraform_remote_state.efs.outputs.efs_id}::${data.terraform_remote_state.efs.outputs.efs_access_point_id}
persistentVolumeReclaimPolicy: Retain
storageClassName: efs-sc
volumeMode: Filesystem
YAML
}
Persistent Volume Claim (PVC)
resource "kubectl_manifest" "persistent_volume_claims" {
depends_on = [kubectl_manifest.persistent_volume]
yaml_body = <<YAML
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: var.name-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 10Gi
YAML
}
output.tf
output "efs_dns_name" {
value = aws_efs_file_system.aws_efs_file_system.dns_name
}
output "efs_id" {
value = aws_efs_file_system.aws_efs_file_system.id
}
output "efs_access_point_id" {
value = aws_efs_access_point.aws_efs_access_point.id
}
output "efs_id_app2" {
value = aws_efs_file_system.aws_efs_file_system_app2.id
}
output "efs_access_point_app2_id" {
value = aws_efs_access_point.aws_efs_access_point_app2.id
}
✅ Why?
- The output blocks expose essential EFS parameters, which can be referenced by other Terraform modules or Kubernetes manifests.
- The Persistent Volume (PV) uses the AWS EFS CSI driver to connect to your EFS file system.
- The Persistent Volume Claim (PVC) lets Kubernetes applications request storage dynamically.
Conclusion
With this complete setup, you now have a scalable, secure, and high-availability persistent storage solution using AWS EFS and Terraform. This guide not only provisions the necessary AWS infrastructure but also integrates with Kubernetes to manage persistent storage using PVs and PVCs.
Next Steps
Experiment with dynamic scaling by integrating AWS Auto Scaling or EFS Intelligent-Tiering.
Continue exploring Terraform modules to further streamline your infrastructure as code.
📌 Have questions or suggestions? Drop a comment below! 🚀
Top comments (0)