sangyul lee
sangyul lee

Reputation: 123

Error using user_data when creating instance in terrafom

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.

Upvotes: 1

Views: 4530

Answers (2)

Martin Atkins
Martin Atkins

Reputation: 74219

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

Upvotes: 4

mohit
mohit

Reputation: 2491

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}"

  ...
}

Upvotes: 3

Related Questions