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) +}