I have to use Ansible modules in order to edit the /etc/ssh/sshd_config file - every time I create a new user I want to append it at these two lines:
AllowUs
I had the same problem. I needed add user to sudoers group, let's say 'testuser' to line:
User_Alias SOMEADMIN = smoeuser1, someuser2, someuser3
This worked well for me:
- name: add testuser to end of line
lineinfile:
dest: /etc/sudoers.d/somegroup
state: present
regexp: '^(User_Alias(.*)$)'
backrefs: yes
line: '\1, testuser'
The point is that if I had '^User_Alias(..)$'* in regexp and not '^(User_Alias(..)$)'* it didn't work and whole line was replaced. With () arround searched text the result was OK:
User_Alias SOMEADMIN = smoeuser1, someuser2, someuser3, testuser
So then anything can work in line:, included ansible variables like "{{ usernames | join(', ') }}"
You could do it in a single play with a newline, but I think it's cleaner to use two lineinfile
plays for this.
- hosts: '127.0.0.1'
vars:
usernames:
- larry
- curly
- moe
usergroups:
- stooges
- admins
tasks:
- lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^AllowUsers'
line: "AllowUsers {{usernames | join(' ')}}"
- lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^AllowGroups'
line: "AllowGroups {{usergroups | join(' ')}}"
Note that groups
is a reserved word so don't use that as a variable name.
This worked for me
- name: Add Group to AllowGroups
lineinfile:
dest=/etc/ssh/sshd_config
backup=True
backrefs=True
state=present
regexp='^(AllowGroups(?!.*\b{{ groupname }}\b).*)$'
line='\1 {{ groupname }}'
The selected answer assumes that the complete list of users is available at runtime, while the most popular answer can fail when there is a dash in the username, because \b
interprets it as a word boundary. The following solution assumes that the playbook cannot regenerate the complete list of usernames from scratch, and tries and handle the corner case of dashes:
name: add a user to the list of AllowUsers if not present
lineinfile:
path: /etc/ssh/sshd_config
backrefs: yes
backup: yes
regexp: "^AllowUsers((?:(?:\s+\S+(?!\S))(?<!\s{{ username }}))+\s*?)(\n?)$"
line: "AllowUsers\1 {{ username }}\2"
validate: /usr/sbin/sshd -t -f %s
As a bonus, I threw in sshd_config backup and verification.
How the (interesting part of the) regular expression works:
--------------------------+----------------------------------------------------
( |
--------------------------+----------------------------------------------------
(?: | This group is not captured
--------------------------+----------------------------------------------------
(?:\s+\S+(?!\S)) | Matches any sequence of whitespace characters fol-
| lowed by any sequence of non-whitespace characters,
| that is to say a leading space and a username. The
| negative look-ahead at the end prevents a "catast-
| rophic backtracking". Also, this group is not cap-
| tured.
--------------------------+----------------------------------------------------
(?<!\s{{ username }}) | Applies a negative look-behind on the username, so
| that if the username found by the previous expres-
| sion matches, the regular expression fails. The
| match on a leading whitespace character ensures
| that the comparison is made on the complete string.
--------------------------+----------------------------------------------------
)+ | Groups the detection of a username and its negative
| look-behind together. The "+" quantifier is used
| here on the assumption that the file already cont-
| ains at least one username, but "*" could be used
| for a more relaxed matching.
--------------------------+----------------------------------------------------
\s*? | Matches any trailing whitespace. The match is lazy
| in order to detect the newline character later on.
--------------------------+----------------------------------------------------
) | Captures the whole text after "AllowUsers" (this
| will be \1).
--------------------------+----------------------------------------------------
(\n?) | Captures either a newline character or an empty
| string (this will be \2).
--------------------------+----------------------------------------------------
If the regular expression matches, it means that the line exists and that it does not contain {{ username }}
, so we append it.
If the regular expression does not match, it means that either the line does not exist or that it contains {{ username }}
, and we do nothing.
The replace module will replace all instances of a regular expression pattern within a file. Write a task to match the AllowUsers
line and replace it with the original line appended with the user name. To ensure the task is idempotent, a negative lookahead assertion in the regular expression checks if the user name already appears in the line. For example:
- name: Add user to AllowUsers
replace:
backup: yes
dest: /etc/ssh/sshd_config
regexp: '^(AllowUsers(?!.*\b{{ user_name }}\b).*)$'
replace: '\1 {{ user_name }}'