Kathy_L
(Kathy L)
March 27, 2024, 10:43am
1
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?
utoddl
(Todd Lewis)
March 27, 2024, 6:16pm
3
The hard part is getting the quotes right. Look at this:
---
- 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
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.
4 Likes
Good stuff! That’s some handy regex right there.
1 Like
kurokobo
(kurokobo)
March 28, 2024, 12:25pm
5
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…
username,password
ted,secretpassword
bill,another password
IP: x.x.x.x
version: x.x
users:
- {"username": "ted", "password": "secretpassword"}
- {"username": "bill", "password": "another password"}
6 Likes
kathyl
April 1, 2024, 12:48pm
6
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.
+1 for json. That would be super easy to work with in Ansible.
kathyl
April 8, 2024, 4:42pm
8
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"”
utoddl
(Todd Lewis)
April 8, 2024, 5:08pm
9
Can you give us more context? What are you reading in, and how?
kathyl
April 8, 2024, 5:51pm
10
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.
utoddl
(Todd Lewis)
April 8, 2024, 6:01pm
11
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.
2 Likes
kathyl
April 9, 2024, 9:07am
12
That’s not picky at all - makes complete sense. Here is my code in its totality:
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:
username: bob, password: anotherpassword ```
utoddl
(Todd Lewis)
April 9, 2024, 10:11am
13
Ah yes, I see the problem now. You’ve been bitten by “smart quotes ” . This:
content: “{{ lookup(‘file’, ‘data.txt’) }}”
should be:
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.
2 Likes
kathyl
April 9, 2024, 3:32pm
14
It must be the font I am using here. My code has ’ and " - the correct type of quotes.
chris
(Chris Croome)
April 9, 2024, 3:37pm
15
This might not be the issue, but
kathyl:
’
Is not the same as '
.
utoddl
(Todd Lewis)
April 9, 2024, 3:38pm
16
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.)
2 Likes
kathyl
April 9, 2024, 4:38pm
17
In my code i am using “U+2019” and “U+0022”
1 Like
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:
---
- 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:
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
1 Like
kathyl
April 10, 2024, 12:06pm
19
That works perfectly - thank you so much!
system
(system)
Closed
May 10, 2024, 12:07pm
20
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.