# Read from text file then add a user

**URL:** <https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588>\
**Category:** Get Help\
**Created:** [March 27, 2024, 10:43am UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588 "2024-03-27T10:43:40Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Kathy\_L](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/kathy_l/32/2652_2.png) [@Kathy\_L](https://forum.ansible.com/u/Kathy_L)\
**Post date:** [March 27, 2024, 10:43am UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/1 "2024-03-27T10:43:40Z")

</div>

I am trying to read from a file and use the content within to add a user. Here is what my text file looks like:

> IP: x.x.x.x  
> version: x.x  
> username: ted password: secretpassword  
> username: bill password: another password  
> … other data here

And here is how I am reading it. i want to read in only the lines that begin with username, then use that data to create a user.

> hosts: localhost  
> gather\_facts: false  
> vars:  
> content: “{{ lookup(‘file’, ‘data.txt’) }}”  
> tasks:
> 
> - name: get usernames  
> set\_fact: content.split(‘\n’) | map(‘trim’) | select(‘search’), ‘^username’) | list }}"
> - name: create user  
> user:  
> name: “{{ item.username }}”  
> group: “{{ item.username }}”  
> loop: “{{ newcontent }}”

The variable newcontent contains this:

> username: ted password: secretpassword  
> username: bill password: another password

How do I get the username and password from the list newcontent?

---

<div class="post-metadata">

**Author:** ![jrglynn2](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@jrglynn2](https://forum.ansible.com/u/jrglynn2)\
**Post date:** [March 27, 2024, 3:52pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/2 "2024-03-27T15:52:58Z")

</div>

Looks like you might need to use some sort of combination of splits? [ansible.builtin.split filter – split a string into a list — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/split_filter.html)

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [March 27, 2024, 6:16pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/3 "2024-03-27T18:16:43Z")

</div>

The hard part is getting the quotes right. Look at this:

```yaml
---
- name: Read user info
  hosts: localhost
  gather_facts: false
  vars:
    # content: "{{ lookup('file', 'username.txt') }}"
    content: |
      IP: x.x.x.x
      version: x.x
      username: bob password: secretpassword
      username: carol password: another password
      IP: x.x.x.x
      version: x.x
      username: ted password: lksj'ldf"98<79>6768z67%&^%&
      username: alice password: another password
  tasks:
    - name: Parse username and passwords from content
      ansible.builtin.debug:
        msg: 'username: «{{ item[0] }}», password: «{{ item[1] }}»' 
      loop: "{{ content |
                split('\n') |
                select('match', 'username:') |
                map('regex_replace', '^username: (.*?) password: (.*)$', '\\1\n\\2') |
                map('split', '\n') }}"

```

which produces

```txt
TASK [Parse username and passwords from content] *****************************
task path: /home/utoddl/ansible/username.yml:17
ok: [localhost] => (item=['bob', 'secretpassword']) => 
  msg: 'username: «bob», password: «secretpassword»'
ok: [localhost] => (item=['carol', 'another password']) => 
  msg: 'username: «carol», password: «another password»'
ok: [localhost] => (item=['ted', 'lksj\'ldf"98<79>6768z67%&^%&']) => 
  msg: 'username: «ted», password: «lksj''ldf"98<79>6768z67%&^%&»'
ok: [localhost] => (item=['alice', 'another password']) => 
  msg: 'username: «alice», password: «another password»'

```

Note particularly that `loop:` expression. It’s a double-quoted string, so the what looks like a single-quoted “`\`” and “`n`” in those `split`s are actually literal new-line characters by the time `split` is invoked. Similarly, that `'\\1\n\\2'` in the `regex_replace` becomes:

- a single backslash
- the digit `1`
- a literal new-line
- another single backslash
- the digit `2`

by the time `regex_replace` is invoked.

Anyway, maybe that will be of some help. Good luck.

---

<div class="post-metadata">

**Author:** ![Denney-tech](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/denney-tech/32/1556_2.png) [@Denney-tech](https://forum.ansible.com/u/Denney-tech)\
**Post date:** [March 27, 2024, 6:19pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/4 "2024-03-27T18:19:16Z")

</div>

Good stuff! That’s some handy regex right there.

---

<div class="post-metadata">

**Author:** ![kurokobo](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/kurokobo/32/182_2.png) [@kurokobo](https://forum.ansible.com/u/kurokobo)\
**Post date:** [March 28, 2024, 12:25pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/5 "2024-03-28T12:25:38Z")

</div>

In the short term, regular expressions are a good approach, but in the long term, tricky implementations can easily be a technical debt.

As a next step, I personally recommend you to consider changing the original text file format to make it easier to process in Ansible 😃  
For example, CSV, YAML, etc…

```csv
username,password
ted,secretpassword
bill,another password

```

```yaml
IP: x.x.x.x
version: x.x
users:
  - {"username": "ted", "password": "secretpassword"}
  - {"username": "bill", "password": "another password"}

```

---

<div class="post-metadata">

**Author:** ![kathyl](https://avatars.discourse-cdn.com/v4/letter/k/46a35a/32.png) [@kathyl](https://forum.ansible.com/u/kathyl)\
**Post date:** [April 1, 2024, 12:48pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/6 "2024-04-01T12:48:39Z")

</div>

We currently have it working with a yaml file, but the people in charge want a text file, or json (which I haven’t tested yet) as the data is ingested to another system. I’ll try the regex example this morning.

---

<div class="post-metadata">

**Author:** ![Denney-tech](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/denney-tech/32/1556_2.png) [@Denney-tech](https://forum.ansible.com/u/Denney-tech)\
**Post date:** [April 1, 2024, 1:59pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/7 "2024-04-01T13:59:09Z")

</div>

+1 for json. That would be super easy to work with in Ansible.

---

<div class="post-metadata">

**Author:** ![kathyl](https://avatars.discourse-cdn.com/v4/letter/k/46a35a/32.png) [@kathyl](https://forum.ansible.com/u/kathyl)\
**Post date:** [April 8, 2024, 4:42pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/8 "2024-04-08T16:42:50Z")

</div>

Thanks. I get something a bit different though. This is what I get printed out:

> (item=[‘username: ted password: secretpassword’] ) =\> {  
> msg: “username: "username: ted password: secretpassword", password: "username: ted password is secretpassword"”
> 
> (item=[‘username: bob password: secretpassword’] ) =\> {  
> msg: “username: "username: bob password: secretpassword", password: "username: bob password is secretpassword"”

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [April 8, 2024, 5:08pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/9 "2024-04-08T17:08:19Z")

</div>

Can you give us more context? What are you reading in, and how?

---

<div class="post-metadata">

**Author:** ![kathyl](https://avatars.discourse-cdn.com/v4/letter/k/46a35a/32.png) [@kathyl](https://forum.ansible.com/u/kathyl)\
**Post date:** [April 8, 2024, 5:51pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/10 "2024-04-08T17:51:30Z")

</div>

Sure. The data.txt file has this:  
username: ted password: secretpassword  
username: bob password: another password

The ansible starts like this:

> hosts: localhost  
> gather\_facts: false  
> vars:  
> content: “{{ lookup(‘file’, ‘data.txt’) }}”
> 
> tasks:
> 
> - debug:  
> msg: “{{ content }}”  
> …  
> then the code I posted above

“Content” has the correct data in it.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [April 8, 2024, 6:01pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/11 "2024-04-08T18:01:31Z")

</div>

Sorry to sound picky, @kathyl , but it’s a lot easier to fix, or suggest fixes, to code we can copy-n-paste than it is to try to pull together snippets from prior posts. Also, if you can bracket your code between tripple-backtick lines that would help a lot, too. Otherwise your yaml lists come out as markdown bullet points.

---

<div class="post-metadata">

**Author:** ![kathyl](https://avatars.discourse-cdn.com/v4/letter/k/46a35a/32.png) [@kathyl](https://forum.ansible.com/u/kathyl)\
**Post date:** [April 9, 2024, 9:07am UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/12 "2024-04-09T09:07:54Z")

</div>

That’s not picky at all - makes complete sense. Here is my code in its totality:

```auto
gather_facts: false
vars:
content: “{{ lookup(‘file’, ‘data.txt’) }}”

tasks:

     - name: Debug
       ansible.builtin.debug:
          msg: “{{ content }}”

    - name: Parse username and passwords from content
      ansible.builtin.debug:
        msg: 'username: «{{ item[0] }}», password: «{{ item[1] }}»' 
      loop: "{{ content |
                split('\n') |
                select('match', 'username:') |
                map('regex_replace', '^username: (.*?) password: (.*)$', '\\1\n\\2') |
                map('split', '\n') }}"

```

My data.txt reads as follows:

````auto
   username: bob, password: anotherpassword ```
````

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [April 9, 2024, 10:11am UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/13 "2024-04-09T10:11:16Z")

</div>

Ah yes, I see the problem now. You’ve been bitten by **“_smart quotes_”**. This:

```auto
content: “{{ lookup(‘file’, ‘data.txt’) }}”

```

should be:

```auto
content: "{{ lookup('file', 'data.txt') }}"

```

Note that both the single- and double-quotes need fixing. It’s also an issue in your `debug` task’s `msg:` line. Somehow, all the quotes in your `loop:` expression seem to be intact.

It is very hard to see the difference in the default size here on the forum. I have to `[ctrl] + [shift] + [+]` a couple of times before I can see it.

---

<div class="post-metadata">

**Author:** ![kathyl](https://avatars.discourse-cdn.com/v4/letter/k/46a35a/32.png) [@kathyl](https://forum.ansible.com/u/kathyl)\
**Post date:** [April 9, 2024, 3:32pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/14 "2024-04-09T15:32:33Z")

</div>

It must be the font I am using here. My code has ’ and " - the correct type of quotes.

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [April 9, 2024, 3:37pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/15 "2024-04-09T15:37:35Z")

</div>

This might not be the issue, but

> [@kathyl](#):
>
> ’

Is not the same as `'` 🤷 .

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [April 9, 2024, 3:38pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/16 "2024-04-09T15:38:19Z")

</div>

Of the two you just posted, the single-quote is `"U+2019"`, not `"U+0027"`. The double-quote is okay: `"U+0022"`.

(Note: discourse is converting double-quotes outside of back-ticks to “those other marks” – but only when they are in pairs. I had not noticed that before. Probably works okay in text. Horrible for un-back-tick quoted code.)

---

<div class="post-metadata">

**Author:** ![kathyl](https://avatars.discourse-cdn.com/v4/letter/k/46a35a/32.png) [@kathyl](https://forum.ansible.com/u/kathyl)\
**Post date:** [April 9, 2024, 4:38pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/17 "2024-04-09T16:38:19Z")

</div>

In my code i am using “U+2019” and “U+0022”

---

<div class="post-metadata">

**Author:** ![Denney-tech](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/denney-tech/32/1556_2.png) [@Denney-tech](https://forum.ansible.com/u/Denney-tech)\
**Post date:** [April 9, 2024, 5:28pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/18 "2024-04-09T17:28:04Z")

</div>

> [@kathyl](#):
>
> Sure. The data.txt file has this:  
> username: ted password: secretpassword  
> username: bob password: another password

> [@kathyl](#):
>
> My data.txt reads as follows:
> 
> ````auto
> username: bob, password: anotherpassword ```
> 
> ````

You’re rather inconsistent. Does your data.txt file have comma delimitters? That would have made things a lot easier to begin with.

If there’s a comma, re-using @utoddl’s snippet earlier:

```yaml
---
- name: Read user info
  hosts: localhost
  gather_facts: false
  vars:
    # content: "{{ lookup('file', 'username.txt') }}"
    content: |
      IP: x.x.x.x
      version: x.x
      username: bob, password: secretpassword
      username: carol, password: another password
      IP: x.x.x.x
      version: x.x
      username: ted, password: lksj'ldf"98<79>6768z67%&^%&
      username: alice, password: another password
  tasks:
    - name: Parse username and passwords from content
      ansible.builtin.debug:
        msg: 'username: {{ item.username }}, password: {{ item.password }}'
      loop: "{{ content |
                split('\n') |
                select('match', 'username:') |
                map('regex_replace', ',\\s+', '\n') |
                map('from_yaml')
                }}"

```

results in:

```bash
PLAY [Read user info] ****************************************************************************************************************************************************************************************

TASK [Parse username and passwords from content] *************************************************************************************************************************************************************
ok: [localhost] => (item={'username': 'bob', 'password': 'secretpassword'}) => {
    "msg": "username: bob, password: secretpassword"
}
ok: [localhost] => (item={'username': 'carol', 'password': 'another password'}) => {
    "msg": "username: carol, password: another password"
}
ok: [localhost] => (item={'username': 'ted', 'password': 'lksj\'ldf"98<79>6768z67%&^%&'}) => {
    "msg": "username: ted, password: lksj'ldf\"98<79>6768z67%&^%&"
}
ok: [localhost] => (item={'username': 'alice', 'password': 'another password'}) => {
    "msg": "username: alice, password: another password"
}

PLAY RECAP ***************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

```

---

<div class="post-metadata">

**Author:** ![kathyl](https://avatars.discourse-cdn.com/v4/letter/k/46a35a/32.png) [@kathyl](https://forum.ansible.com/u/kathyl)\
**Post date:** [April 10, 2024, 12:06pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/19 "2024-04-10T12:06:19Z")

</div>

That works perfectly - thank you so much!

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [May 10, 2024, 12:07pm UTC](https://forum.ansible.com/t/read-from-text-file-then-add-a-user/4588/20 "2024-05-10T12:07:09Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
