This question is about looping in Ansible, not about AWS, but for the sake of clarity I will use an AWS deployment problem as an example.
For our deployment scripts I am trying to loop over some clusters in the Amazon EC2 container service. What I will ultimately do is restart each service on the cluster. I am able to restart a service, given it's name. However I need the simple name, not the fully qualified ARN. So I look up the services per cluster and get something like this:
results:
- _ansible_item_result: true
_ansible_no_log: false
_ansible_parsed: true
ansible_facts:
services:
- arn:aws:ecs:eu-central-1:55:service/test-services
changed: false
failed: false
invocation:
module_args:
aws_access_key: null
aws_secret_key: null
cluster: services
details: false
ec2_url: null
profile: null
region: null
security_token: null
service: null
validate_certs: true
item: services
- _ansible_item_result: true
_ansible_no_log: false
_ansible_parsed: true
ansible_facts:
services:
- arn:aws:ecs:eu-central-1:55:service/test-service
- arn:aws:ecs:eu-central-1:55:service/frontend
- arn:aws:ecs:eu-central-1:55:service/beats
changed: false
failed: false
invocation:
module_args:
aws_access_key: null
aws_secret_key: null
cluster: test-service
details: false
ec2_url: null
profile: null
region: null
security_token: null
service: null
validate_certs: true
item: test-service module_args:
aws_access_key: null
aws_secret_key: null
cluster: test-service
details: false
ec2_url: null
profile: null
region: null
security_token: null
service: null
validate_certs: true
item: test-service
Now I want to replace each ARN by the short name of the service. For example: arn:aws:ecs:eu-central-1:55:service/test-service
becomes test-service
.
After the replacement I can do loop over the services and turn them off by setting the desired count to 0 (later I will turn them back on again):
- name: "Turn services off"
ecs_service:
name: "{{ item[1]}}"
desired_count: 0
task_definition: "{{ taskdefinitions[item[1]] }}"
cluster: "{{ item[0].item }}"
state: present
with_subelements:
- "{{ result.results }}"
- ansible_facts.services
register: turnOffServiceResult
Where taskdefinitions
is a simple dict I defined in the playbook:
taskdefinitions:
services:
- test-services
test-xde-worker-service:
- test-service
So after I get the AWS list shown above into a variable result
I try to regex replace by doing the following:
- set_fact:
result:
results:
ansible_facts:
services: "{{ result.results.1.ansible_facts.services | map('regex_replace', '.*/(.*?)$', '\\1' ) | list }}"
This works fine, but it obviously only replaces the service names for one cluster and I lose any other fields in the dict ansible_facts
. The latter is acceptable, the former not. So here is the question: how can I replace text in a nested list? Another problem would be to skip turning off the services that are not included in taskdefinitions
, but that is not the matter at hand.
I'm not aware of any built-in method to modify arbitrary items in complex objects in-place (at least in current Ansible 2.3).
You either select required items from original object (with
select
,map(attribute=...)
,json_query
, etc) and then modify items in that reduced set/list. In your hypothetical example with JMESPath likeresult.results[].ansible_facts.services[]
to select all services across all clusters andmap('regex_replace',...
this list.Or iterate over complex object and apply modification inside a loop, for example: