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 16, 2018 in Ansible by Atul
• 10,240 points
874 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 16, 2018 by shubham
• 6,890 points

Related Questions In Ansible

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 in Ansible by Kennedy
209 views
0 votes
1 answer

How to ignore failed commands in Ansible?

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

answered Feb 7 in Ansible by Patt
1,411 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 19 in Ansible by Shalaka
121 views
0 votes
1 answer

How to use Ansible git module pull a branch with local changes?

You cannot achieve it using the git ...READ MORE

answered Jul 9, 2018 in Ansible by Atul
• 10,240 points
1,263 views
+13 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 26, 2018 in DevOps & Agile by DragonLord999
• 8,380 points
115 views
0 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
• 6,890 points
638 views
0 votes
1 answer