0
votes

Terraform version = 0.12

resource "aws_instance" "bespin-ec2-web" {
  ami = "ami-0bea7fd38fabe821a"
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.bespin-sg.id]
  subnet_id = aws_subnet.bespin-subnet-private-a.id
  associate_public_ip_address = true
  tags = {
    Name = "bespin-ec2-web-a"
  }
  user_data = <<EOF
   #!/bin/bash
   USERS="bespin"
   GROUP="bespin"
   for i in $USERS; do
   adduser ${i} -g ${GROUP};
   echo ${i}:${i}1! | chpasswd;

   cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config_old
   sed -i 's/PasswordAuthentication no/#PasswordAuthentication no/' /etc/ssh/sshd_config
   sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config
   systemctl restart sshd
EOF
}

Why do I get an error when running terraform plan?

Error: Invalid reference

on instance.tf line 15, in resource "aws_instance" "bespin-ec2-web": 15: adduser ${i} -g ${GROUP};

A reference to a resource type must be followed by at least one attribute access, specifying the resource name.

Error: Invalid reference

on instance.tf line 15, in resource "aws_instance" "bespin-ec2-web": 15: adduser ${i} -g ${GROUP};

A reference to a resource type must be followed by at least one attribute access, specifying the resource name.

2
You needed to escape the ${} with another $ if you wanted to keep it inline. Also you shouldn't indent inside a HEREDOC.ydaetskcoR
Thanks was solved. Thank you!sangyul lee

2 Answers

1
votes

The direct reason for the error here is that ${ ... } is Terraform's string template interpolation syntax, so Terraform is understanding ${GROUP} as an attempt to interpolate the expression GROUP, which is not a valid Terraform expression. As the error message implies, Terraform is understanding GROUP as a resource type, as if this were the first part of a reference like aws_instance.foo.

The smallest change to fix this is to escape the ${ ... } sequence by adding an additional $ in front, so that the literal ${GROUP} can pass through to the user_data value:

  user_data = <<-EOF
   #!/bin/bash
   USERS="bespin"
   GROUP="bespin"
   for i in $USERS; do
   adduser ${i} -g $${GROUP};
   echo $${i}:$${i}1! | chpasswd;

   cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config_old
   sed -i 's/PasswordAuthentication no/#PasswordAuthentication no/' /etc/ssh/sshd_config
   sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config
   systemctl restart sshd
  EOF

Notice that the escaping is needed only for ${ ... }, not for sequences like $USERS: Terraform does not interpret a single dollar sign as a special character, so that will always pass through literally.


This particular user_data expression is totally static and doesn't include any intended Terraform template syntax, so it's possible to avoid the escaping altogether by moving the user_data value into a separate file and use the file function to take the content of that file literally (no template interpretation at all):

  user_data = file("${path.module}/user_data.sh")

Sometimes the user_data does need to include data interpolated from elsewhere in the Terraform configuration, mixed with shell syntax. It's possible to use escaping as described above to do that, but as the needed string gets more complicated it can be useful to separate the part containing the interpolations from the literal part in order to get the best of both worlds and avoid the escaping within the main body of the script:

  user_data = <<-EOT
    EXAMPLE_VARIABLE='${var.example}'
    ${file("${path.module}/user_data.sh")}
  EOT
1
votes

The user_data should be passed with template rendering or you can also use base64encode function to pass your file.

data "template_file" "user-data" {
  template = file("${path.module}/user-data.sh")
}

resource "aws_instance" "bespin-ec2-web" {
  ...
  ...

  user_data = "${data.template_file.user_data.rendered}"

  ...
}