I am currently using a k8s lookup to search for resources with a certain tag attached to them (in this case branch). This branch is a variable that changes regularly. The problem is that I can't seem to find the correct syntax for adding a variable into the lookup since it is itself using the Jinja syntax.
This works:
- name: delete the replicaset
k8s:
state: absent
api_version: v1
kind: ReplicaSet
namespace: default
name: "{{ replicaset.metadata.name }}"
kubeconfig: /var/lib/awx/.kube/config
vars:
replicaset: "{{ lookup('k8s', kind='ReplicaSet', namespace='default', label_selector='branch=testing' ) }}"
However, when trying to use the branch variable, nothing I try seems to work. Here is one example of not working:
- name: delete the replicaset
k8s:
state: absent
api_version: v1
kind: ReplicaSet
namespace: default
name: "{{ replicaset.metadata.name }}"
kubeconfig: /var/lib/awx/.kube/config
vars:
replicaset: "{{ lookup('k8s', kind='ReplicaSet', namespace='default', label_selector='branch={{ branch }}' ) }}"
You can either add a helper variable:
- name: delete the replicaset
k8s:
state: absent
api_version: v1
kind: ReplicaSet
namespace: default
name: "{{ replicaset.metadata.name }}"
kubeconfig: /var/lib/awx/.kube/config
vars:
replicaset: "{{ lookup('k8s', kind='ReplicaSet', namespace='default', label_selector=my_selector ) }}"
my_selector: branch={{ branch }}
or use a Jinja2 string concatenation:
replicaset: "{{ lookup('k8s', kind='ReplicaSet', namespace='default', label_selector='branch='+branch ) }}"
Related
Note: I have next to zero experience with Ansible.
I need to be able to conditionally modify the configuration of a Kubernetes cluster control plane service. To do this I need to be able to find a specific piece of information in the file and if its value matches a specific pattern, change the value to something else.
To illustrate, consider the following YAML file:
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=127.0.0.1
...
In this scenario, the line I'm interested in is the line containing --bind-address. If that field's value is "127.0.0.1", it needs to be changed to "0.0.0.0". If it's already "0.0.0.0", nothing needs to be done. (I could also approach it from the point of view of: if its not "0.0.0.0" then it needs to change to that.)
The initial thought that comes to mind is: just search for "--bind-address=127.0.0.1" and replace it with "--bind-address=0.0.0.0". Simple enough, eh? No, not that simple. What if, for some reason, there is another piece of configuration in this file that also matches that pattern? Which one is the right one?
The only way I can think of to ensure I find the right text to change, is a multiple expression RegEx match. Something along the lines of:
find spec:
if found, find containers: "within" or "under" spec:
if found, find - command: "within" or "under" containers: (Note: there can be more than one "command")
if found, find - kube-controller-manager "within" or "under" - command:
if found, find - --bind-address "within" or "under" - kube-controller-manager
if found, get the value after the =
if 127.0.0.1 change it to 0.0.0.0, otherwise do nothing
How could I write an Ansible playbook to perform these steps, in sequence and only if each step returns true?
Read the data from the file into a dictionary
- include_vars:
file: conf.yml
name: conf
gives
conf:
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=127.0.0.1
Update the containers
- set_fact:
containers: []
- set_fact:
containers: "{{ containers +
(update_candidate is all)|ternary([_item], [item]) }}"
loop: "{{ conf.spec.containers|d([]) }}"
vars:
update_candidate:
- item is contains 'command'
- item.command is contains 'kube-controller-manager'
- item.command|select('match', '--bind-address')|length > 0
update: "{{ item.command|map('regex_replace',
'--bind-address=127.0.0.1',
'--bind-address=0.0.0.0') }}"
_item: "{{ item|combine({'command': update}) }}"
gives
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
Update conf
conf_update: "{{ conf|combine({'spec': spec}) }}"
spec: "{{ conf.spec|combine({'containers': containers}) }}"
give
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
conf_update:
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
Write the update to the file
- copy:
dest: /tmp/conf.yml
content: |
{{ conf_update|to_nice_yaml(indent=2) }}
gives
shell> cat /tmp/conf.yml
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
Example of a complete playbook for testing
- hosts: localhost
vars:
conf_update: "{{ conf|combine({'spec': spec}) }}"
spec: "{{ conf.spec|combine({'containers': containers}) }}"
tasks:
- include_vars:
file: conf.yml
name: conf
- debug:
var: conf
- set_fact:
containers: []
- set_fact:
containers: "{{ containers +
(update_candidate is all)|ternary([_item], [item]) }}"
loop: "{{ conf.spec.containers|d([]) }}"
vars:
update_candidate:
- item is contains 'command'
- item.command is contains 'kube-controller-manager'
- item.command|select('match', '--bind-address')|length > 0
update: "{{ item.command|map('regex_replace',
'--bind-address=127.0.0.1',
'--bind-address=0.0.0.0') }}"
_item: "{{ item|combine({'command': update}) }}"
- debug:
var: containers
- debug:
var: spec
- debug:
var: conf_update
- copy:
dest: /tmp/conf.yml
content: |
{{ conf_update|to_nice_yaml(indent=2) }}
I'm trying to use Ansible to deploy to two regions in AWS, I have it working with one region at the moment. The issue is I want to only execute my primary_region or my secondary_region depending on the parameter from the template it's using.
My main.yaml:
- hosts: primary_region
name: Create ECR
tasks:
- name: "Create ECR Repository"
cloudformation:
state: "{{ state }}"
stack_name: "{{ stack_create_ecr.name }}"
profile: "{{ aws_profile }}"
region: "{{ inventory_hostname }}"
template_url: "{{ stack_create_ecr.template_url }}"
template_parameters:
"ansibleFile"
tags:
"{{ stack_create_ecr.tags }}"
tags:
- stack_create_ecr
when: stack_create_ecr.region == "primary_region" <-- This
- hosts: secondary_region
name: Create ECR
tasks:
- name: "Create ECR Repository"
cloudformation:
state: "{{ state }}"
stack_name: "{{ stack_create_ecr.name }}"
profile: "{{ aws_profile }}"
region: "{{ inventory_hostname }}"
template_url: "{{ stack_create_ecr.template_url }}"
template_parameters:
"ansibleFile"
tags:
"{{ stack_create_ecr.tags }}"
tags:
- stack_create_ecr
when: stack_create_ecr.region == "secondary_region" <-- This
The template that I am using
stack_create_ecr.yaml:
stack_create_ecr:
name: cloudFormationTemplateNameOmitted
template_url: S3BucketUrl
parameters:
RepoName: EcrRepoName
DevName: cloud-dev
tags:
ansible_playbook: "{{ ansible_playbook_tag }}"
region: primary_region <-- This is what I'm trying to use
Everytime I try to run my playbook I just get msg: 'argument template_parameters is of type <class ''str''> and we were unable to convert to dict: dictionary requested, could not parse JSON or key=value'
I have tried all sorts of things from putting quotes around the string in the when condition. Nothing seems to work, what am I doing wrong?
It seems you massed up with template_parameters prop. It should be a map but you provided a string:
template_parameters:
PropName: "ansibleFile"
I have tried every combination I can conceive of to specify to deploy a google compute instance into a particular subnet (subnetX) in network (networkY).
- name: create a instance
google.cloud.gcp_compute_instance:
name: test_object
machine_type: n1-standard-1
disks:
- auto_delete: 'true'
boot: 'true'
source: "{{ disk }}"
- auto_delete: 'true'
interface: NVME
type: SCRATCH
initialize_params:
disk_type: local-ssd
labels:
environment: production
network_interfaces: # <<< does not work. API request is made without a network_interface
- network:
selfLink: "https://blah/blah/blah/networkY"
subnetwork:
selfLink: "https://blah/blah/blah/subnetworkX"
zone: us-central1-a
project: test_project
auth_kind: serviceaccount
service_account_file: "/tmp/auth.pem"
state: present
When using subnetwork you should not specify a network.
To be more precise this is the workaround for this problem.
---
- name: create a network
gcp_compute_network:
name: ansible-network
auto_create_subnetworks: yes
project: "{{ lookup('env','GCP_PROJECT') }}"
state: present
register: network
- name: Get Network URL
set_fact:
network_url: "{{ network | json_query(jmesquery) }}"
vars:
jmesquery: "{selfLink: selfLink}"
- name: create a firewall
gcp_compute_firewall:
name: ansible-firewall
network: "{{ network_url }}"
allowed:
- ip_protocol: tcp
ports: ['80','22']
target_tags:
- apache-http-server
source_ranges: ['0.0.0.0/0']
project: "{{ lookup('env','GCP_PROJECT') }}"
state: present
register: firewall
I have built a playbook to build a virtual server in F5. I want to make a line only execute if someone enters the variable. In this case the default_persistence_profile: line has a variable "{{ persistenceProfile }}". Sometimes the developers don't want persistence applied to their app but sometimes they do. I have found when I make the variable optional in the run task and don't select a persistence profile the task errors out. See playbook below:
- name: Build the Virtual Server
bigip_virtual_server:
state: present
partition: Common
name: "{{ vsName }}"
destination: "{{ vsIpAddress }}"
port: "{{ vsPort }}"
pool: "{{ poolName }}"
default_persistence_profile: "{{ persistenceProfile }}"
ip_protocol: tcp
snat: automap
description: "{{ vsDescription }}"
profiles:
- tcp
- http
- name: "{{ clientsslName }}"
context: client-side
- name: default-server-ssl
context: server-side
Ansible has a mechanism for omitting parameters using the default filter, like this:
- name: Build the Virtual Server
bigip_virtual_server:
state: present
partition: Common
name: "{{ vsName }}"
destination: "{{ vsIpAddress }}"
port: "{{ vsPort }}"
pool: "{{ poolName }}"
default_persistence_profile: "{{ persistenceProfile|default(omit) }}"
ip_protocol: tcp
snat: automap
description: "{{ vsDescription }}"
profiles:
- tcp
- http
- name: "{{ clientsslName }}"
context: client-side
- name: default-server-ssl
context: server-side
If persistenceProfile is unset, the default_persistence_profile parameter should not be passed to the bigip_virtual_server module.
getting "Unsupported parameters for (ecs_taskdefinition) module: cpu,launch_type,memory Supported parameters include: arn,aws_access_key,aws_secret_key,containers,ec2_url,family,network_mode,profile,region,revision,security_token,state,task_role_arn,validate_certs,volumes"}
ecs_taskdefinition:
family: "{{ taskfamily_name }}"
# task_role_arn: "{{ ecs_role.arn }}"
# execution_role_arn: "{{ ecs_role.arn }}"
containers:
- name: "{{ container_name }}"
essential: true
image: "{{ image_var }}"
portMappings:
- containerPort: "{{ container_port }}"
hostPort: "{{ container_port }}"
environment:
- name: xyz_ENV
value: "{{ env }}"
launch_type: FARGATE
cpu: "{{ cpu_var }}"
memory: "{{ memory_var }}"
state: present
network_mode: awsvpc ```
cpu,launch_type,memory
Those parameters were added in ansible 2.7
You have to upgrade your ansible version or drop those params.
Ref: https://docs.ansible.com/ansible/latest/modules/ecs_taskdefinition_module.html