I'm using Ansible v2.9.10 and trying to use the user module by following the official docs here:
https://docs.ansible.com/ansible/2.9/modules/user_module.html
I failed to use it, and I'm getting an error even when trying to run the example straight from the docs:
- name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
user:
name: johnd
comment: John Doe
uid: 1040
group: admin
Like so:
$ ansible-playbook my-provision.yaml --check
I'm getting the following error:
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
ERROR! The field 'remote_user' is supposed to be a string type, however the incoming data structure is a <class 'ansible.parsing.yaml.objects.AnsibleMapping'>
The error appears to be in 'my-provision.yaml': line 1, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
^ here
Am I missing something?
Related
I have a playbook that includes a set of tasks to obtain the status of an IBM MQ Channel. The playbook is passed the Channel name and I run the runmqsc command on each server
and register the output which unfortunately is on many lines, so i need to get this onto one line
echo "DISPLAY CHS('{{CHANNEL_NAME}}') STATUS RQMNAME"| runmqsc {{QMGR}}|grep -v DISPLAY|sed 's/^[^ ].*$/%/g' | tr -s " " | tr -d "\n" | tr "%" "\n"|grep CHANNEL|sed 's/ CURRENT//g' | sed 's/^ //g'|sed 's/ *$//g'
which gave the output
CHANNEL(CHANNEL) CHLTYPE(CLUSSDR) CONNAME(1.2.3.4(1414)) RQMNAME(QMGR) STATUS(RUNNING) SUBSTATE(MQGET) XMITQ(XMIT_QUEUE)
this I parsed to create a list CHSstatus (when CHANNEL_NAME is a wildcard I can have more than one result). From this list I then set variables to the STATUS of the channel.
- set_fact:
allStopped: "{%if splitStatus.STATUS == 'STOPPED'%}{{allStopped + [splitStatus.CHANNEL]}}{%else%}{{allStopped}}{%endif%}"
allRunning: "{%if splitStatus.STATUS == 'RUNNING'%}{{allRunning + [splitStatus.CHANNEL]}}{%else%}{{allRunning}}{%endif%}"
mixedState: "{%if splitStatus.STATUS != 'STOPPED' and splitStatus.STATUS != 'RUNNING'%}{{mixedState + [splitStatus.CHANNEL]}}{%else%}{{mixedState}}{%endif%}"
with_items: "{{CHSstatus}}"
loop_control:
loop_var: splitStatus
This has been working fine for a while with no issues. The problem with this is that you actually need to know the Channel name and as I wanted to enhance it to get the status of all the channels in a CLUSTER I put a 'wrapper' task in front of it to collect the channel names and use these to pass them.
My playbook now calls the MQ command to get these channel names, we can pass the CLUSTER name and register the result in clusterChannels, and include the previously working task with each channel name
- name: Status of Cluster channels
include: channelstatus.yml CHANNEL_NAME={{item}}
with_items: "{{clusterChannels.stdout_lines}}"
This now fails when trying to access the CHSStatus list, I get the error is ERROR! Unexpected Exception: unhashable type: 'dict'
I have tried both methods and compared some variables for each call of the original tasks
Original
"CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]",
"CHSstatus Type = list",
"CHANNEL_NAME = CHANNEL1",
"CHANNEL_NAME type = AnsibleUnicode"
with the wrapper
"CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]",
"CHSstatus Type = list",
"CHANNEL_NAME = CHANNEL1",
"CHANNEL_NAME type = AnsibleUnsafeText"
I have tried to find information about what AnsibleUnsafeText variable is and found that
'Since you are registering the result of a command in a variable, Ansible can't know what will be the content which becomes delivered. Therefore the registered Text output is marked as Unsafe.'
This has confused me a bit as I am running two commands, one to get the list of Channels in the wrapper and another to get the status of each individual channel in the original unchanged code. The registered variable is of type list but when I pass each channel from the wrapper it becomes an AnsibleUnsafeText type, I noticed that each of the registered variables for both commands when run with_items do indeed have an AnsibleUnsafeText type.
Can i convert this in any way? I have seen answers on how to convert to int, and i have tried item|string but this did not work either.
My original playbook used roles and i created a cutdown version of this using tasks only, this included cut down versions of channelstatus.yml and this worked OK, i then converted this to a role again including the same file and this failed.
One thing i notices in both cases of my MQ command registered results gets parsed successfully but the error occurs when I try to use this list of dict.
Example below is a list which contains a single dict, this can be a list of multiple dict's when run
CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]
and a simple debug command is enough for this error to fail the playbook...
- debug:
msg:
- "{{item.CHANNEL}}"
- "{{item.STATUS}}"
with_items: "{{CHSstatus}}"
ERROR! Unexpected Exception: unhashable type: 'dict'
however
- debug:
msg:
- "{{CHSstatus[0].CHANNEL}}"
- "{{CHSstatus[0].STATUS}}"
works fine, which really does not make sense
Any help appreciated
If you look at a host which was set up be SaltStack, then it is sometimes like looking at a binary file with vi.
You have no clue how the config/file was created.
This makes trouble shooting errors hard. Reverse engineering where a file comes from takes too much time.
My goal: Make it easy to find the way from looking at the unix config file on the minion (created by salt) to the source where this configuration came from. Like $Id$ in svn and cvs.
One idea a friend and I had:
The state file.managed should (optionally) add the source of the file.
Example:
My sls file contains this:
file_foo_bar:
file.managed:
- source:
- salt://foo/bar
Then the created file should contain this comment.
# Source: salt://foo/bar
Of course this is not simple, since there are different ways to put comments into configuration files.
Is this feasible? Or is there a better solution to my goal.
Update
Usually I know what I did wrong and can find the root easily. The problem arises if several people work on a state tree.
This is a starting point where you can get the date and time of the modified file when its managed by Salt by using Salt Pillar.
Lets call our variable salt_managed. Create a pillar file like the following:
{% set managed_text = 'Salt managed: File modified on ' + salt.cmd.run('date "+%Y-%m-%d %H:%M:%S"') %}
salt_managed: {{ managed_text | yaml_dquote }}
Then on the minion when you call the pillar you will get the following result:
$ salt-call pillar.get salt_managed
local:
Salt managed: File modified on 2016-10-18 11:12:40
And you can use this by adding it on the top of your config files for example like this:
{{ pillar.get('salt_managed') }}
Update:
I found a work around that might be useful for someone. Lets say we have a multiple states that could modify the same file. How can we know that State X is the responsible for modifying that file ? by doing the following steps:
1- I have created a state like this one:
Create a File:
file.managed:
- name: /path/to/foofile
- source: salt://statedir/barfile
Add file header:
file.prepend:
- name: /path/to/foofile
- text: "This file was managed by using this salt state {{ sls }}"
The contents of barfile is:
This is a new file
2- Call the state from the minion and this will be the result:
$ salt-call state.sls statedir.test
local:
----------
ID: Create a File
Function: file.managed
Name: /path/to/foofile
Result: True
Comment: File /path/to/foofile updated
Started: 07:50:45.254994
Duration: 1034.585 ms
Changes:
----------
diff:
New file
mode:
0644
----------
ID: Add file header
Function: file.prepend
Name: /path/to/foofile
Result: True
Comment: Prepended 1 lines
Started: 07:50:46.289766
Duration: 3.69 ms
Changes:
----------
diff:
---
+++
## -1,1 +1,2 ##
+This file was managed by using this salt state statedir.test
This is a new file
Summary for local
------------
Succeeded: 2 (changed=2)
Failed: 0
------------
Total states run: 2
Currently the content of foofile is:
This file was managed by using this salt state statedir.test
This is a new file
I have read the documentation related expect module on here
I'm trying to add a CentOS7 to 2012 AD Domain controller, here is my playbook,
- name: Attempt to join the server to AS
expect:
command: realm join --user=admin#mydomain.local mydomain.local
responses:
(?i)Password for admin#mydomain.local: abc123
Ansible playbook fails, saying the password is incorrect, is this the correct way of using expect?
Have you try to incapsulate the password in quotes like so?
(?i)Password for admin#mydomain.local: "abc123"
Say the minion host has a default yaml configuration named myconf.yaml. What I want to do is to edit parts of those yaml entries using values from a pillar. I can't even begin to think how to do this on Salt. The only think I can think of is to run a custom python script on the host via cmd.run and feed it with input via arguments, but this seems overcomplicated.
I want to avoid file.managed. I cannot use a template, since the .yaml file is big, and can change by external means. I just want to edit a few parameters in it. I suppose a python script could do it but I thought salt could do it without writing s/w
I have found salt.states.file.serialize with the merge_if_exists option, I will try this and report.
You want file.serialize with the merge_if_exists option.
# states/my_app.sls
something_conf_file:
file.serialize:
- name: /etc/my_app.yaml
- dataset_pillar: my_app:mergeconf
- formatter: yaml
- merge_if_exists: true
# pillar/my_app.sls
my_app:
mergeconf:
options:
opt3: 100
opt4: 200
On the target, /etc/my_app.yaml might start out looking like this (before the state is applied):
# /etc/my_app.yaml
creds:
user: a
pass: b
options:
opt1: 1
opt2: 2
opt3: 3
opt4: 4
And would look like this after the state is applied:
creds:
user: a
pass: b
options:
opt1: 1
opt2: 2
opt3: 100
opt4: 200
As far as I can tell this uses the same algorithm as pillar merges, so e.g. you can merge or partially overwrite dictionaries, but not lists; lists can only be replaced whole.
This can be done for both json and yaml with file.serialize. Input can be inline on the state or come from a pillar. A short excerpt follows:
state:
cassandra_yaml:
file:
- serialize
# - dataset:
# concurrent_reads: 8
- dataset_pillar: cassandra_yaml
- name: /etc/cassandra/conf/cassandra.yaml
- formatter: yaml
- merge_if_exists: True
- require:
- pkg: cassandra-pkgs
pillar:
cassandra_yaml:
concurrent_reads: "8"
I wanted to create a config file in YAML that stores a few translations. To encapsulate everything, I began to nest the options.
While parsing the file, I see the following error:
Failed to read data from customize.yaml\customize.yaml: yaml: line 30: mapping values are not allow
ed in this context
The parser refers to the following lines:
contact:
title: Contact
form:
name: Name
error: Please enter your name.
email: Email
error: Please enter your email address.
phone: Phone
error: Please enter your phone number.
message: Message
error: Please enter a message.
send: Send
If you want the value as well as the error messages to "belong" to the key, you need to make a list of two items
name:
- Name
- error: Please enter your name.
or as another mapping with two items:
name:
value: Name
error: Please enter your name.