Photo by Barez Omer on Unsplash
When EFS encryption at rest is not enabled, DescribeFileSystems returns Encrypted: false for that file system. You can’t turn encryption on afterwards: the setting is fixed when the file system is created. The fix is to replicate the data to a new file system, which EFS always creates encrypted, then move clients to it and retire the old one.
This example is for engineers who own shared file storage in AWS and need a list of every EFS file system without encryption at rest, usually because an audit, a customer questionnaire or a security review asked for it. You’ll get a TypeScript script for the AWS SDK for JavaScript v3 that finds each case where EFS encryption at rest is not enabled, tells you whether encrypted file systems use the AWS managed key or your own, and checks whether the file system policy forces clients to use TLS.
The script is report only. Nothing about EFS encryption at rest can be changed in place, so the useful output is a migration list, not a switch to flip. The same pattern exists for block storage and databases; the examples to find unencrypted EBS volumes in every Region and find unencrypted RDS instances and Aurora clusters cover those.
Why is EFS encryption at rest not enabled on some file systems?
The Amazon EFS guide to encrypting data at rest explains the difference: the EFS console enables encryption at rest by default, but when you create a file system with the AWS CLI, the API or an SDK, you must enable it explicitly. File systems created by older scripts, templates or tools that never set Encrypted: true end up unencrypted.
The same page states that after you create a file system, you can’t change its encryption setting. That’s the key fact for planning: there’s no UpdateFileSystem option for it, and no downtime-free toggle.
Encrypted file systems use one of two kinds of KMS key for file data:
aws/elasticfilesystem, the default key for Amazon EFS, at no additional charge.- A customer managed key you create in AWS KMS, which you control through its key policy and can audit and rotate yourself. The example to find KMS keys without automatic rotation checks those keys. To see who else can use them, the example to check KMS key policies for wildcard principals and open grants reads each key policy and grant.
Metadata such as file and directory names is encrypted with a key that EFS manages internally.
What does encryption in transit add?
Encryption at rest protects stored data. Encryption in transit protects NFS traffic between the client and the mount target, and it’s configured per mount: the EFS mount helper mounts with TLS when you pass the tls option. The file system itself can’t tell clients to use TLS, but a file system policy can reject clients that don’t. EFS supports the aws:SecureTransport condition key in file system policies for exactly this, so a deny statement like the one below blocks every non-TLS connection:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyNonTlsClients",
"Effect": "Deny",
"Principal": { "AWS": "*" },
"Action": "*",
"Resource": "arn:aws:elasticfilesystem:us-east-1:111122223333:file-system/fs-0d4e5f6a7b8c9d0e1",
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}
]
}
A policy that only contains this deny replaces the default policy, which grants full access to any client that can reach a mount target. Add the allow statements your clients need in the same policy, or they lose access. The script reports no (default policy) when DescribeFileSystemPolicy returns PolicyNotFound, which is how EFS signals that the default policy is in effect.
What does the script do?
- Lists replication configurations
paginateDescribeReplicationConfigurationsmaps each source file system to its destination, so an unencrypted file system that’s already being migrated shows as “migration in progress”. - Lists file systems
paginateDescribeFileSystemsreturnsEncrypted,KmsKeyId,NameandSizeInBytesfor every file system in each Region you pass. - Identifies the key typeFor encrypted file systems,
DescribeKeyreturnsKeyManager:AWSforaws/elasticfilesystem,CUSTOMERfor your own key. - Checks for a TLS-only policy
DescribeFileSystemPolicyreads the policy and looks for aDenystatement withaws:SecureTransportset tofalse.
Pagination uses the SDK’s built-in paginators; the guide to paginate AWS SDK v3 results with async iterators explains how they work.
Prerequisites
- Node.js 18 or later, npm,
tsx,@aws-sdk/client-efsand@aws-sdk/client-kms. - An AWS profile with the read-only permissions below. The script never writes, so an audit role is enough.
- The list of Regions where you run EFS. The script checks one Region per entry in
--regions.
Which IAM permissions does it need?
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadEfs",
"Effect": "Allow",
"Action": [
"elasticfilesystem:DescribeFileSystems",
"elasticfilesystem:DescribeFileSystemPolicy",
"elasticfilesystem:DescribeReplicationConfigurations"
],
"Resource": "*"
},
{
"Sid": "ReadKeyType",
"Effect": "Allow",
"Action": "kms:DescribeKey",
"Resource": "*"
}
]
}
If a customer managed key’s key policy doesn’t allow your role to call DescribeKey, the script prints unknown (AccessDeniedException) for that row instead of failing. To build a policy from your own code instead, try the IAM policy generator for TypeScript AWS SDK code.
The script to find unencrypted EFS file systems
// find-unencrypted-efs-file-systems.ts
// Lists EFS file systems in one or more Regions and reports encryption at rest (and which kind of KMS key),
// whether the file system policy denies connections without TLS, and any replication already set up.
// Report only: EFS can't turn on encryption for an existing file system, so this script changes nothing.
// Usage:
// npx tsx find-unencrypted-efs-file-systems.ts [--regions us-east-1,eu-west-1] [--only-problems]
import {
DescribeFileSystemPolicyCommand,
EFSClient,
paginateDescribeFileSystems,
paginateDescribeReplicationConfigurations,
} from "@aws-sdk/client-efs";
import { DescribeKeyCommand, KMSClient } from "@aws-sdk/client-kms";
const args = process.argv.slice(2);
const flag = (name: string): string | undefined => {
const i = args.indexOf(name);
return i >= 0 ? args[i + 1] : undefined;
};
const regions = (flag("--regions") ?? process.env.AWS_REGION ?? "us-east-1")
.split(",")
.map((r) => r.trim())
.filter(Boolean);
const onlyProblems = args.includes("--only-problems");
interface Row {
Region: string;
FileSystem: string;
Name: string;
SizeGiB: number;
AtRest: string;
TlsRequired: string;
Replica: string;
Action: string;
}
const errName = (err: unknown): string => (err instanceof Error ? err.name : String(err));
// "AWS" for the AWS managed key aws/elasticfilesystem, "CUSTOMER" for your own key.
async function keyManager(kms: KMSClient, keyId: string): Promise<string> {
try {
const { KeyMetadata } = await kms.send(new DescribeKeyCommand({ KeyId: keyId }));
return KeyMetadata?.KeyManager ?? "unknown";
} catch (err) {
return `unknown (${errName(err)})`;
}
}
// True when a Deny statement matches aws:SecureTransport = false, which blocks non-TLS NFS clients.
function deniesInsecureTransport(policyJson: string): boolean {
const doc = JSON.parse(policyJson) as { Statement?: unknown };
const statements = Array.isArray(doc.Statement) ? doc.Statement : [doc.Statement];
return statements.some((s) => {
const st = s as { Effect?: string; Condition?: Record<string, Record<string, unknown>> };
const value = st.Condition?.Bool?.["aws:SecureTransport"];
return st.Effect === "Deny" && String(value).toLowerCase() === "false";
});
}
async function tlsPolicy(efs: EFSClient, fileSystemId: string): Promise<string> {
try {
const { Policy } = await efs.send(new DescribeFileSystemPolicyCommand({ FileSystemId: fileSystemId }));
return Policy && deniesInsecureTransport(Policy) ? "yes" : "no (policy has no TLS deny)";
} catch (err) {
if (errName(err) === "PolicyNotFound") return "no (default policy)";
return `unknown (${errName(err)})`;
}
}
async function scanRegion(region: string): Promise<Row[]> {
const efs = new EFSClient({ region });
const kms = new KMSClient({ region });
// Map each source file system to its replication destinations (Region/ID/status).
const replicas = new Map<string, string>();
for await (const page of paginateDescribeReplicationConfigurations({ client: efs }, {})) {
for (const rc of page.Replications ?? []) {
const dest = (rc.Destinations ?? []).map((d) => `${d.Region}/${d.FileSystemId} (${d.Status})`).join(", ");
if (rc.SourceFileSystemId) replicas.set(rc.SourceFileSystemId, dest);
}
}
const rows: Row[] = [];
for await (const page of paginateDescribeFileSystems({ client: efs }, {})) {
for (const fs of page.FileSystems ?? []) {
if (!fs.FileSystemId) continue;
let atRest = "NOT ENCRYPTED";
if (fs.Encrypted && fs.KmsKeyId) {
const manager = await keyManager(kms, fs.KmsKeyId);
atRest = manager === "AWS" ? "aws/elasticfilesystem" : manager === "CUSTOMER" ? "customer managed key" : manager;
} else if (fs.Encrypted) {
atRest = "encrypted";
}
const tls = await tlsPolicy(efs, fs.FileSystemId);
const replica = replicas.get(fs.FileSystemId) ?? "-";
let action = "ok";
if (!fs.Encrypted) action = replica === "-" ? "migrate to an encrypted file system" : "migration in progress";
else if (!tls.startsWith("yes")) action = "add a TLS-only file system policy";
rows.push({
Region: region,
FileSystem: fs.FileSystemId,
Name: fs.Name ?? "",
SizeGiB: Math.round(((fs.SizeInBytes?.Value ?? 0) / 1024 ** 3) * 10) / 10,
AtRest: atRest,
TlsRequired: tls,
Replica: replica,
Action: action,
});
}
}
return rows;
}
async function main(): Promise<void> {
const all: Row[] = [];
for (const region of regions) all.push(...(await scanRegion(region)));
const shown = onlyProblems ? all.filter((r) => r.Action !== "ok") : all;
const unencrypted = all.filter((r) => r.AtRest === "NOT ENCRYPTED").length;
console.log(`EFS file systems: ${all.length} in ${regions.join(", ")}; without encryption at rest: ${unencrypted}`);
if (shown.length) console.table(shown);
console.log("Report only. Encryption at rest can't be enabled on an existing EFS file system.");
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
How do you run it?
npm install @aws-sdk/client-efs @aws-sdk/client-kms
npm install --save-dev tsx typescript @types/node
# One Region from your profile or AWS_REGION
AWS_PROFILE=audit npx tsx find-unencrypted-efs-file-systems.ts
# Several Regions, only rows that need action
AWS_PROFILE=audit npx tsx find-unencrypted-efs-file-systems.ts --regions us-east-1,eu-west-1 --only-problems
Sample output
EFS file systems: 4 in us-east-1; without encryption at rest: 2
┌─────────┬─────────────┬────────────────────────┬───────────────┬─────────┬─────────────────────────┬───────────────────────┬────────────────────────────────────────────┬───────────────────────────────────────┐
│ (index) │ Region │ FileSystem │ Name │ SizeGiB │ AtRest │ TlsRequired │ Replica │ Action │
├─────────┼─────────────┼────────────────────────┼───────────────┼─────────┼─────────────────────────┼───────────────────────┼────────────────────────────────────────────┼───────────────────────────────────────┤
│ 0 │ 'us-east-1' │ 'fs-0a1b2c3d4e5f60718' │ 'cms-uploads' │ 212.4 │ 'NOT ENCRYPTED' │ 'no (default policy)' │ '-' │ 'migrate to an encrypted file system' │
│ 1 │ 'us-east-1' │ 'fs-07c9e1d2b3a4f5061' │ 'build-cache' │ 38.2 │ 'NOT ENCRYPTED' │ 'no (default policy)' │ 'us-east-1/fs-0e8d7c6b5a4f3e2d1 (ENABLED)' │ 'migration in progress' │
│ 2 │ 'us-east-1' │ 'fs-0d4e5f6a7b8c9d0e1' │ 'wp-content' │ 9.7 │ 'aws/elasticfilesystem' │ 'no (default policy)' │ '-' │ 'add a TLS-only file system policy' │
│ 3 │ 'us-east-1' │ 'fs-02b3c4d5e6f7a8b9c' │ 'ml-datasets' │ 1843 │ 'customer managed key' │ 'yes' │ '-' │ 'ok' │
└─────────┴─────────────┴────────────────────────┴───────────────┴─────────┴─────────────────────────┴───────────────────────┴────────────────────────────────────────────┴───────────────────────────────────────┘
Report only. Encryption at rest can't be enabled on an existing EFS file system.
IDs and names are illustrative. cms-uploads and build-cache are the two file systems where EFS encryption at rest is not enabled; build-cache already replicates to an encrypted copy. wp-content is encrypted with the default key but still accepts plain NFS. ml-datasets uses a customer managed key and rejects clients without TLS.
How do you move an unencrypted EFS file system to an encrypted one?
EFS replication is the documented path. When you replicate to a new file system, EFS creates the destination with encryption at rest enabled, using aws/elasticfilesystem unless you pass a KmsKeyId. The key can’t be changed later, so decide on it first.
- Create the replication configurationCall
CreateReplicationConfiguration(oraws efs create-replication-configuration) with the source file system and a destination Region, plusKmsKeyIdif you want your own key. The destination is read-only while replication runs. - Create mount targets on the destinationEFS doesn’t create them for you. Put them in the subnets your clients use, with security groups that allow NFS (port 2049) from those clients.
- Wait for the initial syncData is only accessible on the destination after it completes. After that, the CloudWatch metric
TimeSinceLastSyncshows how far behind it is. - Stop writes and cut overStop the applications that write to the source, let one more sync finish, then delete the replication configuration. The replica becomes writeable. Remount clients on the new file system ID, with
tls. - Apply the TLS policy and retire the sourceAdd the file system policy above to the new file system, keep the old one read-only for a rollback window, then delete it. Protect the new one with backups; the report to check AWS Backup coverage for EFS and other resources confirms it’s in a plan.
Replication doesn’t copy lifecycle management; the destination starts without it. Re-apply it with the example to find EFS file systems without a lifecycle policy. Replication also adds about 12 MiB of metered hidden data to the destination.
To stop new unencrypted file systems from appearing, use the elasticfilesystem:Encrypted IAM condition key in identity policies or a service control policy so CreateFileSystem calls without encryption are denied.
Troubleshooting
AtRestshowsunknown (AccessDeniedException). The key policy of a customer managed key doesn’t allow your role to describe it. That’s common and not a problem for the audit; the file system is still encrypted.TlsRequiredsays no, but clients use TLS. The script checks for enforcement, not usage. Clients may mount withtlswhile the policy still allows plain NFS.ReplicationAlreadyExistswhen you start the migration. A file system can be part of only one replication configuration. TheReplicacolumn shows the existing one.- Mount fails with TLS on an older Linux distribution. The EFS docs note that some distributions ship a
stunnelversion without the TLS features the mount helper enforces; follow the EFS guide’s steps to upgradestunnel.
Ask ChatWithCloud instead
Ask ChatWithCloud “Which EFS file systems in this Region aren’t encrypted at rest?” It writes AWS SDK for JavaScript v2 code, runs it on your machine with your profile and summarizes the answer. It uses one profile and Region per session, and it runs generated code without a confirmation step, so use a read-only profile; the guide to connect ChatWithCloud to an AWS profile, SSO or role shows how to pick one. The ChatWithCloud security model and data handling page explains what’s sent for processing. For a wider review, see the guide to analyze AWS security posture with an AI CLI.
Frequently asked questions
Can I enable encryption on an existing EFS file system?
No. The encryption setting is fixed at creation. Replicate the data to a new file system, which EFS creates encrypted, then fail over to it by deleting the replication configuration.
Is EFS encrypted by default?
In the EFS console, yes. With the AWS CLI, API or SDKs, you must set encryption explicitly when you create the file system.
How do I check whether an EFS file system is encrypted?
Run aws efs describe-file-systems --query "FileSystems[].[FileSystemId,Encrypted,KmsKeyId]". false in the second column means EFS encryption at rest is not enabled for that file system.
How do I force TLS for EFS clients?
Add a file system policy statement that denies access when aws:SecureTransport is false, and mount clients with the EFS mount helper’s tls option.
Related guides
Ask your AWS account in plain English
Your first 15 runs are free, with no OpenAI key needed.
npx chatwithcloud