I'm trying to create a script that will automatically deploy and install an AWS EC2 instance using a bash script. I have a deployment script that looks something like this:


# Update apt-get
echo Updating apt-get repositories
apt-get update -yqq

# Install pre-requisits
echo Installing AWS CLI and Python 3
apt-get install -yqq awscli python3 python3-pip

# Set timezone to US/Eastern
# https://help.ubuntu.com/community/UbuntuTime
echo "US/Eastern" | sudo tee /etc/timezone
dpkg-reconfigure --frontend noninteractive tzdata

echo cloud-init userdata processed.

The deployment script (lets call userdata.bash) runs fine when I deploy the instance via arguments:

aws ec2 run-instances \ 
  --image-id ami-064a0193585662d74 \
  --instance-type t3.micro \
  --key-name "ssh key" \
  --security-group-ids sg-abcdef01 sg-abcdef02 \
  --user-data file://userdata.bash

The instance starts, the script runs, everything is great!

But when I put these same options into a json file, the user-data part doesn't parse properly.

The json file named launch.json:

  "BlockDeviceMappings": [
      "DeviceName": "/dev/sda1",
      "Ebs": {
        "DeleteOnTermination": true,
        "VolumeType": "gp2",
        "VolumeSize": 8
  "ImageId": "ami-064a0193585662d74",
  "InstanceType": "t3.micro",
  "KeyName": "ssh key",
  "SecurityGroupIds": ["sg-abcdef01", "sg-abcdef02"],
  "Placement": {
    "AvailabilityZone": "us-east-1a"
  "IamInstanceProfile": {
      "Arn": "arn:aws:iam::111111111111:instance-profile/some-role"
  "UserData": "file://userdata.bash"

When I run with the --cli-input-json argument, everything loads fine, with the exception of the userdata script.

I've tried converting the script to base64 and loading directly, which didn't work. I've tried just the file name, and that didn't work either. The documentation seems to be mum about the format of the userdata part of the yaml file.

Any suggestions would be greatly appreciated.


One thing that will work is to assign the actual contents of the script to UserData in the json file and use a newline character to separate lines of the script. Here's a test script I used as an example:

  "UserData":  "#!/bin/bash\nyum install emacs -y\n",

This does install emacs on my instance at startup. It's possible that a base64 encoded version will work as well so long as it is one line.

Note the following for testing purposes:

  1. After launch, if you connect to your instance with SSH, you can use curl to see how the value you passed to UserData was interpreted. In my testing, if I had used "UserData": "file://userdata.bash",then the value returned was file://userdata.bash rather than the contents of the script.
  2. You can also check the /var/log/boot.log file for messages related to your userdata script.
  3. You can also check the value of userdata through the console via actions->instance settings->view/change userdata.

As you stated, this is not very well documented, and I suspect there is a way to get the command to read the contents of the file instead of using the literal value, but I have not seen it documented anywhere.

  • I'll try adding bash script in one line like you have it here. The base64 encoded version does NOT work. I've verified that. The data is properly reported in the meta-service as well as within /var/lib/instance/ and shows that it was properly sent. I'll report back with the specific error I get when I get back to my computer. – scuba_mike Sep 4 '19 at 18:46
  • 1
    Adding the script in one line worked, but not very practical for the length of the script I have. I ended up doing the following: aws ec2 run-instances --cli-input-json file://input.json --user-data file://userdata.sh Thanks for the help! – scuba_mike Sep 6 '19 at 0:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.