in packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts [730:891]
constructor(scope: Construct, id: string, props: FileSystemProps) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
this.props = props;
if (props.performanceMode === PerformanceMode.MAX_IO && props.oneZone) {
throw new ValidationError('performanceMode MAX_IO is not supported for One Zone file systems.', this);
}
if (props.oneZone) { this.oneZoneValidation(); }
if (props.throughputMode === ThroughputMode.PROVISIONED && props.provisionedThroughputPerSecond === undefined) {
throw new ValidationError('Property provisionedThroughputPerSecond is required when throughputMode is PROVISIONED', this);
}
if (props.throughputMode === ThroughputMode.ELASTIC && props.performanceMode === PerformanceMode.MAX_IO) {
throw new ValidationError('ThroughputMode ELASTIC is not supported for file systems with performanceMode MAX_IO', this);
}
if (props.replicationConfiguration && props.replicationOverwriteProtection === ReplicationOverwriteProtection.DISABLED) {
throw new ValidationError('Cannot configure \'replicationConfiguration\' when \'replicationOverwriteProtection\' is set to \'DISABLED\'', this);
}
// we explicitly use 'undefined' to represent 'false' to maintain backwards compatibility since
// its considered an actual change in CloudFormations eyes, even though they have the same meaning.
const encrypted = props.encrypted ?? (FeatureFlags.of(this).isEnabled(
cxapi.EFS_DEFAULT_ENCRYPTION_AT_REST) ? true : undefined);
// LifecyclePolicies must be an array of objects, each containing a single policy
const lifecyclePolicies: CfnFileSystem.LifecyclePolicyProperty[] = [];
if (props.lifecyclePolicy) {
lifecyclePolicies.push({ transitionToIa: props.lifecyclePolicy });
}
if (props.outOfInfrequentAccessPolicy) {
lifecyclePolicies.push({ transitionToPrimaryStorageClass: props.outOfInfrequentAccessPolicy });
}
if (props.transitionToArchivePolicy) {
lifecyclePolicies.push({ transitionToArchive: props.transitionToArchivePolicy });
}
// if props.vpcSubnets.availabilityZones is defined, select the first one as the zone otherwise
// the first AZ of the VPC.
const oneZoneAzName = props.vpcSubnets?.availabilityZones ?
props.vpcSubnets.availabilityZones[0] : props.vpc.availabilityZones[0];
const fileSystemProtection = props.replicationOverwriteProtection !== undefined ? {
replicationOverwriteProtection: props.replicationOverwriteProtection,
} : undefined;
const replicationConfiguration = props.replicationConfiguration ? {
destinations: [
{
fileSystemId: props.replicationConfiguration.destinationFileSystem?.fileSystemId,
kmsKeyId: props.replicationConfiguration.kmsKey?.keyArn,
region: props.replicationConfiguration.destinationFileSystem ?
props.replicationConfiguration.destinationFileSystem.env.region :
(props.replicationConfiguration.region ?? Stack.of(this).region),
availabilityZoneName: props.replicationConfiguration.availabilityZone,
},
],
} : undefined;
this._resource = new CfnFileSystem(this, 'Resource', {
encrypted: encrypted,
kmsKeyId: props.kmsKey?.keyArn,
lifecyclePolicies: lifecyclePolicies.length > 0 ? lifecyclePolicies : undefined,
performanceMode: props.performanceMode,
throughputMode: props.throughputMode,
provisionedThroughputInMibps: props.provisionedThroughputPerSecond?.toMebibytes(),
backupPolicy: props.enableAutomaticBackups ? { status: 'ENABLED' } : undefined,
fileSystemPolicy: Lazy.any({
produce: () => {
const denyAnonymousAccessFlag = FeatureFlags.of(this).isEnabled(cxapi.EFS_DENY_ANONYMOUS_ACCESS) ?? false;
const denyAnonymousAccessByDefault = denyAnonymousAccessFlag || this._grantedClient;
const allowAnonymousAccess = props.allowAnonymousAccess ?? !denyAnonymousAccessByDefault;
if (!allowAnonymousAccess) {
this.addToResourcePolicy(new iam.PolicyStatement({
principals: [new iam.AnyPrincipal()],
actions: [
ClientAction.WRITE,
ClientAction.ROOT_ACCESS,
],
conditions: {
Bool: {
'elasticfilesystem:AccessedViaMountTarget': 'true',
},
},
}));
}
return this._fileSystemPolicy;
},
}),
fileSystemProtection,
availabilityZoneName: props.oneZone ? oneZoneAzName : undefined,
replicationConfiguration,
});
this._resource.applyRemovalPolicy(props.removalPolicy);
this.fileSystemId = this._resource.ref;
this.fileSystemArn = this._resource.attrArn;
this._fileSystemPolicy = props.fileSystemPolicy;
Tags.of(this).add('Name', props.fileSystemName || this.node.path);
const securityGroup = (props.securityGroup || new ec2.SecurityGroup(this, 'EfsSecurityGroup', {
vpc: props.vpc,
}));
this.connections = new ec2.Connections({
securityGroups: [securityGroup],
defaultPort: ec2.Port.tcp(FileSystem.DEFAULT_PORT),
});
// When oneZone is specified, to avoid deployment failure, mountTarget should also be created only in the specified AZ.
let subnetSelection: ec2.SubnetSelection;
if (props.oneZone) {
subnetSelection = {
availabilityZones: [oneZoneAzName],
};
} else {
subnetSelection = props.vpcSubnets ?? { onePerAz: true };
}
const subnets = props.vpc.selectSubnets(subnetSelection);
// We now have to create the mount target for each of the mentioned subnet
// we explicitly use FeatureFlags to maintain backwards compatibility
const useMountTargetOrderInsensitiveLogicalID = FeatureFlags.of(this).isEnabled(cxapi.EFS_MOUNTTARGET_ORDERINSENSITIVE_LOGICAL_ID);
this.mountTargetsAvailable = [];
if (useMountTargetOrderInsensitiveLogicalID) {
subnets.subnets.forEach((subnet) => {
const subnetUniqueId = Token.isUnresolved(subnet.node.id) ? Names.uniqueResourceName(subnet, { maxLength: 16 }) : subnet.node.id;
const mountTarget = new CfnMountTarget(this,
`EfsMountTarget-${subnetUniqueId}`,
{
fileSystemId: this.fileSystemId,
securityGroups: Array.of(securityGroup.securityGroupId),
subnetId: subnet.subnetId,
});
this._mountTargetsAvailable.add(mountTarget);
});
} else {
let mountTargetCount = 0;
subnets.subnetIds.forEach((subnetId: string) => {
const mountTarget = new CfnMountTarget(this,
'EfsMountTarget' + (++mountTargetCount),
{
fileSystemId: this.fileSystemId,
securityGroups: Array.of(securityGroup.securityGroupId),
subnetId,
});
this._mountTargetsAvailable.add(mountTarget);
});
}
this.mountTargetsAvailable = this._mountTargetsAvailable;
}