Ansible sum int value to a registered value - math

I need to sum an integer number to a registered value with ansible.. I tried with this but it doesn't works -->
---
- hosts: localhost
gather_facts: false
tasks:
- name: Ricavo ultima linea /etc/passwd
shell: tail -1 /etc/passwd
register: lastuser
ignore_errors: no
- set_fact:
uid: "{{ lastuser.stdout.split(':')[2] }}"
gid: "{{ lastuser.stdout.split(':')[3] }}"
newuid: "{{ uid|int + 5 }}"
newgid: "{{ gid|int + 5 }}"
- debug:
msg: "UID is {{ uid }} GID is {{ gid }}"
- debug:
msg: "NEW UID is {{ newuid }} GID is {{ newgid }}"
but i get -->
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'uid' is undefined\n\nThe error appears to have been in '/tmp/test_ansible/get_lastuser.yaml': line 10, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - set_fact:\n ^ here\n"}
Thanks in advance!

I think the issue is that the uid and gid variables are not available till the completion of set_fact task. It should work if you split the set_fact tasks. Something like below:
- set_fact:
uid: "{{ lastuser.stdout.split(':')[2] }}"
gid: "{{ lastuser.stdout.split(':')[3] }}"
- set_fact:
newuid: "{{ uid|int + 5 }}"
newgid: "{{ gid|int + 5 }}"
- debug:
msg:
- "UID is {{ uid }} GID is {{ gid }}"
- "NEW UID is {{ newuid }} GID is {{ newgid }}"

Related

How to handle async with inner and outer loops

I am trying to use ansible to manage VM extensions on our Azure VMs across multiple subscriptions. The implementation of inner and outer loops to correctly access nested lists of dictionaries has me struggling to understand how I can run this async without polling so this doesn't take days to execute (doubt anybody has a maintenance window that big). The biggest issue I am running into is I cannot change extensions on VMs that are not running, so I need to power them on, install or remove the extension, then power those previously powered on devices back off.
Because I have to use separate files to iterate (nested lists of dictionaries are fun), I don't know how to use async to run these deployments concurrently.
This is what I am currently using:
main.yml
---
- name: Get subscription
azure_rm_subscription_info:
register: sub_result
- name: get all vms
azure_rm_virtualmachine_info:
subscription_id: "{{ item.subscription_id }}"
loop: "{{ sub_result.subscriptions }}"
register: all_vms
- include_tasks: loopvms.yml
loop: "{{ all_vms.results }}"
loop_control:
loop_var: vmlist
loopvms.yml
---
# set the subscription ID for current loop
- set_fact:
sub_id: "{{ vmlist.item.subscription_id }}"
- include_tasks: extensions.yml
loop: "{{ vmlist.vms }}"
loop_control:
loop_var: vm
when: vmlist.vms is defined
ignore_errors: true
extensions.yml
---
- debug:
msg: "{{ vm.name }} in subscription {{ sub_id }}"
- name: power on vm if off
azure_rm_virtualmachine:
name: '{{ vm.name }}'
resource_group: '{{ vm.resource_group }}'
subscription_id: "{{ sub_id }}"
started: true
register: vmstate
ignore_errors: true
- name: Install Extension - Windows
azure_rm_virtualmachineextension:
subscription_id: "{{ sub_id }}"
resource_group: "{{ vm.resource_group }}"
virtual_machine_name: "{{ vm.name }}"
name: "{{ extension_name }}"
publisher: "{{ publisher }}"
type_handler_version: 1.0
auto_upgrade_minor_version: true
virtual_machine_extension_type: "{{ extension_type }}"
state: "{{ 'present' if (vm.os_type == 'Windows' and target_os == 'Windows') else 'absent' }}"
ignore_errors: true
- name: Install Extension - Linux
azure_rm_virtualmachineextension:
subscription_id: "{{ sub_id }}"
resource_group: "{{ vm.resource_group }}"
virtual_machine_name: "{{ vm.name }}"
name: "{{ extension_name }}"
publisher: "{{ publisher }}"
type_handler_version: 1.0
auto_upgrade_minor_version: true
virtual_machine_extension_type: "{{ extension_type }}"
state: "{{ 'present' if (vm.os_type == 'Linux' and target_os == 'Linux') else 'absent' }}"
ignore_errors: true
- name: power off vms that were turned on
azure_rm_virtualmachine:
name: '{{ vm.name }}'
resource_group: '{{ vm.resource_group }}'
subscription_id: '{{ sub_id }}'
started: false
when: vmstate.powerstate_change == 'poweron'
ignore_errors: true
I am currently ignoring errors to avoid a deployment failing on one VM preventing the rest of the VMs from getting the deployment.
Since I have to use include_tasks twice to iterate, I am not sure how I can run these in parallel. The way it currently executes the 'extensions.yml' task file is only working with one VM at a time. I need to have the tasks grouped like this though, since I need to keep track of the power state per VM. Is there maybe some other way I should be going about this?

Error "Vars in a Task must be specified as a dictionary, or a list of dictionaries"

'data_list' consists of the values in the csv file. I want to use the values in 'data_list' to loop through the parameters in the 'Create user' section of the playbook, but I am getting this error after running my playbook:
TASK [Create Multiple Users : Create multiple users] ***************************
fatal: [10.16.220.30]: FAILED! => {"reason": "Vars in a Task must be specified as a dictionary, or a list of dictionaries\n\nThe error appears to be in '/runner/project/Windows AD/roles/Create Multiple Users/tasks/Create_multiple_users.yml': line 14, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n - \"{{ item.groups }}\"\n vars: data_list\n ^ here\n"}
This is my playbook:
---
- name: Read Users
hosts: localhost
vars:
data_list: []
tasks:
- read_csv:
path: user.csv
key: name
fieldnames: name,firstname,surname,displayName,groups
delimiter: ','
register: userdata
- name: Extract the list
set_fact:
data_list: "{{ data_list + [{ 'name': item.value.name, 'firstname': item.value.firstname, 'surname': item.value.surname, 'displayName': item.value.displayName, 'groups': item.value.groups }] }}"
loop: "{{ userdata.dict|dict2items }}"
- name: Create user accounts
hosts: "{{ hostname }}"
gather_facts: false
any_errors_fatal: false
become: yes
become_method: runas
become_user: admin
roles:
- { role: Create Multiple Users }
- name: Create users
community.windows.win_domain_user:
name: "{{ item.name }}"
firstname: "{{ item.firstname }}"
surname: "{{ item.surname }}"
attributes:
displayName: "{{ item.firstname + ' ' + item.surname }}"
groups:
- "{{ item.groups }}"
vars: data_list
with_items:
- "{{ data_list }}"
What is the correct vars that I should write?
This is the line causing the error in your task
vars: data_list
As mentioned in your error message, the vars section should look like:
vars:
var1: value1
var2: value2
But this is not the only problem in you above script. You are gathering your csv data in a separate play on localhost and setting that info as a fact in variable data_list. When your first play is over, that var will only be known from the localhost target. If you want to reuse it in a second play targeting other hosts, you'll have to get that var from the hostvars magic variable
{{ hostvars.localhost.data_list }}
This is not the best approach here as you can easily shorten your playbook to a single play. The trick here is to delegate your csv gathering task to localhost and set run_once: true so that the registered var is calculated only once and distributed to all hosts with the same value. You can also drop the set fact which basically copies the same key: value to a new var.
Here is an (untested) example playbook to show you the way:
---
- name: Create multiple Windows AD user accounts from CSV
hosts: "{{ hostname }}"
gather_facts: false
tasks:
- name: read csv from localhost (single run same registered var for all hosts)
read_csv:
path: user.csv
key: name
fieldnames: name,firstname,surname,displayName,groups
delimiter: ','
register: userdata
run_once: true
delegate_to: localhost
- name: Create users
community.windows.win_domain_user:
name: "{{ item.name }}"
firstname: "{{ item.firstname }}"
surname: "{{ item.surname }}"
attributes:
displayName: "{{ item.firstname + ' ' + item.surname }}"
groups:
- "{{ item.groups }}"
# This will work on any number of hosts as `userdata`
# now has the same value for each hosts inside this play.
# we just have to extract the values from each keys from
# `userdata` and loop on that list
loop: "{{ userdata.dict | dict2items | map(attribute='value') }}"

Ansible: How to fill dict value with random passwords?

given this dictionary:
passes:
sql_x:
password:
sql_y:
password:
I want to create random passwords for any key in the passes dict.
How do I loop through the keys in the dict and fill the password value with a random password?
I was able to do it with a list but I need to use a dict.
Something like this:
- name: create passwords
set_fact: "{{ item.value.password}}": "{{ lookup('password', '/dev/null', seed=inventory_hostname) }}"
loop: "{{ lookup('dict', passes) }}"
This code above does not work of course, just for clearance what I am trying to achieve.
Thanks for any hint.
you loop over dict2items
- name: "make this working"
hosts: localhost
vars:
passes:
sql_x:
password:
sql_y:
password:
tasks:
- name: Debug
set_fact:
passes: "{{ passes | combine ({item.key: {'password': password}}) }}"
loop: "{{ passes | dict2items }}"
vars:
password: "{{ lookup('password', '/dev/null') }}"
- name: display
debug:
msg: "{{ passes }}"
result:
ok: [localhost] => {
"msg": {
"sql_x": {
"password": "kcOqz_mbIiiT0Wo_2Qox"
},
"sql_y": {
"password": "TMN_nKbnAEIzI5w-8Of."
}
}
}

Ansible Get dict value from key

I have two files, one with 2 dict and another playbook that use those dict.
Vars file:
data: {path: ~/prod/iac/playbook/test.conf, conf: test2}
vagrant: {path: ~/prod/iac/playbook/test.conf/test2.conf, conf: test4}
and the playbook:
---
- hosts: localhost
tasks:
- name: Include var for nginx config
include_vars:
file: ~/prod/iac/playbook/vars.yml
name: conf_vars
- name: overide doc configuration
shell: echo "{{ item[path] }}" > test.conf
with_items: " {{ conf_vars }}"
My vars is correctly loaded but i can't get the dict value...
Any idea ?
conf_vars is a dictionary, with_items is to be used with lists.
You may want to use with_dict:
- name: overide doc configuration
shell: echo "{{ item.value[path] }}" > test.conf
with_dict: "{{ conf_vars }}"

Ansible: can't access dictionary value - got error: 'dict object' has no attribute

---
- hosts: test
tasks:
- name: print phone details
debug: msg="user {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{ users }}"
vars:
users:
alice: "Alice"
telephone: 123
When I run this playbook, I am getting this error:
One or more undefined variables: 'dict object' has no attribute 'name'
This one actually works just fine:
debug: msg="user {{ item.key }} is {{ item.value }}"
What am I missing?
This is not the exact same code. If you look carefully at the example, you'll see that under users, you have several dicts.
In your case, you have two dicts but with just one key (alice, or telephone) with respective values of "Alice", 123.
You'd rather do :
- hosts: localhost
gather_facts: no
tasks:
- name: print phone details
debug: msg="user {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{ users }}"
vars:
users:
alice:
name: "Alice"
telephone: 123
(note that I changed host to localhost so I can run it easily, and added gather_facts: no since it's not necessary here. YMMV.)
You want to print {{ item.value.name }} but the name is not defined.
users:
alice: "Alice"
telephone: 123
should be replaced by
users:
name: "Alice"
telephone: 123
Then both the name and the telephone attribute are defined within the dict (users).
I found out that with dict only works when giving the dict inline. Not when taking it from vars.
- name: ssh config
lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^#?\s*{{item.key}}\s'
line: '{{item.key}} {{item.value}}'
state: present
with_dict:
LoginGraceTime: "1m"
PermitRootLogin: "yes"
PubkeyAuthentication: "yes"
PasswordAuthentication: "no"
PermitEmptyPasswords: "no"
IgnoreRhosts: "yes"
Protocol: 2
If you want to take it from vars which can also be defined globally or on some other place, you can use lookup.
- name: ssh config
lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^#?\s*{{item.key}}\s'
line: '{{item.key}} {{item.value}}'
state: present
loop: "{{ lookup('dict', sshd_config) }}"
vars:
sshd_config:
LoginGraceTime: "1m"
PermitRootLogin: "yes"
PubkeyAuthentication: "yes"
PasswordAuthentication: "no"
PermitEmptyPasswords: "no"
IgnoreRhosts: "yes"
Protocol: 2
small correction:
- name: print phone details
debug: msg="user {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{ users }}" <<<<<<<<<<<<<<<<

Resources