Ansible loops are simple and powerful with mixed data. You will sure say “awesome” when you realize the easiness with loops.
During our technical discussions, we came across a use case for nested loops inside a playbook. Nested loops are easy but we need to be careful when we need some paired values inside the loop.
In below example, user want to update windows registry; 4 values per each port.
HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\<YOUR_PROTOCOL>\<SERVER_OR_CLIENT> -Name <Enabled_OR_DisabledByDefault> -Value <0_or_1> -Type DWord
So the protocol, server/client, Enable/DesabledByDefault and Value will be changed in loops. Let’s say if we have 3 protocols to be changed,
Lets create variables for the same.
protocol:
- 'PCT 1.0'
- 'SSL 2.0'
- 'SSL 3.0'
srvtype:
- 'Server'
- 'Client'
Since, Enable/DesabledByDefault coming with value pair, we will put into a dictionary as below.
regname:
- myname: 'Enabled'
type:
- name: 'Enabled'
value: 0
- myname: 'DisabledByDefault'
type:
- name: 'DisabledByDefault'
value: 1
Before I execute, I just want to print the value and see if its working correctly. For that purpose I have created a task with debug module as below.
tasks:
- name: check loop1
debug:
msg: 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols{{item[0]}}{{item[1]}} -Name {{ item[2]["myname"] }} -Value {{ item.2.type.0.value }} -Type DWord'
with_nested:
- '{{ protocol }}'
- '{{ srvtype }}'
- '{{ regname }}'
I have just run it to see the output.
$ ansible-playbook usecase-nested-loop-with-dictionary.yml
PLAY [nested] *
TASK [check loop1] ****
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\PCT 1.0\Server -Name Enabled -Value 0 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\PCT 1.0\Server -Name DisabledByDefault -Value 1 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\PCT 1.0\Client -Name Enabled -Value 0 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\PCT 1.0\Client -Name DisabledByDefault -Value 1 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server -Name Enabled -Value 0 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server -Name DisabledByDefault -Value 1 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client -Name Enabled -Value 0 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client -Name DisabledByDefault -Value 1 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server -Name Enabled -Value 0 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server -Name DisabledByDefault -Value 1 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client -Name Enabled -Value 0 -Type DWord"
}
ok: [localhost] => (item=None) => {
"msg": "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client -Name DisabledByDefault -Value 1 -Type DWord"
}
PLAY RECAP
localhost : ok=1 changed=0 unreachable=0 failed=0
Let’s create the actual task with win_regedit module to update registry.
- name: Update Windows Reg
win_regedit:
path: 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols{{item[0]}}{{item[1]}}'
name: '{{ item[2]["myname"] }}'
data: '{{ item.2.type.0.value }}'
type: dword
with_nested:
- '{{ protocol }}'
- '{{ srvtype }}'
- '{{ regname }}'
And here see my final playbook.
# Loop to get 3 protocols x 2 servertype x 2 Names&Value (12 combinations)
# We need 12 combinations but Name and Value are pairs, hence used a dictionary
- name: Loop to get 3 protocols x 2 servertype x 2 Names&Value (12 combinations)
hosts: winteldbservers
gather_facts: no
vars:
regname:
- myname: 'Enabled'
type:
- name: 'Enabled'
value: 0
- myname: 'DisabledByDefault'
type:
- name: 'DisabledByDefault'
value: 1
protocol:
- 'PCT 1.0'
- 'SSL 2.0'
- 'SSL 3.0'
srvtype:
- 'Server'
- 'Client'
tasks:
- name: check loop1
debug:
msg: 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols{{item[0]}}{{item[1]}} -Name {{ item[2]["myname"] }} -Value {{ item.2.type.0.value }} -Type DWord'
with_nested:
- '{{ protocol }}'
- '{{ srvtype }}'
- '{{ regname }}'
- name: Update Windows Reg
win_regedit:
path: 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols{{item[0]}}{{item[1]}}'
name: '{{ item[2]["myname"] }}'
data: '{{ item.2.type.0.value }}'
type: dword
with_nested:
- '{{ protocol }}'
- '{{ srvtype }}'
- '{{ regname }}'
You may move these variables to a variable file or create a role for the same for easy handling.
You may find the playbook here.