ansible user module always shows changed - ansible

I'm struggling to properly use ansible's user module. The problem is every time I run my playbook, the users I created always show as changed, even if I have already created them.
I found other people with the same issue here, though I am struggling to actually fix it based on the github thread. Probably the most helpful comment that I didn't understand 👇
I can confirm that it only looked like a bug - adding the append
option to two tasks made it so that they're not always undoing the
work of the other, and fixed the permanently changed trigger. I did
not need to add "group:"
This is what my playbook looks like:
- name: Generate all users for the environment
user:
createhome: yes
state: present # to delete
name: "{{ item.user }}"
groups: "{{ 'developers' if item.role == 'developer' else 'customers' }}"
password: "{{ generic_password | password_hash('sha512') }}"
append: yes
with_items:
- "{{ users }}"
My intention is the have every user belong to their own private group (User Private Groups) but also have a developer belong to the developers group. With the current configuration currently it works, with the problem being ansible always reports the user as "changed". I'll then add the developers group to the sudoers file; hence I'd like to add the user to the developers group.
e.g.
vagrant#ubuntu-bionic:/home$ sudo su - nick
$ pwd
/home/nick
$ touch file.txt
$ ls -al
-rw-rw-r-- 1 nick nick 0 Jul 3 12:06 file.txt
vagrant#ubuntu-bionic:/home$ cat /etc/group | grep 'developers'
developers:x:1002:nick,ldnelson,greg,alex,scott,jupyter
Here is the verbose output running against vagrant locally for one of the users:
changed: [192.168.33.10] => (item={'user': 'nick', 'role': 'developer', 'with_ga': False}) => {
"append": true,
"changed": true,
"comment": "",
"group": 1004,
"groups": "developers",
"home": "/home/nick",
"invocation": {
"module_args": {
"append": true,
"comment": null,
"create_home": true,
"createhome": true,
"expires": null,
"force": false,
"generate_ssh_key": null,
"group": null,
"groups": [
"developers"
],
"hidden": null,
"home": null,
"local": null,
"login_class": null,
"move_home": false,
"name": "nick",
"non_unique": false,
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"password_lock": null,
"remove": false,
"seuser": null,
"shell": null,
"skeleton": null,
"ssh_key_bits": 0,
"ssh_key_comment": "ansible-generated on ubuntu-bionic",
"ssh_key_file": null,
"ssh_key_passphrase": null,
"ssh_key_type": "rsa",
"state": "present",
"system": false,
"uid": null,
"update_password": "always"
}
},
"item": {
"role": "developer",
"user": "nick",
"with_ga": false
},
"move_home": false,
"name": "nick",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/sh",
"state": "present",
"uid": 1002
}
Should be unrelated, but I am adding some to the developers group as I intend to grant sudo access for certain commands.

generic_password | password_hash('sha512') is not idempotent. Salt of the hash changes each time the function password_hash runs.
To make it idempotent, either run it with a specific salt
- name: Generate all users for the environment
user:
password: "{{ generic_password | password_hash('sha512', 'mysalt') }}"
, or update the password on_create only
- name: Generate all users for the environment
user:
update_password: on_create
(, or register the return values and declare changed_when).
Consider external management of passwords e.g. Ansible Vault or Passwordstore. There is a lookup plugin for passwordstore. See ansible-doc -t lookup passwordstore. See also my implementation of Passwordstore.

Related

ansible module os_keypair never returns the value of private_key

I would like to use the ansible module os_keypair to generate a ssh keypair with openstack. To do so I use the following code:
- name: create openstack ssh key pair
os_keypair:
name: my-key
auth: my authentication parameters
auth_type: password
state: present
register: key
When running that code starting from scratch i.e. no key previously generated, it works in the sense that the key is generated and uploaded in openstack.
However, I would need the private key in order to perform subsequent actions through ansible (e.g. local_action for copying the private key to a file). The module documentation says that the private key is available as one of the return value. When checking my return values I get the following:
"key": {
"created_at": null,
"fingerprint": null,
"id": null,
"is_deleted": null,
"location": null,
"name": null,
"private_key": null,
"public_key": null,
"type": "ssh",
"user_id": null
}
which looks kind of puzzling to me. Is that normal behavior or is there something wrong with that module ?
[UPDATE]
Here is a complete playbook that reproduces that behavior:
---
- hosts: localhost
become_user: ansible
become: True
tasks:
- name: create openstack ssh key pair
os_keypair:
name: my-key
auth:
auth_url: http://cloudsrv1.ill.fr:5000/v3
username: ansible-test
password: 1234
project_name: k8s
project_domain_name: default
user_domain_name: default
auth_type: password
state: present
register: key
- debug:
var: key
Playbook executed with python3 as python interpreter.
Output:
TASK [debug] ********************************************************************************************************************
ok: [localhost] => {
"key": {
"changed": true,
"failed": false,
"id": "my-key",
"key": {
"created_at": null,
"fingerprint": null,
"id": null,
"is_deleted": null,
"location": null,
"name": null,
"private_key": null,
"public_key": null,
"type": "ssh",
"user_id": null
}
}
}
and however the key is generated as can be seen from the openstack snapshot.
The problem was solved by pip installing the newest openstacksdk package (0.36).

Ansible Symbolic Link Task Role Failure

I am new to ansible and executing the following ansible task:
- name: Create symbolic links
file:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
owner: "{{ jboss_usr }}"
group: "{{ jboss_grp }}"
state: link
with_items:
- { src: "/apps/etc/jboss", dest: "/etc/jboss" }
- { src: "/apps/var/log/jboss", dest: "/var/log/jboss" }
And I got the following error:
2018-12-21 21:27:23,469 p=15185 u=ex_sam | failed: [hostname.x] (item={u'dest': u'/etc/jboss', u'src': u'/apps/etc/jboss'}) => {
"changed": false,
"invocation": {
"module_args": {
"attributes": null,
"backup": null,
"content": null,
"delimiter": null,
"dest": "/etc/jboss",
"diff_peek": null,
"directory_mode": null,
"follow": true,
"force": true,
"group": "jboss",
"mode": null,
"original_basename": null,
"owner": "jboss",
"path": "/etc/jboss",
"recurse": false,
"regexp": null,
"remote_src": null,
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": "/apps/etc/jboss",
"state": "link",
"unsafe_writes": null,
"validate": null
}
},
"item": {
"dest": "/etc/jboss-as",
"src": "/apps/etc/jboss"
},
"msg": "Error while linking: [Errno 13] Permission denied",
"path": "/etc/jboss-as",
"state": "absent"
}
I am trying to find out why the symbolic link creation failed.
I read the following:
https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#id6
I says the "changed" attribute is a boolean indicating if the task had to make changes.
But, there are lots of null parameters in the invocation:module_args elements of the json
Does that mean the values are really “null” or they are being set to a default value?
I have looked into the ansible documentation and I am not sure if the invocation:module_args null values are representative of the outcome of the trying to create the symbolic link i.e. are the null input or output of executing the tasks.
I think some of the nulls are defaults, but will appreciate some helpful comments on the possible relation between the json returned in my ansible error log and the actual "Error while linking: [Errno 13] Permission denied".
Thanks all for you anticipated help.
I think, is the permissions on /etc. So probably you need to add the option:
become: true
To your task.

Ansible inventory: replace issues

I have an inventory like this:
[all:vars]
env_cidr_prefix='172.25'
antother_var="foo"
[VPN_SERVER]
vpn-server ansible_host="{{ env_cidr_prefix}}.0.1"
During ansible playbook, the inventory holds only private ip address.
I wan't to replace the content of "ansible_host=" with the public ip
Example of a playbook:
- name: grab the vpn public_ip
set_fact: PUBLIC_IP="{{ instance_eip.public_ip }}"
when: inventory_hostname |search("vpn-server")
- name: update inventory with the vpn public ip
replace:
path: "{{ inventory_file }}"
regexp: "{{ ansible_host }}"
replace: "{{ PUBLIC_IP }}"
when: inventory_hostname |search("vpn-server")
if
ansible_host="172.25.0.1"
the replace module will work correctly.
but this fails
ansible_host="{{ env_cidr_prefix}}.0.1"
debug output:
ok: [vpn-server] => {
"changed": false,
"invocation": {
"module_args": {
"after": null,
"attributes": null,
"backup": false,
"before": null,
"content": null,
"delimiter": null,
"directory_mode": null,
"encoding": "utf-8",
"follow": false,
"force": null,
"group": null,
"mode": null,
"owner": null,
"path": "/home/toluna/ansible/openvpn/env.properties",
"regexp": "172.25.0.11",
"remote_src": null,
"replace": "1.1.1.1",
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": null,
"unsafe_writes": null,
"validate": null
}
},
"msg": ""
}
Note, I cant use the add_host module since the playbooks are running in different stages
Is there a better way to do it ?
Thanks
OK, after testing it I guess I understand what are you trying to achieve.
Several parts here:
The inventory file is like this:
vpn-server ansible_host="{{ env_cidr_prefix}}.0.1"
And you are trying to replace 172.25.0.1 literal which doesn't exist in your file. You have "{{ env_cidr_prefix}}.0.1" and not 172.25.0.1.
Options:
If you want to replace that way, you can use a Jinja2 file in your role, replace the variable and the inventory file the same way you are trying.
Override the /etc/hosts file of your Jenkins (I really don't like too much) and play with the host name.
Play with your hosts variable in the playbook like:
Hosts Playbooks:
- name : Test
hosts: "{{ variable_vpn_ip | default('vpn-server') }}"
And call it reading from a variable that you will change ad-hoc or:
ansible-playbook play.yml -e "variable_vpn_ip=172.25.0.1"

Make ansible run a task in a playbook multiple times based on variables

I am trying to use the Ansible URI Module to log into multiple webpages and check the environments are up and working.
At the moment, i want it to just log into 2 webpages (Peoplesoft envs), but i would like a vars file that i can add to each time i want to to check a new page.
This is what i have so far, but it doesnt seem to log into both pages, just 1 of them....any help woould be appreciated.
Playbook -
---
- name: Run Selenium Test Scripts
hosts: local
vars_files:
- /etc/ansible/uri_module/vars_uri.yml
tasks:
- name: Installing URI dependancy
yum: name=python-httplib2.noarch state=present
- name: Log into Webpage
uri:
url: http://{{appserver}}:{{port}}/{{dbname}}/signon.html
method: POST
body: "name={{userid}}&password={{password}}&enter=Sign%20in"
with_file: /etc/ansible/uri_module/vars_uri.yml
Vars file
---
- { name: 'dog', appserver: 'st1920', port: '8100', dbname: 'dbdog', userid: 'user', password: 'pass' }
- { name: 'cat', appserver: 'st1921', port: '8300', dbname: 'dbcat', userid: 'user', password: 'pass' }
Output with -vvvv
ok: [local] => {"changed": false, "content_language": "en-US", "content_length": "1831", "content_type": "text/html", "date": "Thu, 13 Oct 2016 11:45:23 GMT", "invocation": {"module_args": {"backup": null, "body": "name=user&password=pass&enter=Sign%20in", "body_format": "raw", "content": null, "creates": null, "delimiter": null, "dest": null, "directory_mode": null, "follow": false, "follow_redirects": "safe", "force": null, "force_basic_auth": false, "group": null, "method": "POST", "mode": null, "owner": null, "password": null, "regexp": null, "remote_src": null, "removes": null, "return_content": false, "selevel": null, "serole": null, "setype": null, "seuser": null, "src": null, "status_code": [200], "timeout": 30, "url": "http://st1921:8300/dbcat/signon.html", "user": null, "validate_certs": true, "with_file": "/etc/ansible/uri_module/vars_uri.yml"}, "module_name": "uri"}, "last_modified": "Wed, 13 Aug 2014 11:42:42 GMT", "redirected": false, "server": "WebSphere Application Server/7.0", "status": 200}
Using the vars file, i want it to log into the dog environment, tell me its there, log into the cat environment, tell me its there. Then if i have a horse, frog, or what ever environment, i can keep adding to the vars file without adding to or changing the playbook. At the moment it only logs into cat and i dont know why.
Have i goen down the right route with this? Is there a better way of doing it? With it not giving an error, im struggling to figure out the issue!
Thanks.
I don't think it is possible to use with_file like that.
It would be cleaner to store the vars_uri dictonaries in a list like this:
---
vars_uri:
- { name: 'dog', appserver: 'st1920', port: '8100', dbname: 'dbdog', userid: 'user', password: 'pass' }
- { name: 'cat', appserver: 'st1921', port: '8300', dbname: 'dbcat', userid: 'user', password: 'pass' }
and loop over it with with_items like this:
---
# Run Selenium Test Scripts
hosts: local
vars_files:
- /etc/ansible/uri_module/vars_uri.yml
tasks:
- name: Installing URI dependancy
yum: name=python-httplib2.noarch state=present
- name: Log into Webpage
uri:
url: http://{{ item.appserver }}:{{ item.port }}/{{ item.dbname }}/signon.html
method: POST
body: "name={{ item.userid }}&password={{ item.password }}&enter=Sign%20in"
with_items: vars_uri

Error when running this ansible play "ERROR! 'unicode object' hasno attribute 'comment'

Here is my playbook
- name: Add multiple users
user:
name: "{{ item[0].name }}"
comment: "{{ item[0].comment }}"
uid: "{{ item[0].uid }}"
groups: "{{ item[0].groups}}"
shell: /bin/bash
with_nested:
- "{{ name }}"
- "{{ comment }}"
- "{{ uid }}"
- "{{ groups }}"
Here is my vars file
---
name:
- test1
- test2
comment:
- "comment1"
- "comment2"
uid:
- 150
- 151
groups: "sudo, admin"
I'm not sure what is causing this, any ideas? I believe I may need to use with subelement instead of with nested? Am I on the right track there?
UPDATE:
Changed my code but am now experiencing the following. Updated code and error message
- name: Add new group if it doesn't exist already
group:
name: "{{ group }}"
when: group is defined
- name: Add multiple users
user:
name: "{{ item.0 }}"
comment: "{{item.1 }}"
uid: "{{ item.2 }}"
group: "{{ group }}"
groups: "{{ groups }}"
append: yes
with_together:
- "{{ name }}"
- "{{ comment }}"
- "{{ uid }}"
- "{{ group }}"
And variable file:
name:
- test1
- test2
comment:
- "comment1"
- "comment2"
uid:
- 150
- 151
group: sudo
groups:
- admin
- test
However, now I am receiving this error.
failed: [127.0.0.1] => (item=[u'test1', u'comment1', 150, u'sudo']) => {"failed": true, "invocation": {"module_args": {"append": true, "comment": "comment1", "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": "sudo", "groups": "{'ungrouped': ['127.0.0.1'], 'all': ['127.0.0.1']}", "home": null, "login_class": null, "move_home": false, "name": "test1", "non_unique": false, "password": null, "remove": false, "shell": null, "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on ubuntu-512mb-sfo1-01", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": "150", "update_password": "always"}, "module_name": "user"}, "item": ["test1", "comment1", 150, "sudo"], "msg": "Group 'all': ['127.0.0.1']} does not exist"}
failed: [127.0.0.1] => (item=[u'test2', u'comment2', 151, None]) => {"failed": true, "invocation": {"module_args": {"append": true, "comment": "comment2", "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": "sudo", "groups": "{'ungrouped': ['127.0.0.1'], 'all': ['127.0.0.1']}", "home": null, "login_class": null, "move_home": false, "name": "test2", "non_unique": false, "password": null, "remove": false, "shell": null, "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on ubuntu-512mb-sfo1-01", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": "151", "update_password": "always"}, "module_name": "user"}, "item": ["test2", "comment2", 151, null], "msg": "Group 'all': ['127.0.0.1']} does not exist"}
The problem is conflicting variable names. groups is a reserved variable and holds the groups from the inventory. And all is a automatically generated group which holds all the hosts of your inventory.
From the docs:
Even if you didn’t define them yourself, Ansible provides a few variables for you automatically. The most important of these are hostvars, group_names, and groups. Users should not use these names themselves as they are reserved. environment is also reserved.
and
groups is a list of all the groups (and hosts) in the inventory. This can be used to enumerate all hosts within a group.
Simply rename your variable and it should work. In general it's a good idea to prefix all variables of a role with the role name. This gets more important if you use 3rd party roles, e.g. from Ansible Galaxy, just to avoid conflicts. So instead of groups you could use myrole_groups and can be quite sure there never will be conflicts.

Resources