diff --git a/README.md b/README.md
index b66c361..c10fba0 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,8 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_cloudwatch_log_group.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_default_network_acl.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_network_acl) | resource |
| [aws_ecs_cluster.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource |
+| [aws_efs_file_system.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_file_system) | resource |
+| [aws_efs_mount_target.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_mount_target) | resource |
| [aws_eip.infrastructure_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
@@ -93,12 +95,15 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_s3_bucket_server_side_encryption_configuration.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_versioning.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [aws_security_group.infrastructure_ecs_cluster_container_instances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
+| [aws_security_group.infrastructure_ecs_cluster_efs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_dns_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_dns_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_https_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_https_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
+| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_nfs_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_ingress_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
+| [aws_security_group_rule.infrastructure_ecs_cluster_efs_ingress_nfs_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_sns_topic.infrastructure_ecs_cluster_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
| [aws_sns_topic_subscription.ecs_cluster_infrastructure_draining_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_subnet.infrastructure_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
@@ -115,7 +120,12 @@ This project creates and manages resources within an AWS account for infrastruct
|------|-------------|------|---------|:--------:|
| [aws\_profile\_name\_route53\_root](#input\_aws\_profile\_name\_route53\_root) | AWS Profile name which is configured for the account in which the root Route53 Hosted Zone exists. | `string` | n/a | yes |
| [aws\_region](#input\_aws\_region) | AWS region in which to launch resources | `string` | n/a | yes |
+| [ecs\_cluster\_efs\_directories](#input\_ecs\_cluster\_efs\_directories) | ECS cluster EFS directories to create | `list(string)` | n/a | yes |
+| [ecs\_cluster\_efs\_infrequent\_access\_transition](#input\_ecs\_cluster\_efs\_infrequent\_access\_transition) | ECS cluser EFS IA transiton in days. Set to 0 to disable IA transition. | `number` | n/a | yes |
+| [ecs\_cluster\_efs\_performance\_mode](#input\_ecs\_cluster\_efs\_performance\_mode) | ECS cluser EFS performance mode | `string` | n/a | yes |
+| [ecs\_cluster\_efs\_throughput\_mode](#input\_ecs\_cluster\_efs\_throughput\_mode) | ECS cluser EFS throughput mode | `string` | n/a | yes |
| [enable\_infrastructure\_ecs\_cluster](#input\_enable\_infrastructure\_ecs\_cluster) | Enable creation of infrastructure ECS cluster, to place ECS services | `bool` | n/a | yes |
+| [enable\_infrastructure\_ecs\_cluster\_efs](#input\_enable\_infrastructure\_ecs\_cluster\_efs) | Conditionally create and mount EFS to the ECS cluster instances | `bool` | n/a | yes |
| [enable\_infrastructure\_route53\_hosted\_zone](#input\_enable\_infrastructure\_route53\_hosted\_zone) | Creates a Route53 hosted zone, where DNS records will be created for resources launched within this module. | `bool` | n/a | yes |
| [environment](#input\_environment) | The environment name to be used as part of the resource prefix | `string` | n/a | yes |
| [infrastructure\_dockerhub\_email](#input\_infrastructure\_dockerhub\_email) | Dockerhub email | `string` | n/a | yes |
diff --git a/ec2-userdata/ecs-instance.tpl b/ec2-userdata/ecs-instance.tpl
index d87d551..417ac6a 100644
--- a/ec2-userdata/ecs-instance.tpl
+++ b/ec2-userdata/ecs-instance.tpl
@@ -28,3 +28,15 @@ fi
sudo yum install -y \
jq \
rsync
+
+# EFS
+%{if efs_id != "" ~}
+sudo mkdir -p /mnt/efs
+sudo yum install -y nfs-utils
+sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${efs_id}.efs.${region}.amazonaws.com:/ /mnt/efs
+echo '${efs_id}.efs.${region}.amazonaws.com:/ /mnt/efs nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 0 0' | sudo tee -a /etc/fstab
+${if efs_dirs != "" ~}
+cd /mnt/efs
+mkdir -p ${efs_dirs}
+cd ..
+${~ endif}${endif}
diff --git a/ecs-cluster-infrastructure.tf b/ecs-cluster-infrastructure.tf
index 575520f..172ff78 100644
--- a/ecs-cluster-infrastructure.tf
+++ b/ecs-cluster-infrastructure.tf
@@ -101,6 +101,18 @@ resource "aws_security_group_rule" "infrastructure_ecs_cluster_container_instanc
security_group_id = aws_security_group.infrastructure_ecs_cluster_container_instances[0].id
}
+resource "aws_security_group_rule" "infrastructure_ecs_cluster_container_instances_egress_nfs_tcp" {
+ count = local.enable_infrastructure_ecs_cluster && local.enable_infrastructure_ecs_cluster_efs ? 1 : 0
+
+ description = "Allow NFS tcp outbound to EFS security group"
+ type = "egress"
+ from_port = 2049
+ to_port = 2049
+ protocol = "tcp"
+ source_security_group_id = aws_security_group_rule.infrastructure_ecs_cluster_efs_ingress_nfs_tcp[0].id
+ security_group_id = aws_security_group.infrastructure_ecs_cluster_container_instances[0].id
+}
+
resource "aws_iam_role" "infrastructure_ecs_cluster" {
count = local.enable_infrastructure_ecs_cluster ? 1 : 0
diff --git a/efs-infrastructure.tf b/efs-infrastructure.tf
new file mode 100644
index 0000000..03df881
--- /dev/null
+++ b/efs-infrastructure.tf
@@ -0,0 +1,48 @@
+resource "aws_efs_file_system" "infrastructure_ecs_cluster" {
+ count = local.enable_infrastructure_ecs_cluster_efs ? 1 : 0
+
+ encrypted = local.infrastructure_kms_encryption
+ kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].id : null
+ performance_mode = local.ecs_cluster_efs_performance_mode
+ throughput_mode = local.ecs_cluster_efs_throughput_mode
+
+ dynamic "lifecycle_policy" {
+ for_each = local.ecs_cluster_efs_infrequent_access_transition != 0 ? [1] : []
+ content {
+ transition_to_ia = local.ecs_cluster_efs_infrequent_access_transition
+ transition_to_primary_storage_class = "AFTER_1_ACCESS"
+ }
+ }
+}
+
+resource "aws_efs_mount_target" "infrastructure_ecs_cluster" {
+ for_each = local.enable_infrastructure_ecs_cluster_efs ? local.infrastructure_vpc_network_enable_private ? [
+ for subnet in aws_subnet.infrastructure_private : subnet.id
+ ] : local.infrastructure_vpc_network_enable_public ? [
+ for subnet in aws_subnet.infrastructure_private : subnet.id
+ ] : [] : []
+
+ file_system_id = aws_efs_file_system.infrastructure_ecs_cluster[0].id
+ subnet_id = each.value
+ security_groups = local.enable_infrastructure_ecs_cluster ? [aws_security_group.infrastructure_ecs_cluster_efs[0].id] : []
+}
+
+resource "aws_security_group" "infrastructure_ecs_cluster_efs" {
+ count = local.enable_infrastructure_ecs_cluster_efs && local.enable_infrastructure_ecs_cluster ? 1 : 0
+
+ name = "${local.resource_prefix}-infrastructure-ecs-cluster-efs"
+ description = "Infrastructure ECS cluster EFS"
+ vpc_id = aws_vpc.infrastructure[0].id
+}
+
+resource "aws_security_group_rule" "infrastructure_ecs_cluster_efs_ingress_nfs_tcp" {
+ count = local.enable_infrastructure_ecs_cluster_efs && local.enable_infrastructure_ecs_cluster ? 1 : 0
+
+ description = "Allow ECS instances access to EFS (NFS) tcp"
+ type = "ingress"
+ from_port = 2049
+ to_port = 2049
+ protocol = "tcp"
+ source_security_group_id = aws_security_group.infrastructure_ecs_cluster_container_instances[0].id
+ security_group_id = aws_security_group.infrastructure_ecs_cluster_efs[0].id
+}
diff --git a/locals.tf b/locals.tf
index 38f9f1d..395d46b 100644
--- a/locals.tf
+++ b/locals.tf
@@ -114,9 +114,19 @@ locals {
dockerhub_token = local.infrastructure_dockerhub_token,
dockerhub_email = local.infrastructure_dockerhub_email,
docker_storage_size = local.infrastructure_ecs_cluster_ebs_docker_storage_volume_size
+ efs_id = local.enable_infrastructure_ecs_cluster_efs && (
+ local.infrastructure_vpc_network_enable_private || local.infrastructure_vpc_network_enable_public
+ ) ? aws_efs_file_system.infrastructure_ecs_cluster[0].id : "",
+ efs_dirs = join(" ", local.ecs_cluster_efs_directories)
})
)
+ enable_infrastructure_ecs_cluster_efs = var.enable_infrastructure_ecs_cluster_efs && local.infrastructure_vpc
+ ecs_cluster_efs_performance_mode = var.ecs_cluster_efs_performance_mode
+ ecs_cluster_efs_throughput_mode = var.ecs_cluster_efs_throughput_mode
+ ecs_cluster_efs_infrequent_access_transition = var.ecs_cluster_efs_infrequent_access_transition
+ ecs_cluster_efs_directories = var.ecs_cluster_efs_directories
+
default_tags = {
Project = local.project_name,
Infrastructure = local.infrastructure_name,
diff --git a/variables.tf b/variables.tf
index cbdf658..5347456 100644
--- a/variables.tf
+++ b/variables.tf
@@ -264,3 +264,28 @@ variable "infrastructure_ecs_cluster_max_instance_lifetime" {
description = "Maximum lifetime in seconds of an instance within the ECS cluster"
type = number
}
+
+variable "enable_infrastructure_ecs_cluster_efs" {
+ description = "Conditionally create and mount EFS to the ECS cluster instances"
+ type = bool
+}
+
+variable "ecs_cluster_efs_performance_mode" {
+ description = "ECS cluser EFS performance mode"
+ type = string
+}
+
+variable "ecs_cluster_efs_throughput_mode" {
+ description = "ECS cluser EFS throughput mode"
+ type = string
+}
+
+variable "ecs_cluster_efs_infrequent_access_transition" {
+ description = "ECS cluser EFS IA transiton in days. Set to 0 to disable IA transition."
+ type = number
+}
+
+variable "ecs_cluster_efs_directories" {
+ description = "ECS cluster EFS directories to create"
+ type = list(string)
+}