I have boolean parameter input from Jenkins. My Ansible then reads this boolean as 'when' condition to execute the tasks. However, this boolean parameter seems not working properly. My playbook looks like this:
- hosts: localhost
var:
my_boolean: {{lookup('env','boolean_parameter1')}}
- name: print msg1
debug:
msg: "msg1 is {{lookup('env','boolean_parameter1')}}."
- name: print msg2
debug:
msg: "msg2 is {{my_boolean}}."
- name: execute tasks if my_boolean is 'true'
shell: ls -l
when:
- "{{my_boolean}}"=="true"
Output:
msg1 is true.
msg2 is .
<<<<execution of tasks skipped>>>>
Why does msg2 output my_boolean as blank? Should this be 'true' as well?
In your play, the comparison in when: is matching the string "true". Whereas the variable my_boolean is a boolean, so it doesn't match and skips the task. And in the debug: task it is the other way around - the message is trying to display Boolean true, and so it is blank.
The below tasks should do the job:
# Here we need the string equivalent of boolean, i.e. the text 'true'
- name: print msg2
debug:
msg: "msg2 is {{ my_boolean | string }}."
# But here we need to compare the boolean value
- name: execute tasks if my_boolean is 'true'
shell: ls -l
when: my_boolean
It is worth noting that as of Ansible 2.10 onwards referencing bare variables in when: conditionals will be deprecated. It would be a good idea to use: when: my_boolean | bool.
Try casting my_boolean to bool during initialization. This allows to just check if value of my_boolean is True.
Playing with your code, the following worked for me:
- name: Test
hosts: all
vars:
my_boolean: "{{lookup('env','boolean_parameter1')|bool}}"
tasks:
- name: print msg1
debug:
msg: "msg1 is {{lookup('env','boolean_parameter1')}}."
- name: print msg2
debug:
msg: "msg2 is {{my_boolean}}."
- name: execute tasks if my_boolean is 'true'
shell: ls -l
when:
- my_boolean
Inventory:
[hosts]
localhost ansible_connection=local
Related
Is there is the way to pass variable with "map type" ?
For example is it possible to do something like below:
jobs:
docker_build_and_push:
uses: test/test/.github/workflows/matrixDocker.yml#main
with:
# Docker image name
service_name: nesso
tags: {"nginx":"new", "api":"old"} <<<<<< ????
Unfortunately, this is not possible yet. As I wrote in a previous answer, the only supported input types for actions are string | number | boolean (schema: with ref: definitions/env).
So the workaround would be either you pass multiple arguments to your action, or you pass them as a JSON string then you parse it with jq (if your action uses shell/bash).
And here are these example workarounds:
Specify more options as inputs to your action
Simply add more inputs to your action instead of one input of type map:
name: 'Hello World'
description: 'Greet someone'
inputs:
greeting-word:
description: What to say
required: false
default: Hello
who-to-greet:
description: 'Who to greet'
required: true
something-else:
description: Something else to say
required: false
default: ""
runs:
using: "composite"
steps:
- name: Greet!
shell: bash
run: echo "${{ inputs.greeting-word }} ${{ inputs.who-to-greet }} ${{ inputs.something-else }}"
Then just pass them form your workflow file
- name: Greet
uses: ./.github/actions/my-action
with:
greeting-word: Hallo
who-to-greet: Bob
Pass arguments as a JSON string
Workflow file:
- name: Greet
uses: ./.github/actions/my-action
with:
greeting-args: '{"greeting-word": "Hello", "who-to-greet": "Bob"}'
The action
name: 'Hello World'
description: 'Greet someone'
inputs:
greeting-args:
required: true
description: Greeting arguments
runs:
using: "composite"
steps:
- name: Greet!
shell: bash
run: |
MY_INPUT='${{ inputs.greeting-args }}'
GREETING_WORD=$(echo $MY_INPUT | jq -r '."greeting-word"')
WHO_TO_GREET=$(echo $MY_INPUT | jq -r '."who-to-greet"')
echo "${GREETING_WORD} ${WHO_TO_GREET}"
Or you can pass it as a multi-line string
This approach is used by actions like actions/upload-artifact
- uses: actions/upload-artifact#v3
with:
name: my-artifact
path: |
path/output/bin/
path/output/test-results
!path/**/*.tmp
And google-github-actions/get-secretmanager-secrets
- id: 'secrets'
uses: 'google-github-actions/get-secretmanager-secrets#v1'
with:
secrets: |-
token:my-project/docker-registry-token
anotherOne:my-project/a-secret
anotherOneToo:my-project/another-secret
i.e you just need to read these lines and split your map's key/values
Right now, if I want to pass a environment variable to a reusable workflow I have to do something like this:
name: Reusable workflow
on:
workflow_call:
inputs:
my_env_var:
required: false
type: string
env:
my_env_var: ${{ inputs.my_env_var }}
However, for this I first need to define as an input each environment variable I want to pass. This works, but having to hard code the environment variables makes my reusable workflows less generic. Is there a way to pass envs without having to define them one by one? I was thinking on something like this:
name: Calling reusable workflow
on:
workflow_dispatch:
jobs:
push-image-dev:
uses: ./.github/workflows/my-reusable-workflow.yml
with:
input1: ...
input2: ...
env:
env1: ...
env2: ...
However, I have been reading some documentation and I don't think that exists. Is there any other way of doing it, as inheriting env variables or creating a single input which is a variable dictionary, which is later parsed and sets all the env vars in the reusable workflow?
I created this simple logic which allows me to pass all the environment variables I want on a 1:N relationship with respect to inputs. I created an input which expects a list of environment variables, formatted as "env=value", and which is later converted to environment variables in a step inside the workflow.
Calling the workflow:
name: Calling reusable workflow
on:
workflow_dispatch:
jobs:
my-job:
uses: ./.github/workflows/my-reusable-workflow.yml
with:
env_vars: |
env1=value1
env2=value2
env3=value3
Workflow definition:
name: My reusable workflow
on:
workflow_call:
inputs:
env_vars:
description: List of environment variables to set up, given in env=value format.
required: false
type: string
jobs:
my-job:
runs-on: ubuntu-latest
steps:
- name: Set environment variables
if: ${{ inputs.env_vars }}
run: |
for i in "${{ inputs.env_vars }}"
do
printf "%s\n" $i >> $GITHUB_ENV
done
Please I'm trying to run some steps in the CircleCI Pipeline with conditions happened in the previous step. I tried a lot of tricks like exposing the value from Step 1 to global vars and pickup it in Step 2, I can see and print the variables in Step 2 but using WHEN BLOCK forever evaluated with Empty. I searched a lot and I knew that logical conditions already evaluated before running the jobs, Please I need alternative way to execute steps in second job in case a condition happened in Step 1?
I pasted here the example that I'm trying to fix
version: 2.1
orbs:
workflows:
test-and-deploy:
jobs:
- set-data:
context: my-context
- read-data:
context: my-context
requires:
- set-data
definitions:
node_image: &node-image
docker:
- image: cimg/node:14.15.5
executors:
base-12-14-0:
description: |
Single Docker container with Node 12.14.0 and Cypress dependencies
see https://github.com/cypress-io/cypress-docker-images/tree/master/base.
Use example: `executor: cypress/base-12-14-0`.
docker:
- image: cypress/base:12.14.0
jobs:
set-data:
<<: *node-image
description: Sets the data
steps:
- run: echo "VAR=app" > global-vars
- persist_to_workspace:
root: .
paths:
- global-vars
read-data:
<<: *node-image
description: read the data
steps:
- attach_workspace:
at: .
- run: ls
- run: cat global-vars // I COULD HERE SEE THE CORRECT VAR inside global-vars
- run: cat global-vars >> $BASH_ENV
- run: echo "Test $VAR" // Successfully Printed
- when:
condition:
matches: {
pattern: "app",
value: $VAR
}
steps:
- run: echo "Condition Executed"
It's not possible to use environment variables in logic statements. The reason is that logic statements are evaluated at configuration compilation time, whereas environment variables are interpolated at run time.
The only workaround I know of is to use the CircleCI dynamic configuration functionality to set pipeline parameters' values in the "setup workflow" that you then pass to the "continuation" workflow.
And by the way, you're not using $BASH_ENV correctly (https://circleci.com/docs/env-vars#setting-an-environment-variable-in-a-shell-command). But again, even if you did, you wouldn't be able to use an environment variable in a logic statement.
In a YAML playbook, I need to run multiple commands through each element in an array. I am using with_items to iterate, but I am getting syntax errors when I try to blend debug into the tasks.
Everything works when I remove the debug module, but than I can't see whats happening. Example provided below.
Produces Syntax Error:
- name: Iterate through array and display results
shell: "run command 1 on {{ item }}"
register: command1
debug:
msg: "Command 1 || {{ command1.stdout_lines}}"
shell: "run command 2 on {{ item }}"
register: command2
debug:
msg: "Command 2 || {{ command2.stdout_lines}}"
with_items: "{{ array_name }}"
Everything seems to be working fine when I remove the debugs, but I need to see what each command produces because it has important info.
Is there some way to print/debug the results while keeping it inside the with_items iterations?
Edit:
I also tried a few more things but they also gave me a syntax error
Also Tried 1:
- name: Iterate through array and display results
- shell: "run command 1 on {{ item }}"
register: command1
- debug:
msg: "Command 1 || {{ command1.stdout_lines}}"
- shell: "run command 2 on {{ item }}"
register: command2
- debug:
msg: "Command 2 || {{ command2.stdout_lines}}"
with_items: "{{ array_name }}"
Also Tried 2:
- name: Iterate through array and display results
- shell: "run command 1 on {{ item }}"
register: command1
- debug:
msg: "Command 1 || {{ command1.stdout_lines}}"
- shell: "run command 2 on {{ item }}"
register: command2
- debug:
msg: "Command 2 || {{ command2.stdout_lines}}"
with_items: "{{ array_name }}"
As the comments of β.εηοιτ.βε already stated
In Ansible, you can only have one module per item in the list (tasks is a list of tasks, each being an Ansible module) ... you have to have a new item in the list (a dash -), in front of all the debug and shell tasks.
This means,
integrate debug into iterations
I try to blend debug into the tasks
you can't do debugging in that way since the debug_module is for
Print statements during execution
only. However, according your question
Is there some way to print/debug the results while keeping it inside the with_items iterations?
it seems for me that you are more interested in Debugging tasks, play, role or block itself. So for this
Ansible offers a task debugger so you can fix errors during execution instead of editing your playbook and running it again to see if your change worked.
For usage you have to Enabling the debugger with the debugger keyword and may have a look into the Examples, Resolving errors in the debugger and Available debug commands.
Further questions here on SO are
How to debug Ansible issues?
Does Ansible have any feature to allow for debugging/pausing?
I was able to solve the issue, Ansible does not allow iterations through multiple tasks. I had to put the tasks in a separate yml playbook and use include_tasks: to call the playbook.
I have a text parameter in Jenkins that looks like so:
text(
defaultValue: '''
- 'evn-111'
- 'evn-423'
- 'evn-414'
''',
description: 'Add list of devices to create lab',
name: 'DEVICES',
)
I want to take this list of devices and pass it as a string into an ansible playbook.
However, when I pass this string into the ansible playbook like so:
extra_args = "-e devices=${ params.DEVICES }"
sh "ansible-playbook $extra_args deploy_lab.yml"
My output in Jenkins is:
ansible-playbook -e devices=
Followed by recommendations on what arguments I need to pass. Can anyone provide suggestions on how I can accomplish passing a multiline parameter to Ansible? Once I get the variable in the Ansible file, I have a template that creates another yml file using the multi-line text string passed from Jenkins.
My Ansible file looks like this:
---
- hosts: all
gather_facts: true
vars:
devices: ""
tasks:
- name: 'Create device file'
template:
src: devices.j2
dest: "host_vars/devices.yml"
mode: '0600'
delegate_to: localhost
My Template file devices.j2 looks like this:
---
devices_from_jenkins_param:
"{{ devices }}"
In the end, I want there to be a file host_vars/devices.yml that looks like this:
---
devices_from_jenkins_param:
- 'evn-111'
- 'evn-423'
- 'evn-414'