Curious as to how I can pass a number to a jinja2 template file in a Terraform -> Ansible flow?
In my tfvars json, I have:
{
"Users": 100
}
And in my main.tf, I am calling an ansible playbook upon starting my ec2 instance and running:
- sudo ansible-playbook /PostServerConfiguration.yml --extra-vars '${jsonencode({"lic_users"=var.Users})}'
That variable in ansible should go to a jinja2 template file, but I need it as an int and not a string. Would anyone happen to know the solution to pass an int through? In my variables.tf I have also put the type for Users to be a number
I found the answer :)
First I tested by running the debug task in the ansible-playbook to output lic_users during runtime. It was 100 as an int, so that crosses off Terraform as the culprit.
I ended up using "{{ lic_users | int }}" in my j2 template and it worked. Thanks all!
Related
The source of my host inventory is from an internal tool that outputs pairs of values, example, here are six observations, I currently have 160 observations:
servername1 processname1
servername1 processname2
servername1 processname3
servername2 processname1
servername3 processname1
servername4 processname1
So column 1 is my target host list (my inventory). Column2 are unique processname values, assigned specifically to the value of the servername. Often the same server will occur. Some servers have only one processname, others may have 2 to N. Meaning my target host may repeat for a unique list of processnames. I want to use both dynamic inventory from this output list of pairs, and I need both values on each observation to be associated and assigned to variables. I'm not absolutely required to use dynamic inventory, I just need a solution. I also need to pass to the target host and the value in {{ processname# }}, via the command: or shell: modules. (This is unique, there are no modules related to this need)
If required, I have a way to filter this data and output it in JSON format or YAML, making a separate YML file for each host. While I'd prefer to process these dynamically; pre-processing the list is acceptable.
Because ansible-playbook, requires some known host inventory list, I'm getting stuck understanding how I can create this list, from my dynamic output, at the time I start the play.
What I've done so far: I've tried reading up and trying to set these pairs as in the /etc/ansible/hosts/host_vars/servername#.yml files. This is extremely ugly, as I have to pre-process the output of the data, into YML format. But it does not give me a host list to reference in my playbook. So while it seems that hostvar is the logical choice, I cannot get my head around it.
What I need:
- The suggested format of the data? JSON? YAML? Other? (if I cannot read it in dynamically.
- Is putting this in host_vars correct?
- Last night I saw another answer using set_fact, would that help?
Thank you for any insight. I've now been using Ansible for 3.5 weeks! I've done pretty good using static and dynamic inventories, but this stumps me as the inventory list is not obvious, give the format of the matched pairs.
Note: MANY have suggested using host_vars, but that seems to me, to be reserved to hostnames, and related port and proxy values. I could be wrong.
===================================================================
UPDATE: Thanks for the help in the right direction.
I have updated our inventory script to output host list in JSON.
The first new option is to output the hosts in JSON.
Example:
{"my_host":["servername1","servername2",]}
Calling this as a dynamic inventory script, works great!
ansible all -m ping
servername1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
servername2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Next: The second new option to the inventory script was to add a new switch to input a hostname. This part is still confusing me. Here is the output:
showInv --host=servername1
{"servername1":["processname1","processname2","processname3",]}
The final part that I am missing is how I call the inventory script with a specific "--host={{ my_host }} , from within my playbook.
It seems that I need to find the variable for the existing hostname and pass that back to the inventory script as the switch option "--host= "
You say that you are OK with dynamic inventories. Make your own.
Here is the docs.
You need to make a script that will do two things:
when executed with --list, processes your file and prints this JSON to stdout:
{ "myhosts": ["servername1", "servername2", "servername3"] }
when executed with --host servername1, prints this JSON to stdout:
{ "myprocesses": ["processname1", "processname2"] }
So with --list you should provide uniq list of hosts. In my example they belong to myhosts group.
And with --host <hostname> you should provide list dict of host vars for that host (<hostname>). In my example there is a list variable myprocesses that contains all processes for that host.
Then just call ansible-playbook -i my_inv_script myplaybook.yml.
Example playbook:
---
- hosts: myhosts
tasks:
- debug:
msg: "Process name is {{ item }}"
with_items: "{{ myprocesses }}"
This playbook will go trough all hosts in your dynamic inventory and print all processes for each host.
You will need to develop a dynamic inventory script, that takes the first column as the hostname, and the second column as variables for that host.
Please, find below the link to my dynamic inventory written in php
https://github.com/walden-it/ansible-ij/blob/master/inventory.php
take a look at the functions get_vars() and get_hosts() to see how is the array being populated.
And in case you need it, here is the dump for the database this script is looking at:
https://github.com/walden-it/ansible-ij/blob/master/ansible.sql
Then you just specify it with -i inventory in the ansible run, or add it as inventory_file to the ansible.cfg
Closing this out. With the help of Konstantin's suggestions, I now have a working play. What is not immediately apparent, is that Ansible is doing some "magic" behind the scenes. I had to modify my inventory script, that generates my dynamic inventory to accept the "--list" switch option, and the "--host hostname" option.
Once this was done, I could run the playbook with the -i listInv and Ansible internally calls this script as listInv --list, which produces my dynamic inventory list. Then it loops, through to the with_items, and internally calls the script as, listInv --host {{ items }} and outputs the matching processnames.
Additionally, the JSON output generated by my script, had to make the "group" (first) field, "myprocess". Initially, I had it as "my_process", and this failed. Removing the underscore, fixed, that error.
All working now. This is a great example for learning, but it's still magic.
The playbook looks like this:
- hosts: all
gather_facts: no
connection: local
tasks:
- debug:
msg: "Process name is {{ item }}"
with_items: "{{ myprocess }}"
I'm running an ansible playbook on a list of hosts with a host file:
[consul]
${HOST1} ansible_ssh_host=${HOST1} ansible_ssh_user=devops ansible_ssh_pass=blabla
${HOST2} ansible_ssh_host=${HOST2} ansible_ssh_user=devops ansible_ssh_pass=blabla
.......so on...
The thing is that I need to pass a different variable for each host.
I know of the flag -e that allows me to send a variable with the ansible-playbook command but it's not for each of the hosts.
I'm running the playbook with this:
ansible-playbook -vvvv site.yml
How can I pass a different var for each host?
Thanks!
Note: I'm using ansible 1.7.1
Two ways you should be able to do this:
1) Include the variable in your host file:
[consul]
${HOST1} ansible_ssh_host=${HOST1} .... myvar=x
${HOST2} ansible_ssh_host=${HOST2} .... myvar=y
2) Or use the include_vars task to load a file based on the host name
include_vars: "{{ ansible_ssh_host }}.yml"
The second method is good if you have a lot of variables to load for a host.
For more complex cases the lookups module might help:
http://docs.ansible.com/ansible/playbooks_lookups.html
I have the following version installed: ansible 2.3.0 (devel 2131eaba0c)
I want to specify my host variable as external variable and then use it in the playbook similar to this:
hosts: "{{integration}}"
In my group_vars/all file I have the following defined variable:
integration: "int60"
The host file looks like this:
[int60]
hostA
[int61]
hostB
Unfortunately this does not work. I also tried to define the host var in the following way:
[integration]
127.0.0.1 ansible_host="{{ integration_env }}"
and have the integration_env specified in my group_vars/all file. In this case it seemed like it ran the tasks locally and not in the desired environment.
Is it possible to do something like this? I'd be open to whole new ways of doing this. The main goal is simply to define the host variable in a var file.
This will work if you pass integration variable as extra variable:
ansible-playbook -e integration=int60 myplaybook.yml
Any variables used in play "header", should be defined before Ansible parses playbook.
In your example you define integration as host facts. Facts are only defined on task level, not play level.
Update: and you can use other ways of passing variables, not only extra vars.
For example:
- hosts: "{{ lookup('env','DYN_HOSTS') }}"
will also work.
I am provisioning AWS infrastructure using terraform and want to pass variables such as aws_subnet_id and aws_security_id into ansible playbook using vars_file (don't know if there is any other way though). How can I do that?
I use Terraform local_file to create an Ansible vars_file. I add a tf_ prefix to the variable names to make it clear that they originate in Terraform:
# Export Terraform variable values to an Ansible var_file
resource "local_file" "tf_ansible_vars_file_new" {
content = <<-DOC
# Ansible vars_file containing variable values from Terraform.
# Generated by Terraform mgmt configuration.
tf_environment: ${var.environment}
tf_gitlab_backup_bucket_name: ${aws_s3_bucket.gitlab_backup.bucket}
DOC
filename = "./tf_ansible_vars_file.yml"
}
Run terraform apply to create Ansible var_file tf_ansible_vars_file.yml containing Terraform variable values:
# Ansible vars_file containing variable values from Terraform.
# Generated by Terraform mgmt configuration.
tf_environment: "mgmt"
tf_gitlab_backup_bucket_name: "project-mgmt-gitlab-backup"
Add tf_ansible_vars_file.yml to your Ansible playbook:
vars_files:
- ../terraform/mgmt/tf_ansible_vars_file.yml
Now, in Ansible the variables defined in this file will contain values from Terraform.
Obviously, this means that you must run Terraform before Ansible. But it won't be so obvious to all your Ansible users. Add assertions to your Ansible playbook to help the user figure out what to do if a tf_ variable is missing:
- name: Check mandatory variables imported from Terraform
assert:
that:
- tf_environment is defined
- tf_gitlab_backup_bucket_name is defined
fail_msg: "tf_* variable usually defined in '../terraform/mgmt/tf_ansible_vars_file.yml' is missing"
UPDATE: An earlier version of this answer used a Terraform template. Experience shows that the template file is error prone and adds unnecessarily complexity. So I moved the template file to the content of the local_file.
terraform outputs are an option, or you can just use something like:
provisioner "local-exec" {
command = "ANSIBLE_HOST_KEY_CHECKING=\"False\" ansible-playbook -u ${var.ssh_user} --private-key=\"~/.ssh/id_rsa\" --extra-vars='{"aws_subnet_id": ${aws_terraform_variable_here}, "aws_security_id": ${aws_terraform_variable_here} }' -i '${azurerm_public_ip.pnic.ip_address},' ansible/deploy-with-ansible.yml"
}
or you can do a sed thing ... as a local provisioner to update the var file..
or you can use terraform outputs.... your preference....
I highly recommend this script. It works well and is maintained by Cisco and will give you more flexibility.
https://github.com/CiscoCloud/terraform.py
Since I want to run both terraform plan and ansible --check in a pull request with a CI, I've decided to go with terraform output.
Basically this is how I run my Ansible now and it gets all outputs from terraform :
With the following output
// output.tf
output "tf_gh_deployment_status_token" {
value = var.GH_DEPLOYMENT_STATUS_TOKEN
sensitive = true
}
Running a bit modified terraform output parsed with jq :
$ terraform output --json | jq 'with_entries(.value |= .value)'
{
"tf_gh_deployment_status_token": "<some token>"
}
This makes it great to run with --extra-args with Ansible :
$ ansible-playbook \
-i ./inventory/production.yaml \
./playbook.yaml \
--extra-vars "$(terraform output --json | jq 'with_entries(.value |= .value)')"
Now I can use {{ tf_gh_deployment_status_token }} anywhere in my playbook.
Use terraform outputs - https://www.terraform.io/intro/getting-started/outputs.html (it is not clear if you are using it already)
Then using command like terraform output ip, you can then use those values in your scripts to generate or populate other files like inventory files or vars_file.
Another option is to use terraform templates and render your files like inventory files from terraform itself and then use it from Ansible.
I would like to have a master playbook, which include's other playbooks. Is it possible to pass a variable to that included playbook?
The normal syntax which is used for passing variables to included tasks doesn't work (see below)
- include: someplaybook.yml variable=value
and
- include: someplaybook.yml
vars:
variable: value
I'm running v2.0.2.0.
The only thing i see missing is quotes.
- include: someplaybook.yml variable='value'
It works for me and should work for you too. If not share the error you face.
Make sure you have this variable "variable" defined in the task of the role as well and from here you are just passing the value to that variable.
Tested on ansible 2.4
- import_playbook: any_playbook.yml variable='value'
Also, I suggest you read this,
http://docs.ansible.com/ansible/latest/playbooks_reuse.html
and try using roles in this case, it'll help in a case like this, where you're trying to include/import multiple playbooks in a single main playbook.
And about passing a value to the include statement you can add it to the vars main.yml of the role.
Or, if the variable you want to pass is the result of a previous task in the single main playbook use 'register' and save the output in a varible.
- debug: msg="{{result.stdout_lines}}"
here, result is the registered variable.
Use the debug module to know exactly what you want to pass to the playbook.
Hope this helps.
In my opinion most consistent way to pass variable to included playbook and to another play in the current playbook as well is using:
set_fact:
global_var_name: "{{ your_var }}"
before
- import_playbook: your_playbook.yml
After you set the fact it's is accessible from any play in the playbook and imported playbooks, for instance inside "your_playbook" you can call it like so:
debug:
var: global_var_name
If you use:
- import_playbook: someplaybook.yml variable='value'
you can only pass fixed 'value'(in include as well) if you try to pass var value:
- import_playbook: someplaybook.yml internal_var="{{ your_var }}"
you will get NOT DEFINED when you call internal_var inside 'someplaybook.yml'
All of this is true for the beginning of 2021, ansible 2.9*, it's quite possible they will 'fix' import_playbook, I would like this, by the way.