How to regex replace nested values in Ansible

0 votes

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.

Can anyone help me with this?

Jun 17, 2018 in Ansible by Atul
• 10,240 points
5,098 views

1 answer to this question.

0 votes

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 like result.results[].ansible_facts.services[] to select all services across all clusters and map('regex_replace',... this list.

Or iterate over complex object and apply modification inside a loop, for example:

- name: "Turn services off"
  ecs_service:
    name: "{{ myname }}"
    desired_count: 0
    task_definition: "{{ taskdefinitions[myname] }}"
    cluster: "{{ mycluster }}"
    state: present
  vars:
    mycluster: "{{ item[0].item }}"
    myname: "{{ item[1] | regex_search('[^/]*$') }}"
  with_subelements:
    - "{{ result.results }}"
    - ansible_facts.services
 I hope this would be helpful to you. 
answered Jun 17, 2018 by shubham
• 7,340 points

Related Questions In Ansible

0 votes
1 answer

How to replace a line using lineinfile module in Ansible?

Hi@MD, You can use lineinfile module in Ansible. ...READ MORE

answered Sep 2, 2020 in Ansible by akhtar
• 38,230 points
2,449 views
0 votes
1 answer

How to configure fact caching in Ansible?

There are two cache plugins: redis and jsonfile. To ...READ MORE

answered Jan 24, 2019 in Ansible by Kennedy
3,727 views
0 votes
1 answer

How to ignore failed commands in Ansible?

Usually, if even one command fails to ...READ MORE

answered Feb 7, 2019 in Ansible by Patt
15,370 views
0 votes
2 answers

How to enable Inventory Cache plugin in Ansible?

Adding to @Farookh's answer, if the inventory ...READ MORE

answered Feb 20, 2019 in Ansible by Shalaka
2,024 views
+15 votes
2 answers

Git management technique when there are multiple customers and need multiple customization?

Consider this - In 'extended' Git-Flow, (Git-Multi-Flow, ...READ MORE

answered Mar 27, 2018 in DevOps & Agile by DragonLord999
• 8,450 points
3,460 views
+2 votes
1 answer
0 votes
1 answer

How do I execute a shell script and then use the result in ansible

This can work out for you: - name: ...READ MORE

answered Apr 11, 2018 in Ansible by shubham
• 7,340 points
6,479 views
0 votes
1 answer
webinar REGISTER FOR FREE WEBINAR X
REGISTER NOW
webinar_success Thank you for registering Join Edureka Meetup community for 100+ Free Webinars each month JOIN MEETUP GROUP