George Drew
George Drew

Reputation: 33

Terraform: How would I reference a variable in For_Each that is not included in a map, such as file_system_id?

Maybe this is possible, maybe it's not. I'm attempting to mount an EFS target using some of the values stored in a var.ec2_server map which includes subnets, EBS volumes, etc.

The issue I've run into is that I created the EFS File System using a for_each statement; since the efs_file_system was created with a for_each, I must reference the attributes within specified instances when referring to the resource in other variables.

The file_system_id is only known after creation so how would I reference it within a map or other variable inside other for_each statements, such as the aws_efs_mount_target resource defined below? Will what I'm doing even work?

I'm using the antiquated resource.tf > variable.tf > terraform.tfvars (config) style code :

...the ec2.tf file:

###############################################################################
# EC2 Instance
resource "aws_instance" "ec2" {
  for_each = var.ec2_servers

  ami                     = data.aws_ami.ec2[each.key].id
  disable_api_termination = var.disable_api_termination
  iam_instance_profile    = aws_iam_instance_profile.ec2[each.key].id
  instance_type           = each.value.instance_type
  monitoring              = true
  vpc_security_group_ids  = [aws_security_group.ec2[each.key].id]
  subnet_id               = each.value.subnet_name != null ? aws_subnet.private["${each.value.vpc_name}.${each.value.subnet_name}.${each.value.availability_zone}"].id : null
  key_name                = aws_key_pair.ec2.key_name
  user_data               = each.value.user_data == "" ? null : templatefile("./${each.value.user_data}", { region = data.aws_region.current.name })
  private_ip              = each.value.private_ip

  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "required"
  }

  root_block_device {
    delete_on_termination = true
    encrypted             = true
    volume_size           = each.value.root_volume_size
    volume_type           = "gp2"
    tags = {
      Name = replace("${var.project_name}-${each.value.vpc_name}-${each.key}-EBS", " ", "")
    }
  }

  dynamic "ebs_block_device" {
    for_each = each.value.ebs_volumes
    content {
      volume_type = ebs_block_device.value.volume_type
      volume_size = ebs_block_device.value.volume_size
      device_name = ebs_block_device.value.device_name
      tags = {
      Name = replace("${var.project_name}-${each.value.vpc_name}-${each.key}-EBS", " ", "") }
    }
  }

  tags = {
    Name   = replace("${var.project_name}-${each.value.vpc_name}-${each.key}-EC2", " ", "")
    Backup = "true"
  }
}

...the efs.tf file:

###############################################################################
# Create EFS File System
resource "aws_efs_file_system" "efs" {
  for_each = {
    for object, property in var.efs_config : object => property if var.efs_config.efs_enabled
  }

  creation_token = var.efs_config.efs_creation_token
  encrypted      = var.efs_config.efs_encrypt
  kms_key_id     = aws_kms_key.efs_kms.arn

  tags = {
    Name = replace("${var.project_name}-${var.efs_config.efs_vpc}-EFS", " ", "")
  }
}

resource "aws_efs_backup_policy" "efs_backup_policy" {
  file_system_id  = "NEEDS TO BE DETERMINED"

  backup_policy {
    status = "ENABLED"
  }
}

resource "aws_efs_mount_target" "efs_mount_target" {
  for_each = var.ec2_servers

    file_system_id  = "NEEDS TO BE DETERMINED"
    subnet_id       = each.value.subnet_name == "app" ? aws_subnet.private["${each.value.vpc_name}.${each.value.subnet_name}.${each.value.availability_zone}"].id : null
    ip_address      = lookup(var.efs_config, "efs_private_ip")
    security_groups = [aws_security_group.ec2[each.key].id]
}

...the variables.tf file:

variable "ec2_servers" {
  description = "A configurable map of EC2 settings."
  type        = map(any)
}

...the terraform.tfvars file:

###############################################################################
# EFS Configurations
efs_config = {
      efs_enabled        = true
      efs_creation_token = "Prod_EFS"
      efs_encrypt        = true
      efs_vpc            = "Prod"
      efs_private_ip     = "10.200.0.5"
}

# Server Configurations
ec2_servers = {
  EC201 = {
    ami_owner        = "XXXXXXXXXXXX"
    ami_name         = "xxxxx-xxxxxx"
    instance_type    = "t2.micro"
    root_volume_size = "10"
    ebs_volumes = [
      {
        volume_size = "20"
        volume_type = "gp3"
        device_name = "/dev/xvdba"
      },
      {
        volume_size = "20"
        volume_type = "gp3"
        device_name = "/dev/xvdbb"
      }
    ]
    vpc_name          = "Prod"
    subnet_name       = "web"
    set_ec2_hostname  = false
    ec2_hostname      = "xxxxxxxxx"
    availability_zone = "a"
    public_dns        = false
    private_dns       = true
    policy_names      = []
    s3_storage        = false
    transfer_files    = false
    user_data         = "setup_ssm_linux.tftpl"
    private_ip        = "10.200.0.132"
    ingress = {
      ssh = {
        description = "Internal address"
        from_port   = 22
        to_port     = 22
        protocol    = "tcp"
        cidr_blocks = [
          "10.200.0.0/22"
        ]
      }
    }
  }
}

I've tried a number of things such as creating a data resource for aws_efs_mount_target and nothing I do seems to work. If anyone could provide a little insight, both my project leads and myself would be greatly appreciated!

If I missed anything here, please let me know and I will update the question with the relevant information.

Upvotes: 0

Views: 1090

Answers (1)

Mark B
Mark B

Reputation: 201103

Your aws_efs_backup_policy needs a for_each also, since you need to create one for each EFS volume:

resource "aws_efs_backup_policy" "efs_backup_policy" {
  for_each = aws_efs_file_system.efs

  file_system_id  = each.id

  backup_policy {
    status = "ENABLED"
  }
}

For your EFS mount target, I would use the same for_each you use for the EFS volumes:

resource "aws_efs_mount_target" "efs_mount_target" {
  for_each = {
    for object, property in var.efs_config : object => property if var.efs_config.efs_enabled
  }

    file_system_id  = aws_efs_file_system.efs[each.key].id
    ...
}

I think you need to clean up those other lookups in aws_efs_mount_target by moving those values into the efs_config var.

Upvotes: 1

Related Questions