Reputation: 123
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
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
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