Helm values string to boolean - templates

new to helm and templating. I have a helm value file which needs to refer the value from another common values file.
enabled: "{{ .Values.common.service.enabled }}"
and the output i expect is
enabled: false
but i always get with the quotes
enabled: "false"
The common values file
service:
enabled : "false"
Have tried toYaml but still same
enabled: "{{ .Values.common.service.enabled | toYaml }}"
```
`

You're receving the quotes because you wrote the quote in the declaration of the component. Instead, the component definition yaml is:
enabled: {{ .Values.common.service.enabled }}
and the values.yaml:
common:
service:
enabled: false

Related

How to tell community.aws.elb_application_lb to leave untouched the DefaultActions of specific listener

Ansible version: 2.9.16
I have several application load balancers in the AWS account and I'm trying to add a new rule to certain listeners from these load balancers. I'm using the community.aws.elb_application_lb_info plugin to gather information about the load balancers and locate the target listeners where to add the new rule.
In the json returned by community.aws.elb_application_lb_info the default actions of some listeners look like this:
"default_actions":[
{
"target_group_arn":"some-arn",
"type":"forward",
"order":1,
"forward_config":{
"target_group_stickiness_config":{
"enabled":false
},
"target_groups":[
{
"target_group_arn":"some-arn",
"weight":1
}
]
}
}
]
while others look like this:
"default_actions":[
{
"redirect_config":{
"host":"some-host",
"protocol":"HTTPS",
"path":"/",
"status_code":"HTTP_302",
"query":"",
"port":"443"
},
"type":"redirect",
"order":1
}
]
Then I'm using community.aws.elb_application_lb plugin to add the rule to the listener.
Now the problem: the parameter listeners: - DefaultActions is mandatory in community.aws.elb_application_lb. I'm trying to specify it by extracting it from the json generated by community.aws.elb_application_lb_info, so my code looks like this:
# load_balancer_name var is defined somewhere else
- name: "Gather information about the following ALB: {{ load_balancer_name }}"
community.aws.elb_application_lb_info:
...
names: "{{ load_balancer_name }}"
register: elb_application_lb_info_ret
...
- name: Get the HTTPS:443 listener from ALB
vars:
listener_port: 443
listener_protocol: "HTTPS"
set_fact:
listener: "{{ elb_application_lb_info_ret.load_balancers[0].listeners | selectattr('protocol', 'equalto', listener_protocol) | selectattr('port', 'equalto', listener_port) | list | first }}"
...
- name: Create the listener rule in the load balancer
community.aws.elb_application_lb:
name: "{{ load_balancer_name }}"
.....
listeners:
- Protocol: "{{ listener.protocol }}"
Port: "{{ listener.port }}"
....
DefaultActions: "{{ listener.default_actions[0] }}"
Rules:
- Conditions:
....
but I get the following error:
TASK [xxx : Create the listener rule in the load balancer] *******************************************************************************************************************************************
fatal: [xxx]: FAILED! => {
"changed": false
}
MSG:
argument DefaultActions is of type <class 'dict'> found in 'listeners'. and we were unable to convert to list: <class 'dict'> cannot be converted to a list
If in the playbook above I change DefaultActions: "{{ listener.default_actions[0] }}" to DefaultActions: "{{ listener.default_actions }}" I get the following error:
TASK [xxx : Create the listener rule in the load balancer] *******************************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: KeyError: 'Type'
fatal: [xxx]: FAILED! => {
"changed": false,
"rc": 1
}
MSG:
MODULE FAILURE
See stdout/stderr for the exact error
MODULE_STDERR:
Traceback (most recent call last):
...
KeyError: 'Type'
So, looks like my approach is not good.
Is there any possibility to just tell community.aws.elb_application_lb to use the already defined DefaultActions for a listener to which I want to add a rule? All my load balancers already exist and have default actions defined. So I just want my script to add a rule and keep the default actions exactly as they are defined.
I guess it is possible to extract the data for each default action from json and recreate the default action each time, but the code would get pretty complicated.
So, I ended up re-creating DefaultActions. Not a nice solution but I couldn't come up with something better so far. But at least it works.
# code to get the target 'listener' via the community.aws.elb_application_lb_info plugin
...
# there are two types of default actions: redirect or forward
- name: Check if default action of listener is "redirect"
vars:
default_action: "{{ listener.default_actions[0] }}"
when: default_action.type == 'redirect'
set_fact:
default_actions:
- Type: "redirect"
RedirectConfig:
Protocol: "{{ default_action.redirect_config.protocol }}"
Port: "{{ default_action.redirect_config.port }}"
Host: "{{ default_action.redirect_config.host }}"
Path: "{{ default_action.redirect_config.path }}"
Query: "{{ default_action.redirect_config.query }}"
StatusCode: "{{ default_action.redirect_config.status_code }}"
- name: Check if default action of listener is "forward"
vars:
default_action: "{{ listener.default_actions[0] }}"
when: default_action.type == 'forward'
set_fact:
default_actions:
- Type: "forward"
TargetGroupArn: "{{ default_action.target_group_arn }}"
- name: Create/modify the listener rule in the load balancer
community.aws.elb_application_lb:
...
listeners:
- Protocol: "{{ listener.protocol }}"
...
DefaultActions: "{{ default_actions }}"
Rules:
...

Is there a way to deploy a file using Ansible copy and replace values from parameter store?

I would like to know if I can use Ansible template/copy and replace multiple values in a file(Ex: .properties,.xml, etc..) while deploying the file to destination with values from Parameter store?
Ex-file: app.properties
app.timeout.value=APP-TIMEOUT-VALUE
app.zone.value=APP-ZONE-VALUE
Ex-playbook: app.yaml
- name: Deploy app properties
template:
src: path/app_name.properties
dest: /../app_name.properties
notify:
- restart app
In the above example I want to replace values like(APP-TIMEOUT-VALUE, APP-ZONE-VALUE, etc..) with actual values stored in Parameter Store with Keys as(APP-TIMEOUT-VALUE, APP-ZONE-VALUE, etc..).
Someone please suggest me if there is a straight forward way for it without additional scripts.
Many Thanks
Given the file
shell> cat app.properties
app.timeout.value=APP_TIMEOUT_VALUE
app.zone.value=APP_ZONE_VALUE
Create the template
shell> cat app.properties.j2
app.timeout.value={{ APP_TIMEOUT_VALUE }}
app.zone.value={{ APP_ZONE_VALUE }}
If you can't create the template manually the block below would do it for you
- block:
- copy:
force: false
src: app.properties
dest: app.properties.j2
- lineinfile:
backrefs: true
path: app.properties.j2
regex: '^(.*)=\s*{{ item }}\s*$'
line: '\1={{ eval_open }}{{ item }}{{ eval_close }}'
loop:
- APP_TIMEOUT_VALUE
- APP_ZONE_VALUE
delegate_to: localhost
run_once: true
vars:
eval_open: "{{ '{{ ' }}"
eval_close: "{{ ' }}' }}"
Customize the paths and loop to your needs
Then, use the template. For example
- template:
src: app.properties.j2
dest: app_name.properties
vars:
APP_TIMEOUT_VALUE: 10
APP_ZONE_VALUE: 1
gives
shell> cat app_name.properties
app.timeout.value=10
app.zone.value=1
If you can't rename the names of the variables put them into a dictionary. There is no restriction for a key of a dictionary except for being unique. For example
shell> cat app.properties.j2
app.timeout.value={{ app_conf["APP-TIMEOUT-VALUE"] }}
app.zone.value={{ app_conf["APP-ZONE-VALUE"] }}
If you can't create the template manually the block below would do it for you
- block:
- copy:
force: false
src: app.properties
dest: app.properties.j2
- lineinfile:
backrefs: true
path: app.properties.j2
regex: '^(.*)=\s*{{ item }}\s*$'
line: '\1={{ eval_open }}app_conf["{{ item }}"]{{ eval_close }}'
loop:
- APP-TIMEOUT-VALUE
- APP-ZONE-VALUE
delegate_to: localhost
run_once: true
vars:
eval_open: "{{ '{{ ' }}"
eval_close: "{{ ' }}' }}"
Then, the template below will give the same result
- template:
src: app.properties.j2
dest: app_name.properties
vars:
app_conf:
APP-TIMEOUT-VALUE: 10
APP-ZONE-VALUE: 1

Assign list to a key within a chart

Deployment.yaml
...
env: {{ .Values.env}}
...
Values.yaml:
env:
- name: "DELFI_DB_USER"
value: "yyy"
- name: "DELFI_DB_PASSWORD"
value: "xxx"
- name: "DELFI_DB_CLASS"
value: "com.mysql.jdbc.Driver"
- name: "DELFI_DB_URL"
value: "jdbc:sqlserver://dockersqlserver:1433;databaseName=ddbeta;sendStringParametersAsUnicode=false"
feels like I'm missing something obvious.
linter says: ok
template says:
env: [map[name:DELFI_DB_USER value:yyy] map[name:DELFI_DB_PASSWORD
value:xxx] map[name:DELFI_DB_CLASS value:com.mysql.jdbc.Driver]
map[value:jdbc:mysql://dockersqlserver.{{ .Release.Namespace
}}.svc.cluster.local:3306/ddbeta\?\&amp\;useSSL=true\&amp\;requireSSL=false
name:DELFI_DB_URL]]
upgrade says:
Error: UPGRADE FAILED: YAML parse error on
xxx/templates/deployment.yaml: error converting YAML to JSON: yaml:
line 35: found unexpected ':'
solution:
env:
{{- range .Values.env }}
- name: {{ .name | quote }}
value: {{ .value | quote }}
{{- end }}
The current Go template expansion will give output which is not YAML:
env: {{ .Values.env}}
becomes:
env: env: [Some Go type stuff that isn't YAML]...
The Helm Go template needs to loop over the keys of the source YAML dictionary.
This is described in the Helm docs.
The correct Deployment.yaml is:
...
env:
{{- range .Values.env }}
- name: {{ .name | quote }}
value: {{ .value | quote }}
{{- end }}
...
Helm includes undocumented toYaml and toJson template functions; either will work here (because valid JSON is valid YAML). A shorter path could be
env: {{- .Values.env | toYaml | nindent 2 }}
Note that you need to be a little careful with the indentation, particularly if you're setting any additional environment variables that aren't in that list. In this example I've asked Helm to indent the YAML list two steps more, so additional environment values need to follow that too
env: {{- .Values.env | toYaml | nindent 2 }}
- name: OTHER_SERVICE_URL
value: "http://other-service.default.svc.cluster.local"

ansible ec2_instance_facts filter by "tag:Name" does not filter by instance Name

I want to run ec2_instance_facts to find an instance by name. However, I must be doing something wrong because I cannot get the filter to actually work. The following returns everything in my set AWS_REGION:
- ec2_instance_facts:
filters:
"tag:Name": "{{myname}}"
register: ec2_metadata
- debug: msg="{{ ec2_metadata.instances }}"
The answer is to use the ec2_remote_facts module, not the ec2_instance_facts module.
- ec2_remote_facts:
filters:
"tag:Name": "{{myname}}"
register: ec2_metadata
- debug: msg="{{ ec2_metadata.instances }}"
Based on the documentation ec2_remote_facts is marked as DEPRECATED from ansible version 2.8 in favor of using ec2_instance_facts.
This is working good for me:
- name: Get instances list
ec2_instance_facts:
region: "{{ region }}"
filters:
"tag:Name": "{{ myname }}"
register: ec2_list
- debug: msg="{{ ec2_metadata.instances }}"
Maybe the filte is not being applied? Can you go through the results in the object?

How to regex replace nested values in Ansible

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 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