A Terraform primitive module for creating and managing AWS Elastic File System (EFS) file systems. This module provides a standardized way to create EFS file systems with configurable encryption, performance modes, throughput settings, and lifecycle policies for cost optimization.
- Encryption at Rest: Supports both AWS managed and customer managed KMS keys
- Flexible Storage Options: Multi-AZ (high availability) or One Zone (cost optimized) storage classes
- Performance Modes: Choose between General Purpose (lower latency) or Max I/O (higher throughput)
- Throughput Modes: Bursting, Elastic (auto-scaling), or Provisioned throughput
- Lifecycle Management: Automated transitions between storage classes (Standard, IA, Archive) for cost optimization
- Protection Controls: Configurable replication overwrite protection
- Comprehensive Tagging: Support for custom tags with automatic ManagedBy tag and optional Name tag
- Input Validation: Built-in validation for performance mode and throughput mode values
module "efs" {
source = "github.com/launchbynttdata/tf-aws-module_primitive-efs_file_system?ref=1.0.0"
creation_token = "my-app-efs"
name = "My Application EFS"
encrypted = true
tags = {
Environment = "production"
Application = "my-app"
}
}module "efs" {
source = "github.com/launchbynttdata/tf-aws-module_primitive-efs_file_system?ref=1.0.0"
creation_token = "my-app-efs"
name = "Production EFS"
encrypted = true
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
performance_mode = "generalPurpose"
throughput_mode = "elastic"
# Cost optimization with lifecycle policies
lifecycle_policy = {
transition_to_ia = "AFTER_30_DAYS"
transition_to_primary_storage_class = "AFTER_1_ACCESS"
transition_to_archive = "AFTER_90_DAYS"
}
protection = {
replication_overwrite = "DISABLED"
}
tags = {
Environment = "production"
Application = "my-app"
CostCenter = "engineering"
}
}module "efs_one_zone" {
source = "github.com/launchbynttdata/tf-aws-module_primitive-efs_file_system?ref=1.0.0"
creation_token = "my-dev-efs"
name = "Development EFS"
availability_zone_name = "us-east-1a" # Creates One Zone storage
encrypted = true
throughput_mode = "bursting"
tags = {
Environment = "development"
}
}This module uses separate lifecycle_policy blocks for each transition type as required by AWS Provider version 5.100.0. Each transition rule (to IA, to Archive, to Primary Storage) must be defined in its own block. See the main.tf file for detailed implementation comments.
This module creates only the EFS file system resource itself. To mount the file system to EC2 instances or other compute resources, you'll need to create EFS mount targets separately using a mount target module or resource. Mount targets require:
- VPC subnet IDs
- Security groups for access control
- Network connectivity to your compute resources
- Standard: Default storage class for frequently accessed files
- Infrequent Access (IA): Lower cost for files accessed less frequently
- Archive: Lowest cost for long-term storage, rarely accessed files
- Use lifecycle policies to automatically transition files between storage classes based on access patterns
- Bursting: Throughput scales with file system size (good for baseline workloads)
- Elastic: Automatically scales throughput up/down based on workload (recommended for variable workloads)
- Provisioned: Fixed throughput independent of storage size (use when you need guaranteed throughput)
Install required development dependencies:
make configure-dependencies
make configure-git-hooksThis installs:
- Terraform
- Go
- Pre-commit hooks
- Other development tools specified in
.tool-versions
- Identify the AWS resource you're wrapping (e.g.,
aws_eks_cluster) - Review AWS documentation for the resource to understand all available parameters
- Study similar primitive modules for patterns and best practices
Your primitive module should include these core files:
- Contains the primary resource declaration
- Should be clean and focused on the single resource
- Example:
resource "aws_eks_cluster" "this" {
name = var.name
role_arn = var.role_arn
version = var.kubernetes_version
vpc_config {
subnet_ids = var.vpc_config.subnet_ids
security_group_ids = var.vpc_config.security_group_ids
endpoint_private_access = var.vpc_config.endpoint_private_access
endpoint_public_access = var.vpc_config.endpoint_public_access
public_access_cidrs = var.vpc_config.public_access_cidrs
}
tags = merge(
var.tags,
local.default_tags
)
}- Define all configurable parameters
- Include clear descriptions for each variable
- Set sensible defaults where appropriate
- Use validation rules to enforce constraints, but only when the validations can be made precise.
- Alternatively, use
checkblocks to create more complicated validations. (Requires terraform ~> 1.12) - Example:
variable "name" {
description = "Name of the EKS cluster"
type = string
validation {
condition = length(var.name) <= 100
error_message = "Cluster name must be 100 characters or less"
}
}
variable "kubernetes_version" {
description = "Kubernetes version to use for the EKS cluster"
type = string
default = null
validation {
condition = var.kubernetes_version == null || can(regex("^1\\.(2[89]|[3-9][0-9])$", var.kubernetes_version))
error_message = "Kubernetes version must be 1.28 or higher"
}
}- Export all useful attributes of the resource
- Include comprehensive outputs for downstream consumption
- Document what each output provides
- Example:
output "id" {
description = "The ID of the EKS cluster"
value = aws_eks_cluster.this.id
}
output "arn" {
description = "The ARN of the EKS cluster"
value = aws_eks_cluster.this.arn
}
output "endpoint" {
description = "The endpoint for the EKS cluster API server"
value = aws_eks_cluster.this.endpoint
}- Define local values and transformations
- Include standard tags (e.g.,
provisioner = "Terraform") - Example:
locals {
default_tags = {
provisioner = "Terraform"
}
}- Specify required Terraform and provider versions
- Example:
terraform {
required_version = "~> 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.100"
}
}
}Create example configurations in the examples/ directory:
- Minimal, working configuration
- Uses only required variables
- Good for quick starts and basic testing
- Comprehensive configuration showing all features
- Demonstrates advanced options
- Includes comments explaining choices
Each example should include:
main.tf- The module invocationvariables.tf- Example variablesoutputs.tf- Pass-through outputstest.tfvars- Test values for automated testingREADME.md- Documentation for the example
Update the test files in tests/:
Write functional tests that verify:
- The resource is created successfully
- Resource properties match expectations
- Outputs are correct
- Integration with AWS SDK to verify actual state
Define the configuration structure for your tests:
type ThisTFModuleConfig struct {
Name string `json:"name"`
KubernetesVersion string `json:"kubernetes_version"`
// ... other fields
}- Update test names to match your module
- Configure test flags (e.g., idempotency settings)
- Adjust test context as needed
-
Update README.md with:
- Overview of the module
- Feature list
- Usage examples
- Input/output documentation
- Validation rules
-
Document validation rules clearly so users understand constraints.
- Run local validation:
make checkThis runs:
- Terraform fmt, validate, and plan
- Go tests with Terratest
- Pre-commit hooks
- Security scans
- Test with real infrastructure:
cd examples/simple
terraform init
terraform plan -var-file=test.tfvars -out=the.tfplan
terraform apply the.tfplan- Verify outputs:
terraform output- Clean up:
terraform destroy -var-file=test.tfvars- Write a comprehensive README following the pattern in the example modules
- Add files to commit
git add . - Run pre-commit hooks manually
pre-commit run - Resolve any pre-commit issues
- Push branch to github
- Repository:
tf-aws-module_primitive-<resource_name> - Resource identifier: Use
thisfor the primary resource. - Variables: Use snake_case.
- Match AWS resource parameter names where possible.
- Provide sensible defaults when safe to do so.
- Use
nullas default for optional complex objects. - Include validation rules with clear error messages.
- Group related parameters using object types.
- Document expected formats and constraints.
- Export all significant resource attributes.
- Use clear, descriptive output names.
- Include descriptions for all outputs.
- Consider downstream module needs.
- Always include a
tagsvariable, unless the resource does not support tags. - Merge with
local.default_tagsincludingprovisioner = "Terraform". - Use provider default tags when appropriate.
- Validate input constraints at the variable level.
- Provide helpful error messages.
- Check for common misconfigurations.
- Validate relationships between variables.
- Test the minimal example (required parameters only).
- Test the complete example (all features).
- Verify resource creation and properties.
- Test idempotency where applicable.
- Test validation rules by expecting failures.
- Clear overview of the module's purpose.
- Feature list highlighting key capabilities.
- Multiple usage examples (minimal and complete).
- Comprehensive input/output tables.
- Document validation rules and constraints.
- Include links to relevant AWS documentation.
After initialization, your module should have this structure:
tf-aws-module_primitive-<resource_name>/
├── .github/
│ └── workflows/ # CI/CD workflows
├── examples/
│ ├── simple/ # Minimal example
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── test.tfvars
│ │ └── README.md
│ └── complete/ # Comprehensive example
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── test.tfvars
│ └── README.md
├── tests/
│ ├── post_deploy_functional/
│ │ └── main_test.go
│ ├── testimpl/
│ │ ├── test_impl.go
│ │ └── types.go
├── .gitignore
├── .pre-commit-config.yaml
├── .tool-versions
├── go.mod
├── go.sum
├── LICENSE
├── locals.tf
├── main.tf
├── Makefile
├── outputs.tf
├── README.md
├── variables.tf
└── versions.tf
| Target | Description |
|---|---|
make init-module |
Initialize new module from template (run once after creating from template) |
make configure-dependencies |
Install required development tools |
make configure-git-hooks |
Set up pre-commit hooks |
make check |
Run all validation and tests |
make configure |
Full setup (dependencies + hooks + repo sync) |
make clean |
Remove downloaded components |
- Review example modules: EKS Cluster, KMS Key
- Check the Launch Common Automation Framework documentation.
- Reach out to the platform team for guidance.
Follow the established patterns in existing primitive modules. All modules should:
- Pass
make checkvalidation. - Include comprehensive tests.
- Follow naming conventions.
- Include clear documentation.
- Use semantic versioning.
| Name | Version |
|---|---|
| terraform | ~> 1.5 |
| aws | ~> 5.100 |
No modules.
| Name | Type |
|---|---|
| aws_efs_file_system.this | resource |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| creation_token | Required unique identifier for the EFS file system. Must be unique within your AWS account and region and cannot be empty | string |
n/a | yes |
| name | Required friendly name for the EFS file system. Will be added as a 'Name' tag and cannot be empty | string |
n/a | yes |
| availability_zone_name | The AWS Availability Zone in which to create the file system. Used to create a file system that uses One Zone storage classes. If omitted, Multi-AZ storage will be used | string |
null |
no |
| encrypted | If true, the disk will be encrypted | bool |
true |
no |
| kms_key_id | ARN for the KMS encryption key. If set, the EFS file system will be encrypted at rest using this key | string |
null |
no |
| performance_mode | The file system performance mode. Valid values: 'generalPurpose' (default, lower latency for most workloads) or 'maxIO' (higher aggregate throughput for highly parallelized workloads) | string |
"generalPurpose" |
no |
| throughput_mode | Throughput mode for the file system. Valid values: 'bursting' (scales with file system size), 'elastic' (automatically scales based on workload), or 'provisioned' (fixed throughput - requires provisioned_throughput_in_mibps to be set) | string |
"bursting" |
no |
| provisioned_throughput_in_mibps | The throughput, measured in MiB/s, that you want to provision for the file system. Only applicable when throughput_mode is set to 'provisioned' | number |
null |
no |
| lifecycle_policy | Lifecycle policy for the file system. Supports transition_to_ia (AFTER_7_DAYS, AFTER_14_DAYS, AFTER_30_DAYS, AFTER_60_DAYS, AFTER_90_DAYS, AFTER_1_DAY, AFTER_180_DAYS, AFTER_270_DAYS, AFTER_365_DAYS), transition_to_primary_storage_class (AFTER_1_ACCESS), and transition_to_archive (AFTER_1_DAY, AFTER_7_DAYS, AFTER_14_DAYS, AFTER_30_DAYS, AFTER_60_DAYS, AFTER_90_DAYS, AFTER_180_DAYS, AFTER_270_DAYS, AFTER_365_DAYS) | object({ |
null |
no |
| protection | Protection configuration for the file system. Supports replication_overwrite (ENABLED, DISABLED, REPLICATING) | object({ |
null |
no |
| tags | A map of tags to assign to the EFS file system | map(string) |
{} |
no |
| Name | Description |
|---|---|
| file_system_id | The ID of the EFS file system |
| file_system_arn | Amazon Resource Name of the file system |
| file_system_dns_name | The DNS name for the filesystem |
| file_system_creation_token | The creation token of the EFS file system |
| file_system_availability_zone_id | The identifier of the Availability Zone in which the file system's One Zone storage classes exist |
| file_system_availability_zone_name | The Availability Zone name in which the file system's One Zone storage classes exist |
| file_system_number_of_mount_targets | The current number of mount targets that the file system has |
| file_system_owner_id | The AWS account that created the file system |
| file_system_size_in_bytes | The latest known metered size (in bytes) of data stored in the file system |
| file_system_name | The value of the file system's Name tag |