Ansible : Use regex replace for items in list and assign it back - ansible

I tried using regex_replace option this way:
Here groups[group_names[0]] is list of node names
"groups[group_names[0]]": [
"node1.in.labs.corp.netin",
"node2.in.labs.corp.netin"
]
- set_fact:
groups[group_names[0]]={{ groups[group_names[0]] |
map('regex_replace', _regex, _replace)|list }}
vars:
_regex: '^(.*?)\.(.*)$'
_replace: '-n \1'
Hitting the following error:
{"changed": false, "msg": "The variable name 'groups[group_names[0]]' is not valid. Variables must start with a letter or underscore character, and contain only letters, numbers and underscores."}
Can i assign back to same list ? after replacing the regex ?
Also -n option is using so that my expected output should be
-n node1 -n node2

Related

Ansible string manipulation in list [duplicate]

I am using [file lookup] which reads the whole file and stores the content in a variable. My play looks something like this:
- name: Store foo.xml contents in a variable
set_fact:
foo_content: "{{ lookup('file', 'foo.xml' ) | replace('\n', '')}}"
So the above code reads the foo.xml file and stores it in the variable, but the problem is when the foo.xml has line breaks in it, it also includes the line break in the variable.
My foo.xml is this file:
<?xml version="1.0" encoding="utf-8"?>
<initialize_param>
<secrets>
<my_secret id="99">3VMjII6Hw+pd1zHV5THSI712y421USUS8124487128745812sajfhsakjfasbfvcasvnjasjkvbhasdfasgfsfaj5G8A9+n8CkLxk7Dqu0G8Jclg0eb1A5xeFzR3rrJHrb2GBBa7PJNVx8tFJP3AtF6ek/F/WvlBIs2leX2fq+/bGryKlySuFmbcwBsThmPJC5Z5AwPJgGZx</my_secret>
</secrets>
</initialize_param>
The output removes line break \n but also incudes the tabs \r & \t
I need to got rid of the \n , need to get rid of extra formatting too (\r & \t), Moreover after the replace filter I get the error while firing a DB Update query as
stderr: /bin/sh: 1: cannot open ?xml: No such file
Use the Jinja trim filter:
"{{ lookup('file', 'foo.xml' ) | trim }}"
You can do that with the replace filter?
contents: "{{ lookup('file', '/etc/foo.txt') | replace('\n', '')}}"
You may use the regex_replace filter since the trim doesn't clear other line break characters as you mentioned in the question.
"{{ some_stdout_to_clear | regex_replace('[\\r\\n\\t]+','') }}"

Ansible - define variable inside groups

Folks,
I'm trying to achieve the below by using the groups parameter in Ansible.
{{ groups['kafka'] | map('extract',hostvars,'ansible_host') | list | join(':{{ zookeeper_port }},') + ':{{ zookeeper_port }}' }}
variable zookeeper_port is given as:
zookeeper_port: 2181
While running my playbook with the above parameter for groups included, it gives me the result as:
IP:{{ zookeeper_port }},IP:{{ zookeeper_port }}
instead I'm trying to get the result as :
IP:2181,IP:2181
Can anyone help me figuring out what went wrong at my groups parameter declaration.
Anything inside '' inside {{}} treated as literal string. To access variables you type them right away - without any surrounding characters. In your case you need something like:
{{ groups['kafka'] | map('extract',hostvars,'ansible_host') | list | join(':' + zookeeper_port|string + ',') + ':' + zookeeper_port|string }}"
Notice the string filter - if your zookeeper_port defined as integer, then without it the task will fail

Eliminate Unwanted Characters in Ansible print

I store information in a varible HOST in ansible via register command. Then I am trying to print the contents(INFORMATION-1 to 3) of variable HOST line after line.
INFORMATION-1
INFORMATION-2
INFORMATION-3
Instead I am getting this result when printed.
[[u'INFORMATION-1'], [u'INFORMATION-2'], [u'INFORMATION-3']]
Any ideas how I eliminate those unwanted characters like brackets [ ], u and apostrophe(') and print the result in my desired format?
Any ideas how I eliminate those unwanted characters like brackets [ ], u and apostrophe(') and print the result in my desired format?
It's because whatever thing you are printing is actually a python list containing 3 python lists which themselves contain a unicode str
If you want them to be line delimited, then the join() filter will do that for you, and it should be safe to use a join for the inner lists, too, in case you ever end up with more than one value in the inner list:
# assuming your values are in a variable named "list_list_str"
- debug: var=the_output
vars:
the_output: '{{ list_list_str | map("join", "\n") | join("\n") }}'

Probable quoting issue using the stdin argument of ansible's shell module

I've got a playbook with the following tasks:
- set_fact:
asg_filter: >
.AutoScalingGroups[] |
select(.Tags[] | select(.Key == "Role").Value == "myrole")
- shell: aws autoscaling --region us-west-2 describe-auto-scaling-groups | jq --compact-output "{{ asg_filter }}"
register: asgs_result
- set_fact:
stale_instance_filter: >
.LaunchConfigurationName as $lc |
.Instances[] |
select(.LaunchConfigurationName != $lc) |
.InstanceId
Now I want to use stale_instance_filter on asgs_result.stdout. The following works:
- shell: echo '{{ asgs_result.stdout }}' | jq -r '{{ stale_instance_filter }}'
But this doesn't:
- shell: jq -r '{{ stale_instance_filter }}'
args:
stdin: "{{ asgs_result.stdout }}"
I get the following error message: parse error: Invalid numeric literal at line 1, column 23 (which I believe is from the account number in the ARN for the ASG.) I think it's a quoting issue (maybe something about the double quotes in the JSON), but I've also tried asgs_result.stdout | quote to no avail. I also tried the command module; it didn't help either. Of course this all works if I do it directly on the CLI.
I realize I could combine the two jq filters but I want to reuse asgs_result for other things and don't want to have to make the query multiple times. How can I fix this so I can use the stdin argument?
Edit: I was asked to provide an example of the value of asgs_result, well here you go, here's the stdout attribute in it (since I don't use anything else):
"stdout": "{\"AutoScalingGroupARN\":\"arn:aws:autoscaling:us-east-2:123456:autoScalingGroup:e75a213b-75fe-467c-8cf5-d7c51f76c471:autoScalingGroupName/myrole-dev\",\"TargetGroupARNs\":[],\"SuspendedProcesses\":[],\"DesiredCapacity\":4,\"Tags\":[{\"ResourceType\":\"auto-scaling-group\",\"ResourceId\":\"myrole-dev\",\"PropagateAtLaunch\":true,\"Value\":\"dev\",\"Key\":\"Dimension\"},{\"ResourceType\":\"auto-scaling-group\",\"ResouJceId\":\"myrole-dev\",\"PropagateAtLaunch\":true,\"Value\":\"true\",\"Key\":\"Monitored\"},{\"ResourceType\":\"auto-scaling-group\",\"ResourceId\":\"myrole-dev\",\"PropagateAtLaunch\":true,\"Value\":\"myrole\",\"Key\":\"Name\"},{\"ResourceType\":\"auto-scaling-group\",\"ResourceId\":\"myrole-dev\",\"PropagateAtLaunch\":true,\"Value\":\"myrole\",\"Key\":\"Role\"},{\"ResourceType\":\"auto-scaling-group\",\"ResourceId\":\"myrole-dev\",\"PropagateAtLaunch\":true,\"Value\":\"2035-09-30 18:55:31 +0000\",\"Key\":\"cleaner-destroy-after\"},{\"ResourceType\":\"auto-scaling-group\",\"ResourceId\":\"myrole-dev\",\"PropagateAtLaunch\":true,\"Value\":\"vpce-2c23ca45\",\"Key\":\"force_s3_endpoint_dependency\"},{\"ResourceType\":\"auto-scaling-group\",\"ResourceId\":\"myrole-dev\",\"PropagateAtLaunch\":true,\"Value\":\"owned\",\"Key\":\"kubernetes.io/cluster/dev\"}],\"EnabledMetrics\":[],\"LoadBalancerNames\":[],\"AutoScalingGroupName\":\"myrole-dev\",\"DefaultCooldown\":300,\"MinSize\":4,\"Instances\":[{\"ProtectedFromScaleIn\":false,\"AvailabilityZone\":\"us-east-2b\",\"InstanceId\":\"i-0141fd35e3cf3ad0a\",\"HealthStatus\":\"Healthy\",\"LifecycleState\":\"InService\",\"LaunchConfigurationName\":\"dev_myrole_20180511171410107500000002\"},{\"ProtectedFromScaleIn\":false,\"AvailabilityZone\":\"us-east-2c\",\"InstanceId\":\"i-01aec2b3546d75190\",\"HealthStatus\":\"Healthy\",\"LifecycleState\":\"InService\",\"LaunchConfigurationName\":\"dev_myrole_20180511171410107500000002\"},{\"ProtectedFromScaleIn\":false,\"AvailabilityZone\":\"us-east-2a\",\"InstanceId\":\"i-0830b227f034d2859\",\"HealthStatus\":\"Healthy\",\"LifecycleState\":\"InService\",\"LaunchConfigurationName\":\"dev_myrole_20180511171410107500000002\"},{\"ProtectedFromScaleIn\":false,\"AvailabilityZone\":\"us-east-2b\",\"InstanceId\":\"i-0f7d847e8c168040b\",\"HealthStatus\":\"Healthy\",\"LifecycleState\":\"InService\",\"LaunchConfigurationName\":\"dev_myrole_20180511171410107500000002\"}],\"MaxSize\":4,\"VPCZoneIdentifier\":\"subnet-c348988e,subnet-79743210,subnet-156ee36e\",\"HealthCheckGracePeriod\":300,\"TerminationPolicies\":[\"Default\"],\"LaunchConfigurationName\":\"dev_myrole_20180511171410107500000002\",\"CreatedTime\":\"2018-02-20T22:35:32.183Z\",\"AvailabilityZones\":[\"us-east-2a\",\"us-east-2b\",\"us-east-2c\"],\"HealthCheckType\":\"EC2\",\"NewInstancesProtectedFromScaleIn\":false}"
Sorry that it is all on one line but I don't want to make anyone think there is a newline in there, because there isn't.
The JSON content seems to be interpreted before sent to the stdin, so looks like simple quotes are sent (seen in verbose mode with -vvv):
"stdin": "{'AutoScalingGroupARN': 'arn:aws:autoscaling:us-east-2:123456:autoScalin
gGroup:e75a213b-75fe-467c-8cf5-d7c51f76c471:autoScalingGroupName/myrole-dev', ...,
'AvailabilityZones': ['us-east-2a', 'us-east-2b', 'us-east-2c']}"
Which is not JSON valid:
$ echo "{'AutoScalingGroupARN': 'arn:aws:autoscaling:us-east-2:123456:autoScalingGroup:e75a213b-75fe-467c-8cf5-d7c51f76c471:autoScalingGroupName/myrole-dev', 'HealthCheckGracePeriod': 300}" | jq
parse error: Invalid numeric literal at line 1, column 23
$ echo '{"AutoScalingGroupARN": "arn:aws:autoscaling:us-east-2:123456:autoScalingGroup:e75a213b-75fe-467c-8cf5-d7c51f76c471:autoScalingGroupName/myrole-dev", "HealthCheckGracePeriod": 300}' | jq
{
"AutoScalingGroupARN": "arn:aws:autoscaling:us-east-2:123456:autoScalingGroup:e75a213b-75fe-467c-8cf5-d7c51f76c471:autoScalingGroupName/myrole-dev",
"HealthCheckGracePeriod": 300
}
So, you need to "escape" it.
Unfortunately, the to_json filter, escape to much:
"stdin": "\"{\\\"AutoScalingGroupARN\\\":\\\"arn:aws:autosca...
But the string filter fits perfectly:
"stdin": "{\"AutoScalingGroupARN\":\"arn:aws:autosca...
So, the correct way with stdin is this:
- shell: jq -r '{{ stale_instance_filter }}'
args:
stdin: "{{ asgs_result.stdout | string }}"

Ansible: insert a single word on an existing line in a file

I have to use Ansible modules in order to edit the /etc/ssh/sshd_config file - every time I create a new user I want to append it at these two lines:
AllowUsers root osadmin <new_user>
AllowGroups root staff <new_group>
At this moment I'm using the shell module to execute a sed command but would like to use lineinfile, if possible
- shell: "sed -i '/^Allow/ s/$/ {{ user_name }}/' /etc/ssh/sshd_config"
Any suggestions would be sincerely appreciated.
The replace module will replace all instances of a regular expression pattern within a file. Write a task to match the AllowUsers line and replace it with the original line appended with the user name. To ensure the task is idempotent, a negative lookahead assertion in the regular expression checks if the user name already appears in the line. For example:
- name: Add user to AllowUsers
replace:
backup: yes
dest: /etc/ssh/sshd_config
regexp: '^(AllowUsers(?!.*\b{{ user_name }}\b).*)$'
replace: '\1 {{ user_name }}'
You could do it in a single play with a newline, but I think it's cleaner to use two lineinfile plays for this.
- hosts: '127.0.0.1'
vars:
usernames:
- larry
- curly
- moe
usergroups:
- stooges
- admins
tasks:
- lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^AllowUsers'
line: "AllowUsers {{usernames | join(' ')}}"
- lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^AllowGroups'
line: "AllowGroups {{usergroups | join(' ')}}"
Note that groups is a reserved word so don't use that as a variable name.
The selected answer assumes that the complete list of users is available at runtime, while the most popular answer can fail when there is a dash in the username, because \b interprets it as a word boundary. The following solution assumes that the playbook cannot regenerate the complete list of usernames from scratch, and tries and handle the corner case of dashes:
name: add a user to the list of AllowUsers if not present
lineinfile:
path: /etc/ssh/sshd_config
backrefs: yes
backup: yes
regexp: "^AllowUsers((?:(?:\s+\S+(?!\S))(?<!\s{{ username }}))+\s*?)(\n?)$"
line: "AllowUsers\1 {{ username }}\2"
validate: /usr/sbin/sshd -t -f %s
As a bonus, I threw in sshd_config backup and verification.
How the (interesting part of the) regular expression works:
--------------------------+----------------------------------------------------
( |
--------------------------+----------------------------------------------------
(?: | This group is not captured
--------------------------+----------------------------------------------------
(?:\s+\S+(?!\S)) | Matches any sequence of whitespace characters fol-
| lowed by any sequence of non-whitespace characters,
| that is to say a leading space and a username. The
| negative look-ahead at the end prevents a "catast-
| rophic backtracking". Also, this group is not cap-
| tured.
--------------------------+----------------------------------------------------
(?<!\s{{ username }}) | Applies a negative look-behind on the username, so
| that if the username found by the previous expres-
| sion matches, the regular expression fails. The
| match on a leading whitespace character ensures
| that the comparison is made on the complete string.
--------------------------+----------------------------------------------------
)+ | Groups the detection of a username and its negative
| look-behind together. The "+" quantifier is used
| here on the assumption that the file already cont-
| ains at least one username, but "*" could be used
| for a more relaxed matching.
--------------------------+----------------------------------------------------
\s*? | Matches any trailing whitespace. The match is lazy
| in order to detect the newline character later on.
--------------------------+----------------------------------------------------
) | Captures the whole text after "AllowUsers" (this
| will be \1).
--------------------------+----------------------------------------------------
(\n?) | Captures either a newline character or an empty
| string (this will be \2).
--------------------------+----------------------------------------------------
If the regular expression matches, it means that the line exists and that it does not contain {{ username }}, so we append it.
If the regular expression does not match, it means that either the line does not exist or that it contains {{ username }}, and we do nothing.
I had the same problem. I needed add user to sudoers group, let's say 'testuser' to line:
User_Alias SOMEADMIN = smoeuser1, someuser2, someuser3
This worked well for me:
- name: add testuser to end of line
lineinfile:
dest: /etc/sudoers.d/somegroup
state: present
regexp: '^(User_Alias(.*)$)'
backrefs: yes
line: '\1, testuser'
The point is that if I had '^User_Alias(..)$'* in regexp and not '^(User_Alias(..)$)'* it didn't work and whole line was replaced. With () arround searched text the result was OK:
User_Alias SOMEADMIN = smoeuser1, someuser2, someuser3, testuser
So then anything can work in line:, included ansible variables like "{{ usernames | join(', ') }}"
This worked for me
- name: Add Group to AllowGroups
lineinfile:
dest=/etc/ssh/sshd_config
backup=True
backrefs=True
state=present
regexp='^(AllowGroups(?!.*\b{{ groupname }}\b).*)$'
line='\1 {{ groupname }}'

Resources