Home > Software design >  How can I have a Terraform output become a permanent value in a userdata script?
How can I have a Terraform output become a permanent value in a userdata script?

Time:10-23

I'm not sure what the best way to do this is - but I want to deploy EFS and an ASG Launch Template with Terraform. I'd like my userdata script (in my launch template) to run commands to mount to EFS

For example:

sudo mount -t efs -o tls fs-0b28edbb9efe91c25:/ efs

My issue is: I need my userdata script to receive my EFS ID, however, this can't just happen on my initial deploy, I also need this to happen whenever I perform a rolling update. I want to be able to change the AMI ID in my launch template, which will perform a rolling update when I run terraform apply and need my EFS ID to be in my userdata script to run the command to mount EFS.

Is there a way to have a terraform output get permanently added to my Userdata script? What are other alternatives for making this happen? Would it involve Cloudformation or other AWS services?

main.tf

resource "aws_vpc" "mtc_vpc" {
  cidr_block           = "10.123.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "dev"
  }
}

resource "aws_launch_template" "foobar" {
  name_prefix   = "LTTest"
  image_id      = "ami-017c001a88dd93847"
  instance_type = "t2.micro"
  update_default_version = true

  key_name = "lttest"

  user_data = base64encode(templatefile("${path.module}/userdata.sh", {efs_id = aws_efs_file_system.foo.id}))

  iam_instance_profile {
    name = aws_iam_instance_profile.test_profile.name
  }

  vpc_security_group_ids = [aws_security_group.mtc_sg.id]
}

resource "aws_autoscaling_group" "bar" {
  desired_capacity   = 2
  max_size           = 2
  min_size           = 2

  vpc_zone_identifier  = [
    aws_subnet.mtc_public_subnet1.id
    ]

  instance_refresh {
    strategy = "Rolling"
    preferences {
      min_healthy_percentage = 50
    }
  }

  launch_template {
    id      = "${aws_launch_template.foobar.id}"
    version = aws_launch_template.foobar.latest_version
  }
}

resource "aws_efs_file_system" "foo" {
  creation_token   = "jira-efs"
}

resource "aws_efs_mount_target" "alpha" {
  file_system_id = aws_efs_file_system.foo.id
  subnet_id      = aws_subnet.mtc_public_subnet1.id
  security_groups = [aws_security_group.mtc_sg.id]
}

Update:

User-data Script:

#!/usr/bin/env bash

sudo yum install -y amazon-efs-utils

sudo yum install -y git

cd /home/ec2-user
mkdir efs

sudo mount -t efs -o tls ${efs_id}:/ efs

CodePudding user response:

There are a few ways to do this. A couple that come to mind are:

  1. Provide the EFS ID to the user data script using the templatefile() function.
  2. Give your EC2 instance permissions (via IAM) to use the EFS API to search for the ID.

The first option is probably the most practical.

First, define your EFS filesystem (and associated aws_efs_mount_target and aws_efs_access_point resources, but I'll omit those here):

resource "aws_efs_file_system" "efs" {}

Now you can define the user data with the templatefile() function:

resource "aws_launch_template" "foo" {
  # ... all the attributes ...

  user_data = base64encode(templatefile("${path.module}/user-data.sh.tpl", {
    efs_id = aws_efs_file_system.efs.id # Use dns_name or id here
  })) 
}

The contents of user-data.sh.tpl can have all your set up steps, including the filesystem mount:

sudo mount -t efs -o tls ${efs_id}:/ efs

When Terraform renders the user data in the launch template, it will substitute the variable.

  • Related