# How to use ec2 modules user\_data field?

**URL:** <https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969>\
**Category:** Ansible Project\
**Tags:** ansible-project\
**Created:** [August 16, 2014, 3:04pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969 "2014-08-16T15:04:56Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Michael\_DeHaan1](https://avatars.discourse-cdn.com/v4/letter/m/5e9695/32.png) [@Michael\_DeHaan1](https://forum.ansible.com/u/Michael_DeHaan1)\
**Post date:** [August 16, 2014, 3:04pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/1 "2014-08-16T15:04:56Z")

</div>

(a) can you please show the line from your playbook where you are using the user data variable?

(b) with the above, how are you determining the way it fails? I.e. what does failure look like?

---

<div class="post-metadata">

**Author:** ![Darvin\_Denmian](https://avatars.discourse-cdn.com/v4/letter/d/c68b51/32.png) [@Darvin\_Denmian](https://forum.ansible.com/u/Darvin_Denmian)\
**Post date:** [October 24, 2014, 4:06pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/2 "2014-10-24T16:06:22Z")

</div>

I got the following error:

`  
ERROR: Syntax Error while loading YAML script, /etc/ansible/playbooks/search/roles/aws/tasks/main.yml  
Note: The error may actually appear before this position: line 13, column 17

state: running  
user\_data: “”"#!/bin/bash  
^  
This one looks easy to fix. It seems that there is a value started  
with a quote, and the YAML parser is expecting to see the line ended  
with the same kind of quote. For instance:

when: “ok” in result.stdout

Could be written as:

when: ‘“ok” in result.stdout’

or equivalently:

when: “‘ok’ in result.stdout”

`

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan1](https://avatars.discourse-cdn.com/v4/letter/m/5e9695/32.png) [@Michael\_DeHaan1](https://forum.ansible.com/u/Michael_DeHaan1)\
**Post date:** [October 25, 2014, 12:00am UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/3 "2014-10-25T00:00:37Z")

</div>

I don’t believe YAML takes Python-like docstrings.

So you would need to start with a single or double quote and escape any internal quotes as need be.

The “file” lookup plugin may also be helpful.

---

<div class="post-metadata">

**Author:** ![Chris\_Church1](https://avatars.discourse-cdn.com/v4/letter/c/ac8455/32.png) [@Chris\_Church1](https://forum.ansible.com/u/Chris_Church1)\
**Post date:** [October 25, 2014, 7:33pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/4 "2014-10-25T19:33:08Z")

</div>

The file lookup plugin is your friend.

I’m using the following task for launching Windows instances and configuring PowerShell remoting:

> - ec2:
> 
> aws\_access\_key: ‘{{ aws\_access\_key }}’
> 
> aws\_secret\_key: ‘{{ aws\_secret\_key }}’
> 
> region: ‘{{ win\_ec2\_region }}’
> 
> instance\_type: ‘{{ win\_ec2\_instance\_type }}’
> 
> instance\_tags:
> 
> Name: ‘{{win\_ec2\_name\_prefix}}-{{[item.name](http://item.name)}}’
> 
> group: ‘{{ win\_ec2\_security\_group }}’
> 
> key\_name: ‘{{ win\_ec2\_key\_name }}’
> 
> image: ‘{{ [item.id](http://item.id) }}’
> 
> user\_data: “{{ lookup(‘file’, ‘win\_ec2\_user\_data’) }}”
> 
> exact\_count: 1
> 
> count\_tag:
> 
> Name: ‘{{win\_ec2\_name\_prefix}}-{{[item.name](http://item.name)}}’
> 
> wait: yes
> 
> with\_items: win\_ec2\_images
> 
> register: win\_ec2

My win\_ec2\_user\_data file contains:

---

<div class="post-metadata">

**Author:** ![Rob\_White](https://avatars.discourse-cdn.com/v4/letter/r/71c47a/32.png) [@Rob\_White](https://forum.ansible.com/u/Rob_White)\
**Post date:** [December 18, 2014, 5:46am UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/5 "2014-12-18T05:46:26Z")

</div>

Hi Chris,

What’s your approach for logging in to your Win boxes using WinRM after this step?

Ansible docs direct you to configure a vars file with ansible\_ssh\_pass set however every ec2 instance is going to have a different password. I’ve created a module for using boto to get the ec2 password but i dont know how to chain this through to then use it for a following play which would configure my windows instances.

---

<div class="post-metadata">

**Author:** ![Chris\_Church1](https://avatars.discourse-cdn.com/v4/letter/c/ac8455/32.png) [@Chris\_Church1](https://forum.ansible.com/u/Chris_Church1)\
**Post date:** [December 19, 2014, 8:50am UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/6 "2014-12-19T08:50:52Z")

</div>

I’m planning to get some of these roles onto Galaxy, but until then…

The next tasks in that particular play are:

> - name: wait for instances to listen on port 5986 (winrm https)
> 
> wait\_for:
> 
> state=started
> 
> host={{ item.tagged\_instances[0].public\_ip }}
> 
> port=5986
> 
> with\_items: win\_ec2.results
> 
> when: win\_ec2\_images and win\_ec2 is defined
> 
> - name: obtain windows passwords for instances
> 
> ec2\_win\_pass:
> 
> aws\_access\_key: ‘{{ aws\_access\_key }}’
> 
> aws\_secret\_key: ‘{{ aws\_secret\_key }}’
> 
> region: ‘{{ win\_ec2\_region }}’
> 
> instance\_id: “{{ item.tagged\_instances[0].id }}”
> 
> private\_key: “{{ lookup(‘file’, win\_ec2\_private\_key) }}”
> 
> with\_items: win\_ec2.results
> 
> register: win\_ec2\_passwords
> 
> when: win\_ec2\_images and win\_ec2 is defined
> 
> - name: add\_host
> 
> add\_host:
> 
> name: ‘{{ item.item.tagged\_instances[0].public\_dns\_name }}’
> 
> groups: ‘cloud,ec2,windows,{{win\_ec2\_name\_prefix}}-{{ [item.item.item.name](http://item.item.item.name) }}’
> 
> ansible\_ssh\_host: ‘{{ item.item.tagged\_instances[0].public\_ip }}’
> 
> ansible\_ssh\_port: 5986
> 
> ansible\_ssh\_user: ‘{{ item.item.item.user }}’
> 
> ansible\_ssh\_pass: ‘{{ item.password }}’
> 
> ansible\_connection: ‘winrm’
> 
> with\_items: win\_ec2\_passwords.results
> 
> when: win\_ec2\_images and win\_ec2\_passwords is defined

The second task is using my module from [https://github.com/ansible/ansible-modules-core/pull/378](https://github.com/ansible/ansible-modules-core/pull/378)

You can also save these new hosts to an inventory file (using template module).

After the play above runs, I can then run another play to ping the newly-launched Windows hosts:

> - name: ping new windows instances
> 
> hosts: windows
> 
> gather\_facts: false
> 
> tasks:
> 
> - action: win\_ping

Hope that helps!

---

<div class="post-metadata">

**Author:** ![Daniel\_Neades](https://avatars.discourse-cdn.com/v4/letter/d/dc4da7/32.png) [@Daniel\_Neades](https://forum.ansible.com/u/Daniel_Neades)\
**Post date:** [December 23, 2014, 11:06pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/7 "2014-12-23T23:06:38Z")

</div>

It is – if one wishes to supply text-based user data. Unfortunately, I have a requirement to provide a binary blob of user data, and the file lookup plugin does not seem to handle that – looking at the source code, it expects the file to be a UTF8-encoded text file.

What I’d like to do is make use of FreeBSD’s configinit capability, which can treat the user data as a tar file, extract it, and then process each of the contained files. This is documented here:

[http://www.daemonology.net/blog/2013-12-09-FreeBSD-EC2-configinit.html](http://www.daemonology.net/blog/2013-12-09-FreeBSD-EC2-configinit.html)

As I mention in the comments on that page, I did try passing the contents of a base64-encoded tar file (via the file lookup plugin) in as the user\_data parameter, but (unsurprisingly), that gets passed-through as-is (i.e. still base64-encoded) to the ec2 instance user data, whereas I need the user data to be set to the raw binary data of the unencoded tar file.

Now, the Ansible ec2 module documentation specifically says that the user\_data parameter is an ‘opaque blob of data which is made available to the ec2 instance’, so it is obviously anticipating that directly passing a binary blob should be possible. The question, though, is how?

Any help would be greatly appreciated. I am new to both Ansible and EC2, so it is entirely possible that I am missing something blindingly obvious.

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![Daniel\_Neades](https://avatars.discourse-cdn.com/v4/letter/d/dc4da7/32.png) [@Daniel\_Neades](https://forum.ansible.com/u/Daniel_Neades)\
**Post date:** [December 24, 2014, 4:10pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/8 "2014-12-24T16:10:14Z")

</div>

Incidentally, I also tried using the file lookup plugin on a base64-encoded tar file, and then filtering that with Jinja2’s b64decode filter. That also fails, though I think it does so at a later stage than trying to use the file lookup plugin on a binary file. Using the b64decode filter on a looked-up base64-encoded file, one gets an error whose stack trace ends something like this:

File “/usr/local/Cellar/ansible/1.8.2/libexec/lib/python2.7/site-packages/ansible/utils/template.py”, line 362, in template\_from\_string  
res = jinja2.utils.concat(rf)  
File “”, line 7, in root  
UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xfd in position 0: ordinal not in range(128)

I then thought I’d do a little experiment, and provide the user data as a literal (copy-and-pasted) string of base64-encoded data, again filtered with the Jinga2 b64decode filter, like this:

user\_data: “{{ ‘/Td6WFoAAATm1rRGA … VZiascRn+wIAAAAABFla’ | b64decode }}”

That took the file lookup plugin out of the equation, but resulted in the same error.

I next tried the pipe lookup plugin (with ‘cat my-binary-file’), but that also fails in the same way as the file lookup plugin. Looking at the source code, the pipe lookup plugin follows the file lookup one and unconditionally tries to decode the output from the command as a UTF-8 encoded string.

I provisionally conclude from all this that Ansible generally doesn’t want to handle binary parameter values. That seems limiting. I suppose one solution to my specific problem (without fixing the wider issue) would be for the ec2 module to accept something like a user\_data\_file parameter (as an alternative to user\_data), which would specify a local file whose content was to be used without any processing at all as an opaque user data binary object. In fact, that’s pretty much exactly what I want, as I could give it the FreeBSD configinit .tar file directly.

I am still hoping that someone has a solution that will enable binary user data to be provided to an instance.

Again, my thanks in advance for any assistance.

---

<div class="post-metadata">

**Author:** ![Chris\_Church1](https://avatars.discourse-cdn.com/v4/letter/c/ac8455/32.png) [@Chris\_Church1](https://forum.ansible.com/u/Chris_Church1)\
**Post date:** [December 26, 2014, 7:54am UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/9 "2014-12-26T07:54:45Z")

</div>

One issue may be that boto is encoding unicode user data into utf-8, then base64-encoding it:

> [https://github.com/boto/boto/blob/develop/boto/ec2/connection.py#L927](https://github.com/boto/boto/blob/develop/boto/ec2/connection.py#L927)

So, if we can somehow pass a str() instance to boto instead of a unicode() instance, it’ll skip the utf-8 encoding step.

I’ve implemented a version of the ec2 module that accepts a user\_data\_b64 parameter as a base64-encoded string of the user data, and decodes it into a str() instance before passing to boto:

> [https://github.com/cchurch/ansible-modules-core/blob/f6522309ffaedace2305fd67a5a5721213f59199/cloud/amazon/ec2.py](https://github.com/cchurch/ansible-modules-core/blob/f6522309ffaedace2305fd67a5a5721213f59199/cloud/amazon/ec2.py)

You can read the local binary file into a base64-encoded string using the slurp module, then pass that blob to the ec2 module. The slurp module needs an absolute path, so you can use role\_path to specify a path relative to a role, or playbook\_dir relative to your top-level playbook.

---

<div class="post-metadata">

**Author:** ![Catalin\_Costache](https://avatars.discourse-cdn.com/v4/letter/c/a5b964/32.png) [@Catalin\_Costache](https://forum.ansible.com/u/Catalin_Costache)\
**Post date:** [December 26, 2014, 10:05am UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/10 "2014-12-26T10:05:33Z")

</div>

Yaml has a notation for multiline string. I set user data like so:

`user_data: | #!/bin/bash echo "Defaults:{{admin_user}} !requiretty" > /etc/sudoers.d/disable_requiretty`

Note the pipeline character.

`

```auto

# Join multiple lines without new line

value: >

        part 1

        part 2

        

# Join with newline

value2: |

        line 1

        line 2

```

`

---

<div class="post-metadata">

**Author:** ![Daniel\_Neades](https://avatars.discourse-cdn.com/v4/letter/d/dc4da7/32.png) [@Daniel\_Neades](https://forum.ansible.com/u/Daniel_Neades)\
**Post date:** [December 26, 2014, 12:19pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/11 "2014-12-26T12:19:01Z")

</div>

Chris, thank you _very_ much for looking at that. I have tried your modified ec2.py but, unfortunately, even using it exactly as you describe, the user data is still being stored on AWS in its base64 form. From within the instance:

# fetch [http://169.254.169.254/latest/user-data](http://169.254.169.254/latest/user-data)

# cat user-data

/Td6WFoAAATm1rRGAgAhARYAAAB … /sCAAAAAARZWg==

# base64 -d user-data test.tar.xz

# tar tvf test.tar.xz

-rw-r–r-- 0 djn staff 2 Dec 26 10:40 1.txt

-rw-r–r-- 0 djn staff 2 Dec 26 10:40 2.txt

-rw-r–r-- 0 djn staff 322 Dec 24 12:03 configinit

By the way, looking at line 930 of the boto connection.py source file you referenced, I don’t understand why boto is base64-encoding a parameter that its own documentation (on lines 774-776) indicates is apparently already to be base64-encoded. (Note, however, that I am not familiar with the boto source.) Still, since you are passing in a decoded version of the Ansible user\_data\_b64 parameter, I’d have thought what you’ve done ought to work. Empirically, though, it doesn’t seem to.

Finally, perhaps I should note that to test this, I dropped your ec2.py over the top of the existing ec2.py in a stock Ansible 1.8.2 installation that was installed by Homebrew on my OS X machine. This installation of Ansible is using boto 2.34.0, I believe.

Thank you again for your help!

---

<div class="post-metadata">

**Author:** ![Daniel\_Neades](https://avatars.discourse-cdn.com/v4/letter/d/dc4da7/32.png) [@Daniel\_Neades](https://forum.ansible.com/u/Daniel_Neades)\
**Post date:** [December 26, 2014, 1:01pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/12 "2014-12-26T13:01:43Z")

</div>

Chris, please ignore my previous response, which was premature. I had misinterpreted the (perhaps slightly ambiguous) documentation for the slurp module, and thought that it needed to be given a base64-encoded file. Apparently, it does not. If I give it a raw .tar.xz, your ec2.py revision seems to work perfectly, thank you!

Perhaps an example in the documentation for the ec2 module using slurp and user\_data would be in order, however, before you submit a pull request for your modifications to be incorporated into Ansible proper?

Many thanks again for your solution, let’s hope it can be incorporated into the next Ansible release.

---

<div class="post-metadata">

**Author:** ![Bryan\_Larsen](https://avatars.discourse-cdn.com/v4/letter/b/9de053/32.png) [@Bryan\_Larsen](https://forum.ansible.com/u/Bryan_Larsen)\
**Post date:** [December 7, 2015, 3:11pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/13 "2015-12-07T15:11:30Z")

</div>

I’m having trouble using this notation.

- ec2\_lc:  
name: “{{env}}-render-premium-{{gitsha}}”  
…  
user\_data: |

{  
“services”: [“worker-render-premium@4100”],  
“autoScalingGroupName”: “{{env}}-render-premium”  
}

but when I do a `curl http://169.254.169.254/latest/user-data` to read it back I get:

{‘services’: [‘worker-render-premium@4100’], ‘autoScalingGroupName’: ‘staging-render-premium’}

The loss of whitespace I can deal with, but translating double quotes into single quotes makes this invalid JSON and breaks things.

I’m using ansible 1.9.4. Even with -vvvv user\_data isn’t printed.

---

<div class="post-metadata">

**Author:** ![einar.coutin](https://avatars.discourse-cdn.com/v4/letter/e/34f0e0/32.png) [@einar.coutin](https://forum.ansible.com/u/einar.coutin)\
**Post date:** [October 8, 2016, 12:21am UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/14 "2016-10-08T00:21:06Z")

</div>

Hey,

You want to put your data in a variable, let’s say in /defaults/main.yml and then invoke that from the ec2\_lc module:

- ec2\_lc:  
name: “{{env}}-render-premium-{{gitsha}}”  
…  
user\_data: “{ {my\_user\_data }}”

I did that exact thing for Launch Configs and it works prefectly.

Other than that you may try to rewrite your do to include the pipe WITHIN the brackets. But I wouldn’t recommend that as a best practice.

- ec2\_lc:  
name: “{{env}}-render-premium-{{gitsha}}”  
…  
user\_data:

{ |  
“services”: [“worker-render-premium@4100”],  
“autoScalingGroupName”: “{{env}}-render-premium”  
}

my\_user\_data: |  
#!/bin/bash  
echo “Defaults:{{admin\_user}} !requiretty” \> /etc/sudoers.d/disable\_requiretty

And then use that variable from within the module:

---

<div class="post-metadata">

**Author:** ![Cesar\_Ramirez](https://avatars.discourse-cdn.com/v4/letter/c/34f0e0/32.png) [@Cesar\_Ramirez](https://forum.ansible.com/u/Cesar_Ramirez)\
**Post date:** [February 15, 2023, 8:07pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/15 "2023-02-15T20:07:46Z")

</div>

Hello Guys, new tot this Group, trying to provision an EC2 Windows using Ansible with user\_data.

---

<div class="post-metadata">

**Author:** ![dnmvisser](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dnmvisser](https://forum.ansible.com/u/dnmvisser)\
**Post date:** [February 15, 2023, 10:34pm UTC](https://forum.ansible.com/t/how-to-use-ec2-modules-user-data-field/15969/16 "2023-02-15T22:34:24Z")

</div>

And your question is…?
