I'm a bit out of my wheelhouse here. Normally i code only in Powershell.
I was asked to use AWX/Ansible to automate the creation of user centric AD groups.
I did a lot of reading, figured out how to set up an inventory/project/template/survey/import hosts/credentials/ ... .
But what i can't seem to figure out, is this YAML.
According to most information i've found, it seems to be best to use an indentation of 2 spaces, no tabs.
So i did. But i keep getting the following error :
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
did not find expected key
The error appears to be in '.../CreateUserCentricGroups.yml': line 27, column 3, ...
The offending line appears to be:
- name: Setup variables
^ here
This is the YAML. It was bigger but i made it into the most basic i could think of, in order to find the error. Still, at the first action it fails.
---
- name: Create user centric AD-groups
hosts: "{{ domainName }}"
gather_facts: false
vars:
GroupNameUpper: "RCWR_UC_APP_{{ appName | upper }}"
groupDescription: "{{ appDescription }}"
domainDict:
"Labo": "DC=whatever"
domainDC: '{{domainDict[domainName | default("Labo")] | default("stop") }}' #Allow for future domains to be used.
companyDC: "whatever"
tasks:
- name: Do something with the variables, print full groupname.
debug:
msg: "{{ GroupNameUpper }}"
- name: Verify if the group already exists in AD #Error occurs here <-
register: lookupResult
win_shell: |
Try {
$user = Get-ADGroup {{ GroupNameUpper }}
echo "Group {{ GroupNameUpper }} already exists."
}
catch {
echo "Group {{ GroupNameUpper }} does not exist yet."
}
Try {
$user = Get-ADGroup "{{ GroupNameUpper }}_GrpMgmt"
echo "Group {{ GroupNameUpper }}_GrpMgmt already exists."
}
catch {
echo "Group {{ GroupNameUpper }}_GrpMgmt does not exist yet."
}
debug:
msg: "{{ lookupResult }}"
#gather_facts: false
- name: Inform that group already exists
debug:
msg: Group {{ GroupNameUpper }} already exists in {{ domainName }}.
meta: end_play #https://medium.com/opsops/finishing-playbook-on-condition-in-ansible-958b81d3476d THIS SHOULD STOP IF 1 OF THE 2 GROUPS EXIST.
when: "'already exists' in lookupResult.stdout"
- name: Create groups in AD
register: createOutput
win_shell: |
Try {
$SamAccountname = "{{ GroupNameUpper }}" -replace "_","" -replace "RCWRUC",""
New-ADUser -Name "{{ GroupNameUpper }}" -DisplayName "{{ GroupNameUpper }}" -SamAccountName $SamAccountName -Description "{{ groupDescription }}" -GroupCategory "Security" -GroupScope "DomainLocal" -Path "OU=Groups,{{ companyDC }},OU=Departments,{{ domainDC }},DC=com"
}
catch {
echo "Group {{ GroupNameUpper }} creation failed."
}
Try {
$SamAccountname = "{{ GroupNameUpper }}" -replace "_","" -replace "RCWRUC",""
New-ADUser -Name "{{ GroupNameUpper }}_GrpMgmt" -DisplayName "{{ GroupNameUpper }}_GrpMgmt" -SamAccountName $SamAccountName -Description "{{ groupDescription }}" -GroupCategory "Security" -GroupScope "DomainLocal" -Path "OU=Groups,{{ companyDC }},OU=Departments,{{ domainDC }},DC=com"
}
catch {
echo "Group {{ GroupNameUpper }}_GrpMgmt creation failed."
}
when: "'does not exist yet' in lookupResult.stdout"
failed_when: "('creation failed' in createOutput.stdout) or (createOutput.rc != 0)"
no_log: true
- name: Determine email address (Jenkins)
set_fact:
sendToEmailAddress: "{{ requestorMail }}"
when: requestorMail is defined
- name: Determine email address (non-Jenkins)
set_fact:
sendToEmailAddress: "{{ awx_user_email }}"
when: requestorMail is not defined
- name: Communicate result
delegate_to: localhost
mail:
subject: "Create AD-group {{ GroupNameUpper }}"
body: "Groups {{ GroupNameUpper }} have been created in {{ domainName }}"
host: "exchangeserver"
port: 25
to: "{{sendToEmailAddress}}"
from: "noreply#us.com"
secure: starttls
subtype: html
when: "'does not exist yet' in lookupResult.stdout"
no_log: true
...
What am i forgetting or doing wrong?
Thank you in advance for your time/help.
I don't get the exact error you listed, but your play is kind of play-shaped but not valid.
Your task is not a valid task, since it contains a play-level directive (gather_facts) and does not contain a valid action name.
Assuming you want these variables to apply to the entire play, this is a more valid (and more consistently formatted) reworking of the limited example you provided:
- name: Create user centric AD-groups
hosts: "{{ domainName }}"
gather_facts: false
vars:
GroupNameUpper: Prefix1_APP_{{ appName | upper }}
groupDescription: "{{ appDescription }}"
domainDict:
Labo: DC=whatever
domainDC: "{{ domainDict[domainName | default('Labo')] | default('stop') }}"
companyDC: Whatever
tasks:
- name: Do something with the variables
debug:
msg: "{{ domainDC }}"
Related
I want to get the state of two account from a registered results and rename them but Ansible gives error when looping over that register.
The variable I am looping on is:
user_accounts:
- old_name: account1
new_name: account11
- old_name: account2
new_name: account22
I look the accounts up.
- name: get the state of the users account
win_user:
name: "{{ item.old_name }}"
state: query
loop: "{{ user_accounts }}"
register: accounts_to_rename
- name: debug
debug:
var: accounts_to_rename
The debug results:
{
"accounts_to_rename":{
"changed":false,
"msg":"All items completed",
"results":[
{
"ansible_loop_var":"item",
"changed":false,
"failed":false,
"invocation":{
"module_args":{
"account_disabled":null,
"account_locked":null,
"description":null,
"fullname":null,
"groups":null,
"groups_action":"replace",
"home_directory":null,
"login_script":null,
"name":"account1",
"password":null,
"password_expired":null,
"password_never_expires":null,
"profile":null,
"state":"query",
"update_password":"always",
"user_cannot_change_password":null
}
},
"item":{
"old_name":"account1",
"new_name":"account11"
},
"name":"account1",
"state":"present"
},
{
"ansible_loop_var":"item",
"changed":false,
"failed":false,
"invocation":{
"module_args":{
"account_disabled":null,
"account_locked":null,
"description":null,
"fullname":null,
"groups":null,
"groups_action":"replace",
"home_directory":null,
"login_script":null,
"name":"account2",
"password":null,
"password_expired":null,
"password_never_expires":null,
"profile":null,
"state":"query",
"update_password":"always",
"user_cannot_change_password":null
}
},
"item":{
"old_name":"account2",
"new_name":"account22"
},
"name":"account2",
"state":"present"
}
],
"skipped":false
}
}
Then, I want to:
rename account1 to account11
rename account2 to account22
This is based on the state of the old account:
- name: W2K19.319_L1_Rename_administrator_account
win_shell: "Rename-LocalUser -Name '{{ item.old_name }}' -NewName '{{ item.new_name }}'"
loop: "{{ user_accounts | flatten(1) }}"
when: accounts_to_rename.results[*].state == present
The error I get:
"msg": "The conditional check 'accounts_to_rename.results[*].state == present' failed. The error was: template error while templating string: unexpected '*'.
Without the [*], Ansible gives:
"msg": "The conditional check 'accounts_to_rename.results.state == present' failed. The error was: error while evaluating conditional (accounts_to_rename.results.state == present): 'list object' has no attribute.
Rather loop on accounts_to_rename and access the new name via its item.item.new_name attribute.
So:
- name: W2K19.319_L1_Rename_administrator_account
win_shell: >-
Rename-LocalUser
-Name '{{ item.name }}'
-NewName '{{ item.item.new_name }}'
loop: "{{ accounts_to_rename.results }}"
when: item.state == 'present'
This has been a challenge and I am stuck on what else to try. Basically, I have a text file that has an unknown amount of lines. I wish to read through each line and set facts for each line that is then used as variables for a win_domain_user module.
If there is only one line in the text file, then I can accomplish my task with no issues or errors. But, if there is more than one line, I do not know how to start the process again with the next line.
For example:
- name: "Pre-task AD | Get user account info"
win_shell: |
Get-Content C:\AD\user_info.txt
register: 'read_output'
The line of text in the user_info.txt file looks like this:
first-John;last-Doe;display_name-Doe, John (US);title-Engineer;employee_type-Employee;user-john.doe;email-john.doe#company.com;customer-Some_Business;dept_groups-Test_Group
Example of two lines:
first-John;last-Doe;display_name-Doe, John (US);title-Engineer;employee_type-Employee;user-john.doe;email-john.doe#company.com;customer-Some_Business;dept_groups-Test_Group
first-Jane;last-Doe;display_name-Doe, Jane (US);title-Engineer II;employee_type-Employee;user-jane.doe;email-jane.doe#company.com;customer-Some_Business;dept_groups-Test_Group
The set facts looks like:
- set_fact:
first: "{{ read_output.stdout | regex_search(...) }}"
last: "{{ read_output.stdout | regex_search(...) }}"
user: "{{ read_output.stdout | regex_search(...) }}"
email: "{{ read_output.stdout | regex_search(...) }}"
customer: "{{ read_output.stdout | regex_search(...) }}"
title: "{{ read_output.stdout regex_search(...) }}"
display_name: "{{ read_output.stdout regex_search(...) }}"
employee_type: "{{ read_output.stdout | regex_search(...) }}"
dept_groups: "{{ read_output.stdout | regex_search(...) }}"
The winrm Active Directory related module looks like:
- name: "Active Directory | Add domain user account to domain controller"
win_domain_user:
firstname: "{{ first }}"
surname: "{{ last }}"
name: "{{ first }}{{ space }}{{ last }}"
description: "{{ title }}"
password: "{{ password_var_here}}"
upn: "{{ user }}{{ domain_fqdn_var_here }}"
email: "{{ email }}"
state: 'present'
path: "{{ ou_path_here }}"
groups: "Domain Users, {{ dept_groups }}"
attributes:
displayName: "{{ first }}{{ space }}{{ last }}"
domain_username: "{{ domainusername_var_here }}"
domain_password: "{{ domainpassword_var_here }}"
domain_server: "{{ dc_var_here }}"
register: 'add_ad_user'
I tried using a win_shell ForEach (PowerShell) loop to action the lines in C:\AD\user_info.txt but I was not able to figure out how to use set_facts within the ForEach loop and use the win_domain_user module within it. So, instead, I looked into using with_items which from what I understand is now loop. From the info above, I was going to create 2 yml files, one that has the 'Get-Content C:\AD\user_info.txt' / register and then use the include_tasks yml with block for the tasks to run against the data from the Get-Content.
The tricky part is that I do not know how many lines are going to be in that user_info.txt file. Am I on the right track as far as using two files for this or is there a more efficient way?
Each line from the file will be in the registered variable read_output.stdout_lines. We can loop with this variable and split the line using semi-colon (;) to get the individual attributes of the user.
If we take only the first line, the example below with return John:
"{{ read_output.stdout_lines[0].split(';')[0].split('-')[1] }}"
Similarly a debug task:
- name: "Pre-task AD | Get user account info"
win_shell: |
Get-Content C:\AD\user_info.txt
register: read_output
- name: Show user details after splitting
debug:
msg:
- "First: {{ first }}"
- "Last: {{ last }}"
- "Display name: {{ display_name }}"
vars:
first: "{{ item.split(';')[0].split('-')[1] }}"
last: "{{ item.split(';')[1].split('-')[1] }}"
display_name: "{{ item.split(';')[2].split('-')[1] }}"
loop: "{{ read_output.stdout_lines }}"
will produce:
"msg": [
"First: John",
"Last: Doe",
"Display name: Doe, John (US)"
]
Continuing with this theme, we can have the win_domain_user task like (not tested):
- name: "Active Directory | Add domain user account to domain controller"
win_domain_user:
firstname: "{{ first }}"
surname: "{{ last }}"
name: "{{ first }} {{ last }}"
description: "{{ title }}"
password: "{{ password_var_here }}"
upn: "{{ user }}{{ domain_fqdn_var_here }}"
email: "{{ email }}"
state: 'present'
path: "{{ ou_path_here }}"
groups: "Domain Users, {{ dept_groups }}"
attributes:
displayName: "{{ first }} {{ last }}"
domain_username: "{{ domainusername_var_here }}"
domain_password: "{{ domainpassword_var_here }}"
domain_server: "{{ dc_var_here }}"
vars:
first: "{{ item.split(';')[0].split('-')[1] }}"
last: "{{ item.split(';')[1].split('-')[1] }}"
display_name: "{{ item.split(';')[2].split('-')[1] }}"
name: "{{ first }} {{ last }}"
title: "{{ item.split(';')[3].split('-')[1] }}"
email: "{{ item.split(';')[6].split('-')[1] }}"
user: "{{ item.split(';')[5].split('-')[1] }}"
dept_groups: "{{ item.split(';')[8].split('-')[1] }}"
loop: "{{ read_output.stdout_lines }}"
As already stated in the comments, the real good solution would be to change your file format at source to something that is easy to parse. Being in ansible the best format would indeed be yaml/json. But any other standard would make this much easier (xml, csv...).
Note: I know your current data is actually csv with semicolon delimiters. But it doesn't have a header and values contain the field name that must be extracted on each line
As an other remark: registering the output of a cat command (e.g. Get-Content) from a (win-)shell to get a file content is a bad practice. This is precisely why the fetch and slurp modules exist.
The below is just a workaround to get a dict out of your custom and not really ideal format. It will break as soon as a value does not strictly respect the current inferred schema (i.e. contains a dash, a semicolon, a new line...) and will probably become a real nightmare to maintain every time you encounter an exception. But at least it does what you expect from your example and you can build upon that to adapt it to your exact needs.
Edit: my original solution was using a regex (see history if you want to look at it). But #seshadri_c opened my eyes on a much simpler solution that leads to the same result using only split. I borrowed the idea while keeping the key/value mapping dynamic.
Given the following files/user_data.txt
first-John;last-Doe;display_name-Doe, John (US);title-Engineer;employee_type-Employee;user-john.doe;email-john.doe#company.com;customer-Some_Business;dept_groups-Test_Group
first-Jane;last-Doe;display_name-Doe, Jane (US);title-Engineer II;employee_type-Employee;user-jane.doe;email-jane.doe#company.com;customer-Some_Business;dept_groups-Test_Group
The loop_bad_file.yml playbook
---
- name: Loop on a poorly formated file
hosts: localhost
gather_facts: false
vars:
# File name on the remote (here relative to the current dir for the example....)
bad_file_name: files/user_data.txt
# list of user dicts we get by:
#
# 1. spliting each line from the original file (`select` removes the last trainling blank line)
# So we get ['line1....', 'line2....']
#
# 2. split each line on semicolons so we get:
# [['key1-value1', 'key2-value2', ....], ['key1-value1', 'key2-value2', ....], ...]
#
# 3. split each field-value on a dash so we get:
# [[['key1', 'value1'], ['key2', 'value2'], ..., [['key1', 'value1'], ['key2', 'value2'], ...], ...]
#
# 4. Turn this back into a list of dicts using the key/values pairs in each element
user_list: "{{
(bad_file.content | b64decode).split('\n') | select
| map('split', ';')
| map('map', 'split', '-')
| map('items2dict', key_name=0, value_name=1)
}}"
tasks:
- name: Get file content from remote
slurp:
src: "{{ bad_file_name }}"
register: bad_file
- name: Show the result using the transformed list of dicts
vars:
debug_msg: |-
This is entry for {{ item.display_name }}
first: {{ item.first }}
last: {{ item.last }}
title: {{ item.title }}
employee type: {{ item.employee_type }}
email: {{ item.email }}
customer: {{ item.customer }}
department group: {{ item.dept_groups }}
debug:
msg: "{{ debug_msg.split('\n') }}"
loop: "{{ user_list }}"
Gives:
$ ansible-playbook loop_bad_file.yml
PLAY [Loop on a poorly formated file] *********************************************************************
TASK [Get file content from remote] *********************************************************************
ok: [localhost]
TASK [Show the result using the transformed list of dicts] *********************************************************************
ok: [localhost] => (item={'first': 'John', 'last': 'Doe', 'display_name': 'Doe, John (US)', 'title': 'Engineer', 'employee_type': 'Employee', 'user': 'john.doe', 'email': 'john.doe#company.com', 'customer': 'Some_Business', 'dept_groups': 'Test_Group'}) => {
"msg": [
"This is entry for Doe, John (US)",
"first: John",
"last: Doe",
"title: Engineer",
"employee type: Employee",
"email: john.doe#company.com",
"customer: Some_Business",
"department group: Test_Group"
]
}
ok: [localhost] => (item={'first': 'Jane', 'last': 'Doe', 'display_name': 'Doe, Jane (US)', 'title': 'Engineer II', 'employee_type': 'Employee', 'user': 'jane.doe', 'email': 'jane.doe#company.com', 'customer': 'Some_Business', 'dept_groups': 'Test_Group'}) => {
"msg": [
"This is entry for Doe, Jane (US)",
"first: Jane",
"last: Doe",
"title: Engineer II",
"employee type: Employee",
"email: jane.doe#company.com",
"customer: Some_Business",
"department group: Test_Group"
]
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You may want to try to use csvfile lookup plugin [1] to load data from csv file as a variable or iterable object.
[1] https://docs.ansible.com/ansible/latest/collections/ansible/builtin/csvfile_lookup.html
I'm facing a strange issue while trying to search inside a dictionary. In the code above, i'm trying to define a new var "OLD_VLANID_PP", with the value of the "name" key of the dictionary "VM_NETWORK_INFOS_PP".
If the dictionary is set and not empty, I want the var to be defined, otherwise just skip the task.
In order to test if the condition was true or false, I've added a debug task which should display the content of the dictionary.
When running my playbook, the debug task does indicates that the condition is not matched, and so doesn't display my dictionary, but Ansible keep trying to define my new var as shown in the output, and so fires an error "'item' is undefined"
---
- block:
#Search VCENTER for PPROD VM (If Windows to create => Search PG / EIther search Postgre)
- name: Search for PPROD VM already created for the CLIENT {{ CLIENTNAME }} to retrieve VLANID
set_fact:
OLD_VMNAME_PP: "{{ item.guest_name }}"
with_items: "{{ VM_EXIST_PP.virtual_machines }}"
when: item.guest_name == VMNAME_PP
delegate_to: localhost
when: ENV != "PROD" and DEPLOY != "ALL" and (VM_EXIST_PP is defined) and (VM_EXIST_PP|length > 0)
#If we successfully found the previous VM for the client, then get its network info
- name: Retrieve PPROD VM {{ VMNAME_PP }} VLAN ID
community.vmware.vmware_guest_network:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
datacenter: "{{ datacenter_name }}"
validate_certs: False
name: "{{ OLD_VMNAME_PP }}"
gather_network_info: true
register: VM_NETWORK_INFOS_PP
when: (OLD_VMNAME_PP is defined) and OLD_VMNAME_PP != ""
- block:
- debug: msg={{VM_NETWORK_INFOS_PP}}
#If we successfully found the network info, set the OLD_VLANID with previous VM VLANID
- set_fact:
OLD_VLANID_PP: "{{ (item.name) | replace('(HDS : Client)','') | replace('VLAN0','') | replace('VLAN','') | replace(' ','') }}"
with_items:
- "{{ VM_NETWORK_INFOS_PP.network_info }}"
when: item.name | regex_search('VLAN') and not item.name | regex_search('PVLAN')
when: (VM_NETWORK_INFOS_PP is defined) and VM_NETWORK_INFOS_PP != ""
Debug Output (Should display the dictionary content) :
ok: [127.0.0.1] => {
"msg": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
Error Output :
The error was: error while evaluating conditional (item.name | regex_search('VLAN') and not item.name | regex_search('PVLAN')): 'item' is undefined
Ansible Version : 2.10.7
Python Version : 3.7.3
Any help would be appreciated.
Thank you
Not sure if it's the right thing to do, but finally get ride of this issue using :
when: item is defined and item.name | regex_search('VLAN') and not item.name | regex_search('PVLAN')
note the item is defined before the regex search
I'm trying to append a number to the end of the instance name tag, i have the following code but there's a problem with the second task which i cannot find, and i've not been able to find an example of anyone else trying to solve this problem.
I'm also relatively new to Ansible and cannot find the relevant documentation to do certain things like how to lookup a value in a list like how i'm doing with my until: which is probably invalid syntax
Ansible is 2.9 and runs on the instance itself
The Tasks I have are setup to do the following
Get a list of running EC2 instances that belong to the same launch template
Loop the same amount of times as the instance list until the desired name based on item index is not found in the name tags of the ec2 list
Set the updated tag name
Current Code:
---
- name: "{{ role_name }} | Name Instance: Gather facts about current LT instances"
ec2_instance_info:
tags:
"aws:autoscaling:groupName": "{{ tag_asg }}"
"aws:ec2launchtemplate:id": "{{ tag_lt }}"
Application: "{{ tag_application }}"
filters:
instance-state-name: [ "running" ]
no_log: false
failed_when: false
register: ec2_list
- name: "{{ role_name }} | Name Instance: Generate Name"
with_sequence: count="{{ ec2_list.instances|length }}"
until: not "{{ tag_default_name }} {{ '%02d' % (item + 1) }}" in ec2_list.instances[*].tags['Name']
when: tag_name == tag_default_name
no_log: false
failed_when: false
register: item
- name: "{{ role_name }} | Name Instance: Append Name Tag"
ec2_tag:
region: eu-west-1
resource: "{{ instance_id }}"
state: present
tags:
Name: "{{ tag_default_name }} {{ '%02d' % (item + 1) }}"
when: tag_name == tag_default_name
no_log: false
failed_when: false
As requested here's the error I am getting:
ERROR! no module/action detected in task.
The error appears to be in '/app/deploy/Ansible/roles/Boilerplate/tasks/name_instance.yml': line 14, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: "{{ role_name }} | Name Instance: Generate Name"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
The error is not with the name: as i constantly get that error when there's some other error within the task body
You don't appear to have a module listed in the second task. You might be able to use debug as the module, or use set_fact and skip the register line.
I think it might also be possible to combine the last two tasks with some more advanced looping, but I'd have to play around with it to give you a working example.
Thanks to bnabbs answer i realised the problem with that version was the lack of module, after fixing that i then finished creating and testing it and now have a fully working set of tasks which has resulted in the following code.
---
- name: "{{ role_name }} | Name Instance: Gather facts about current LT instances"
ec2_instance_info:
filters:
"tag:aws:autoscaling:groupName": "{{ tag_asg }}"
"tag:aws:ec2launchtemplate:id": "{{ tag_lt }}"
"tag:Application": "{{ tag_application }}"
instance-state-name: [ "pending", "running" ]
register: ec2_list
- name: "{{ role_name }} | Name Instance: Set Needed Naming Facts"
set_fact:
tag_name_seperator: " "
start_index: "{{ (ec2_list.instances | sort(attribute='instance_id') | sort(attribute='launch_time') | map(attribute='instance_id') | list).index(instance_id) }}"
name_tag_list: "{{ (ec2_list.instances | map(attribute='tags') | list) | map(attribute='Name') | list }}"
# Generate Name from starting index of array and mod to the length of the amount of instances to help prevent conflicts when multiple instances are launched at the same time
- name: "{{ role_name }} | Name Instance: Generate Name"
set_fact:
desired_tag_name: "{{ tag_default_name }}{{ tag_name_seperator }}{{ '%02d' % (((item|int) + (start_index|int) % (name_tag_list|length)) + 1) }}"
loop: "{{ name_tag_list }}"
until: not "{{ tag_default_name }}{{ tag_name_seperator }}{{ '%02d' % (((item|int) + (start_index|int) % (name_tag_list|length)) + 1) }}" in name_tag_list
- name: "{{ role_name }} | Name Instance: Append Name Tag"
ec2_tag:
region: eu-west-1
resource: "{{ instance_id }}"
state: present
tags:
Name: "{{ desired_tag_name }}"
I'm trying to define Ansible variables this way:
user:
name: First Last
nick: '{{ vars["user"]["name"] | regex_replace("\W", "_") }}'
email: '{{ vars["user"]["nick"] }}#example.com'
And the result email is: "{{ vars[\"user\"][\"name\"] | regex_replace(\"\\W\", \"_\") }}#example.com.
I also tried to set email like this: {{ lookup("vars", "user.nick") }}#example.com or {{ lookup("vars", "user")["nick"] }}#example.com, and it says An unhandled exception occurred while running the lookup plugin 'vars'.
Is there a way to get resulting variable values as:
user:
name: First Last
nick: First_Last
email: First_Last#example.com
?
ansible 2.9.10,
python version = 3.8.5
It's not possible cross-reference keys in a dictionary. It's necessary to declare the variables outside the dictionary. For example, the playbook
- hosts: localhost
vars:
my_name: First Last
my_nick: "{{ my_name | regex_replace('\\W', '_') }}"
user:
name: "{{ my_name }}"
nick: "{{ my_nick }}"
email: "{{ my_nick }}#example.com"
tasks:
- debug:
var: user
gives (abridged)
user:
email: First_Last#example.com
name: First Last
nick: First_Last
A more flexible option is to create the variables in the loop. For example, the playbook
- hosts: localhost
vars:
users:
"First Last":
domain: example.com
tasks:
- debug:
msg:
- "name: {{ name }}"
- "nick: {{ nick }}"
- "email: {{ email }}"
loop: "{{ users|dict2items }}"
vars:
name: "{{ item.key }}"
nick: "{{ item.key|regex_replace('\\W', '_') }}"
email: "{{ nick ~ '#' ~ item.value.domain }}"
gives (abridged)
msg:
- 'name: First Last'
- 'nick: First_Last'
- 'email: First_Last#example.com'