How to add User data with shell scripting to be executed when EC2 instance is provisioned

2,364 views
Skip to first unread message

devula...@gmail.com

unread,
Aug 29, 2018, 2:15:14 PM8/29/18
to Terraform
Could someone please help me how to add  unix script to the user data of EC2 instance , I want that script gets executed when EC2 is getting provisioned 

when I add template_file or <<EOF   its not workign as expected 

Example :

retry_attempts=10
attempt=0
while [[ $attempt -lt $retry_attempts ]]
do
echo "Starting callback to https:// /$host/api/"
echo "With host_config_key="
status_code=`curl -k -s -i --data "  https://$Thost/api/v2// -w '%{response_code}' -so /dev/null`
if [[ $status_code == 202 ]]
  then
  exit 0
fi
attempt=$(( attempt + 1 ))
echo "${status_code} received... retrying in 1 minute. (Attempt ${attempt})"
sleep 60
done

devula...@gmail.com

unread,
Aug 29, 2018, 2:25:40 PM8/29/18
to Terraform
Basically I want to shell script to the aws_launch_configuration of user_data , please help me

David Adams

unread,
Aug 29, 2018, 2:37:33 PM8/29/18
to terrafo...@googlegroups.com
Unless you need your script to be parameterized by Terraform as well, probably the easiest way is to put the script in a file in your project directory "script.sh", and then include it in your launch configuration resource definition with:

    user_data = "${file("script.sh")}"

You probably also need to start your script with a line indicating what command to run it with, for example: "#!/bin/bash" or "#!/bin/sh".

--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/terraform/issues
IRC: #terraform-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Terraform" group.
To unsubscribe from this group and stop receiving emails from it, send an email to terraform-too...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/terraform-tool/99192a90-53d4-42d1-a66a-524410eb248f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

upendar devu

unread,
Aug 29, 2018, 3:09:18 PM8/29/18
to terrafo...@googlegroups.com
Thanks David , I have some variables in the shell script and its complaining about inline params  to use $$  , should I use all below variables with $${}  ? and also how can I add script.sh based on environment specific ? Can I create 2 sh files  say one for test and other for prod , then  use ternary operator  or use lookup syntax ?  Please suggest me 


retry_attempts=10
attempt=0
while [[ $attempt -lt $retry_attempts ]]
do
echo "Starting callback to https:// /$host/api/"
echo "With host_config_key="
status_code=`curl -k -s -i --data "  https://$Thost/api/v2// -w '%{response_code}' -so /dev/null`
if [[ $status_code == 202 ]]
  then
  exit 0
fi
attempt=$(( attempt + 1 ))
echo "${status_code} received... retrying in 1 minute. (Attempt ${attempt})"
sleep 60
done

David Adams

unread,
Aug 29, 2018, 3:12:34 PM8/29/18
to terrafo...@googlegroups.com
Can you share your actual code? It's hard to tell what's going on exactly. If you use the `file()` function, no templating should be happening.

upendar devu

unread,
Aug 29, 2018, 3:25:11 PM8/29/18
to terrafo...@googlegroups.com
Here is the code other than masking the host and API call  ; Here the HOST and version variables  will vary  based on environment specific  test or prod and rest of the script is same


#!/bin/bash
 
retry_attempts=10
attempt=0
HOST= XXX
version =1 
while [[ $attempt -lt $retry_attempts ]]
do
echo "Starting callback to https://$HOST/api/$version/rest/callback/"
status_code=`curl -k -s -i --data "key=$KEY" https://$HOST/api/$version/rest/callback/ -w '%{response_code}' -so /dev/null`
if [[ $(status_code) == 202 ]]
  then
  exit 0
fi
attempt=$(( attempt + 1 ))
echo "${status_code} received... retrying in 1 minute. (Attempt ${attempt})"
sleep 60
done
#TODO add git pull if Tower call doesnt exit.
#TODO TODO email if git and tower both fail.
exit 1

David Adams

unread,
Aug 29, 2018, 5:52:24 PM8/29/18
to terrafo...@googlegroups.com
Sorry, I meant the terraform code.

upendar devu

unread,
Aug 29, 2018, 6:07:59 PM8/29/18
to terrafo...@googlegroups.com

does this help ?
resource "aws_launch_configuration" "launch_config" {
   
  name = "${var.app_name}-${data.terraform_remote_state.vpc.environment}"
  image_id = "${data.aws_ami.ami.id}"
  instance_type = "${lookup(var.instance_type,data.terraform_remote_state.vpc.environment)}"
  iam_instance_profile = "${aws_iam_instance_profile.instance_profile.name}"
    user_data = <<EOF
#!/bin/bash
retry_attempts=10
attempt=0
HOST= XXX
version =1 
while [[ $attempt -lt $retry_attempts ]]
do
echo "Starting callback to https://$HOST/api/$version/rest/callback/"
status_code=`curl -k -s -i --data "key=$KEY" https://$HOST/api/$version/rest/callback/ -w '%{response_code}' -so /dev/null`
if [[ $(status_code) == 202 ]]
  then
  exit 0
fi
attempt=$(( attempt + 1 ))
echo "${status_code} received... retrying in 1 minute. (Attempt ${attempt})"
sleep 60
done
#TODO add git pull if Tower call doesnt exit.
#TODO TODO email if git and tower both fail.
exit 1
EOF
}

Justin Seiser

unread,
Aug 30, 2018, 8:00:09 AM8/30/18
to Terraform
Here is a sample that we use.  This is a bit more involved than your request.  but i have a folder, in my module folder called templates.  Thats where my 'userdata' scripts live.  There are 2.  If 'var.set_password_on_launch' is set to true, we use the first user data, if its set to false we use the second user data. Notice for the first template_file, I am declaring a variable, and passing that variable into my file.
This variable will get interpolated inside the template by terraform, replacing the variable with whatever value I defined for 'administrator_password'.

This line `user_data = "${element(concat(data.template_file.windows_password.*.rendered, data.template_file.windows.*.rendered), 0)}"` is a fancy way of saying, if set_password_long == true, use the first userdata, if false, use the second userdata.

data "template_file" "windows_password" {
 count
= "${var.set_password_on_launch}"
 
template = "${file("${path.module}/templates/windows_set_password.tpl")}"

 vars
{
 administrator_password
= "${var.administrator_password}"
 
}
}

data
"template_file" "windows" {
 count
= "${1 - var.set_password_on_launch}"
 
template = "${file("${path.module}/templates/windows.tpl")}"
}

resource
"aws_instance" "dc" {
 ami
= "${var.ami != "" ? var.ami : data.aws_ami.dc.id}"
 instance_type
= "${var.instance_type}"
 key_name
= "${var.ec2_key}"
 subnet_id
= "${data.aws_subnet.subnet.id}"
 vpc_security_group_ids
= ["${aws_security_group.dc.id}"]
 private_ip
= "${var.private_ip}"
 iam_instance_profile
= "role_WindowsEC2SSM_Access"
 user_data
= "${element(concat(data.template_file.windows_password.*.rendered, data.template_file.windows.*.rendered), 0)}"

 root_block_device
{
 volume_size
= "${var.ebs_size}"
 
}

 lifecycle
{
 create_before_destroy
= true
 prevent_destroy
= true
 ignore_changes
= ["user_data"]
 
}

 tags
{
 
Name = "${format("ec2-%s-%s-%s-%s", var.customer, var.name, var.project, var.environment)}"
 
Customer = "${var.customer}"
 
Environment = "${var.environment}"
 
Project = "${var.project}"
 
Owner = "${var.owner}"
 
Terraform = true
 AZ
= "${var.az}"
 AMI
= "true"
 
"Patch Group" = "${format("%s-%s-%s", var.customer, var.project, var.environment)}"
 startInstance
= "${var.start_time_tag}"
 stopInstance
= "${var.stop_time_tag}"
 
}

 volume_tags
{
 
Name = "${format("ebs-%s-%s-%s-%s", var.customer, var.name, var.project, var.environment)}"
 
Customer = "${var.customer}"
 
Environment = "${var.environment}"
 
Project = "${var.project}"
 
Owner = "${var.owner}"
 
Terraform = true
 AZ
= "${var.az}"
 
}
}


Here is a sample of the template file. Notice `"${administrator_password}"` is in the file, just like it was defined in my vars above. When this file is actually created, `"${administrator_password}"` would be
replaced with the value I defined for that variable. If I wanted to have a literal ${bash_variable} in this file. I would do this `\$${bash_variable}. Which tells terraform I really want the string ${bash_variable}
to be in the userdata file used for my launch configuration.



<powershell>
$url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
$file = "$env:SystemDrive\ConfigureRemotingForAnsible.ps1"

(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)

powershell.exe -ExecutionPolicy ByPass -File $file -EnableCredSSP

Invoke-WebRequest "https://s3.amazonaws.com/amazoncloudwatch-agent/windows/amd64/latest/AmazonCloudWatchAgent.zip" -OutFile "C:\AmazonCloudWatchAgent.zip"
Expand-Archive -LiteralPath "C:\AmazonCloudWatchAgent.zip" -DestinationPath "C:\AmazonCloudWatchAgent" -Force:$true
Set-Location "C:\AmazonCloudWatchAgent"
./install.ps1
Set-Location "C:\Program Files\Amazon\AmazonCloudWatchAgent"
./amazon-cloudwatch-agent-ctl.ps1 -a fetch-config -m ec2 -c ssm:cwla_windows-prd.cfg -s
net user Administrator "${administrator_password}"
</powershell>



To your question about having diff. variables defined for prd/dev. You could do that a few ways. If you were using .tfvars, and defining variables as inputs to your template file, the value of those variables would change depending on what
you specify in the tfvars file for those environments. If you wanted to have separate files for dev and prod, you could do like I did above, and choose one based on some input value.

upendar devu

unread,
Aug 30, 2018, 8:05:15 AM8/30/18
to terrafo...@googlegroups.com
Thanks for the response ,this would be helpful.

--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/terraform/issues
IRC: #terraform-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Terraform" group.
To unsubscribe from this group and stop receiving emails from it, send an email to terraform-too...@googlegroups.com.

upendar devu

unread,
Aug 30, 2018, 10:43:27 AM8/30/18
to terrafo...@googlegroups.com
Justin,

can we think of this simple way ? just create 2 templates one for test and other for prod and then let template picked based on given environment ? please suggest. 

data "template_file" "windows_password" {
 count = "${var.set_password_on_launch}"
 template = "${file("${path.module}/templates/filename-${env}.tpl")}"

 vars {
 env= "${var.environment}"
 }
}

Thanks
Upendar

Srinivas Thanneeru

unread,
Sep 5, 2018, 1:19:14 PM9/5/18
to terrafo...@googlegroups.com
Hi,
Let's say I'm generating data like below. How can I create a map that consists of key as name and value as id.


data "template_file" "groups_data" {
  count = "${length(var.group_names)}"

  template = "$${name},$${id}"
  vars {
    id = "${lookup(data.oci_identity_groups.test_groups.groups[count.index], "id")}"
    name="${lookup(data.oci_identity_groups.test_groups.groups[count.index], "name")}"
  }
}

Please suggest.

Thanks
Srini


Srinivas Thanneeru

unread,
Sep 5, 2018, 3:28:45 PM9/5/18
to terrafo...@googlegroups.com
I did this:

data "template_file" "groups_data" {
  count = "${length(var.group_names)}"

  template = "$${id}"

  vars {
    id = "${lookup(data.oci_identity_groups.test_groups.groups[count.index], "id")}"
    name="${lookup(data.oci_identity_groups.test_groups.groups[count.index], "name")}"
  }
}

data "template_file" "groups_data_names" {
  count = "${length(var.group_names)}"

  template = "$${name}"

  vars {
    id = "${lookup(data.oci_identity_groups.test_groups.groups[count.index], "id")}"
    name="${lookup(data.oci_identity_groups.test_groups.groups[count.index], "name")}"
  }
}

locals {
   groups_map = "${zipmap(data.template_file.groups_data_names.*.rendered, data.template_file.groups_data.*.rendered)}"

}

Reply all
Reply to author
Forward
0 new messages