Ansible Playbook with Variables? - bash

Environment:
Ubuntu 16.04 in Azure vm
ansible 2.4.3.0
python version = 2.7.12 (default, Nov 20 2017, 18:23:56) [GCC 5.4.0 20160609]
azure-cli (2.0.27)
I have successfully created my first vm in azure using an Ansible Playbook.
Now, my question is this: Can I use variables in the playbook for things like admin-password, vmname etc, so that I could, for instance, put a list of 10 machines I want to create in a file with the different names and their passwords, and then loop through and feed that info through the playbook command to the playbook.yml file?
I'm open to any way of doing this, and I'm aware that Ansible may already have an easier/normal way of doing it, I just don't know enough to google what I need.
EDIT:
Evidently my question is too broad. I'll try and be more specific. I looked at the link that Konstantin Suvorov left. As I understand it, the method described does the inverse of what I'm after, by letting you assign variables to groups. And in the examples, to existing machines.
What I need is to be able to create vms, in the normal YAML style, but be able to have the process loop in some way so that it will create machines 1 thru 10 with all the same info, save for the machine name and the password. The examples above seem to use the variables to assign the same info to multiple machines.
How Can I do this? I'm mostly a bash scripter, so what I'd do is put name:password for each machine on multiple lines of a file, loop thru it and run the ansible-playbook command once per loop to create each machine, feeding in the variables somehow. However, I have a feeling that there's a more efficient way, such that I run the command once and the command will create each machine with the variables/info that are coded (somehow) into the yaml file. Is this specific enough?
FURTHER EDIT:
Thanks for putting me on the right track. I have adapted the script i used to create one vm to try to make it create two. I am getting assorted errors, none of which seem to actually point to offending code. Here follows what I'm trying:
- name: Create Azure VM
hosts: localhost
connection: local
with_items:
- { vmname: 'testvmfordb', userpassword: 'my password' }
- { vmname: 'testvmforfe', userpassword: 'my other password' }
tasks:
- name: Create virtual network inteface card
azure_rm_networkinterface:
resource_group: my_rg
name: "{{ item.vmname }}NIC"
virtual_network: my_rg
subnet: default
public_ip_name: "{{ item.vmname }}IP"
security_group: my_firewall_rules
- name: Create VM
azure_rm_virtualmachine:
resource_group: my_rg
name: "{{ item.vmname }}"
vm_size: Standard_DS1_v2
location: EastUS
admin_username: myusername
admin_password: "{{ item.userpassword }}"
ssh_password_enabled: true
storage_container: vhds
storage_blob: "{{ item.vmname }}osdisk.vhd"
network_interfaces: "{{ item.vmname }}NIC"
image:
offer: UbuntuServer
publisher: Canonical
sku: '16.04-LTS'
version: latest
os_type: Linux
Thanks in advance!
I followed #KonstantinSuvorov suggestion and separated the creation tasks in a separate file. I got this message:
TASK [include_tasks] *******************************************************************************************************************************
task path: /home/user/ansible_playbooks/azure_create_many_vms.yml:5
fatal: [localhost]: FAILED! => {
"reason": "included task files must contain a list of tasks"
}
fatal: [localhost]: FAILED! => {
"reason": "included task files must contain a list of tasks"
}
Here's the files/code:
azure_create_many_vms.yml
- name: Create Azure VM
hosts: localhost
connection: local
tasks:
- include_tasks: azure_create_many_vms_tasks.yml
with_items:
- { vmname: 'testvmforelastic', userpassword: 'my user password' }
- { vmname: 'testvmforfe', userpassword: 'my user password' }
azure_create_many_vms_tasks.yml
tasks:
- name: Create virtual network inteface card
azure_rm_networkinterface:
resource_group: my_core_rg
name: "{{ item.vmname }}NIC"
virtual_network: my_core_rg
subnet: default
public_ip_name: "{{ item.vmname }}IP"
security_group: my_core_firewall_rules
- name: Create VM
azure_rm_virtualmachine:
resource_group: my_core_rg
name: "{{ item.vmname }}"
vm_size: Standard_DS1_v2
location: EastUS
admin_username: adminuser
admin_password: "{{ item.userpassword }}"
ssh_password_enabled: true
storage_container: vhds
storage_blob: "{{ item.vmname }}osdisk.vhd"
network_interfaces: "{{ item.vmname }}NIC"
image:
offer: UbuntuServer
publisher: Canonical
sku: '16.04-LTS'
version: latest
os_type: Linux

You should remove tasks keyword from included file, leave just a plain list of tasks like this:
- name: Create virtual network inteface card
azure_rm_networkinterface:
...
- name: Create VM
azure_rm_virtualmachine:
...

Related

Create ec2 instance within vpc with two nics through ansible

after a day of googling I decided to give up and ask here:
I'm still quite new to ansible and AWS, hence this question might lack background information which I'm happy to provide on request.
What I'm trying to achieve:
Write an Ansible playbook, which creates a new ec2 instance within my vpc.
This instance shall be provided with two new nics, eth0 and eth1. These nics should be associated with each one specific security group.
My playbook so far is built like this:
Create eth0
Create eth1
Create ec2 instance
My problem:
All documentation says I need to provide the eni-id of the interface I'd like to attach to my instance. I can't provide this, since the ids do not exist yet. The only thing I know is the name of the interfaces so I was trying to get the id of the interfaces separately, which also didn't work.
If I try to register the output of the creation of eth{0,1} in ansible, the whole output is stored and breaks the validation later when calling the variables in the section of the instance creation. Same with the extra step after the creation process.
More about the setup:
Running a VPC in AWS, hosts inside VPC are only accessible through VPN.
Running Ansible on macOS:
ansible --version
ansible [core 2.14.1]
config file = None
configured module search path = ['/Users/mg/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/Cellar/ansible/7.1.0/libexec/lib/python3.11/site-packages/ansible
ansible collection location = /Users/mg/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.11.1 (main, Dec 23 2022, 09:40:27) [Clang 14.0.0 (clang-1400.0.29.202)] (/usr/local/Cellar/ansible/7.1.0/libexec/bin/python3.11)
jinja version = 3.1.2
libyaml = True
Playbook:
---
- name: Create ec2 instances
hosts: localhost
gather_facts: false
tasks:
# Block is a Group of Tasks combined together
- name: Get Info Block
block:
- name: Get Running instance Info
ec2_instance_info:
register: ec2info
- name: Print info
debug: var="ec2info.instances"
# By specifying always on the tag,
# I let this block to run all the time by module_default
# this is for security to net create ec2 instances accidentally
tags: ['always', 'getinfoonly']
- name: Create ec2 block
block:
- amazon.aws.ec2_vpc_net_info:
vpc_ids: vpc-XXXXXXXXXXXXXXXXX
- name: Create ec2 network interface eth0_lan
delegate_to: localhost
tags: ec2-create
amazon.aws.ec2_eni:
name: "eth0_lan_{{ vpc_hostname }}"
description: "eth0_lan_{{ vpc_hostname }}"
subnet_id: "{{ vpc_subnetid }}"
state: present
delete_on_termination: true
region: eu-central-1
security_groups: "sg-XXXXXXXXXXXXXXXXX"
- name: Get id of eth0
delegate_to: localhost
tags: ec2-create
amazon.aws.ec2_eni:
name: "eth0_lan_{{ vpc_hostname }}"
register: eth0
- name: Create ec2 network interface eth1_wan
delegate_to: localhost
tags: ec2-create
amazon.aws.ec2_eni:
name: "eth1_wan_{{ vpc_hostname }}"
description: "eth1_wan_{{ vpc_hostname }}"
subnet_id: "subnet-XXXXXXXXXXXXXXXXX"
state: present
delete_on_termination: true
region: eu-central-1
security_groups: 'sg-XXXXXXXXXXXXXXXXX'
- name: Get id of eth1
delegate_to: localhost
tags: ec2-create
amazon.aws.ec2_eni:
name: "eth1_wan_{{ vpc_hostname }}"
register: eth1
- name: Launch ec2 instances
tags: ec2-create
amazon.aws.ec2_instance:
name: "{{ vpc_hostname }}"
region: "eu-central-1"
key_name: "MyKey"
image_id: ami-XXXXXXXXXXXXXXXXX
vpc_subnet_id: "{{ vpc_subnetid }}"
instance_type: "{{ instance_type }}"
volumes:
- device_name: /dev/sda1
ebs:
volume_size: 30
delete_on_termination: true
network:
interfaces:
- id: "{{ eth0 }}"
- id: "{{ eth1 }}"
detailed_monitoring: true
register: ec2
delegate_to: localhost
# By specifying never on the tag of this block,
# I let this block to run only when explicitely being called
tags: ['never', 'ec2-create']
(If you're wondering about the tag-stuff, this comes from the tutorial I followed initially, credits: https://www.middlewareinventory.com/blog/ansible-aws-ec2/#How_Ansible_works_with_AWS_EC2_Setup_Boto_for_Ansible)
The execution of the ansible playbook breaks with this error:
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: Invalid type for parameter NetworkInterfaces[1].NetworkInterfaceId, value: {'changed': True, 'interface': {'id': 'eni-XXXXXXXXXXXXXXXXX', 'subnet_id': 'subnet-XXXXXXXXXXXXXXXXX', 'vpc_id': 'vpc-XXXXXXXXXXXXXXXXX', 'description': 'somedescription', 'owner_id': 'XXXXXXXXXXXXXXXXX', 'status': 'available', 'mac_address': 'xx:xx:xx:xx:xx:xx, 'private_ip_address': 'xx.xx.xxx.xx', 'source_dest_check': True, 'groups': {'sg-XXXXXXXXXXXXXXXXX': 'SGNAME'}, 'private_ip_addresses': [{'private_ip_address': 'xx.xx.xxx.xx', 'primary_address': True}], 'name': 'eth1_wan_<fqdn>', 'tags': {'Name': 'eth1_wan_<fqdn>'}}, 'failed': False}, type: <class 'dict'>, valid types: <class 'str'>
So, my colleague and I managed to solve this: Use "{{ eth0.interface.id }}" instead. However, all instances continue to terminate themselves on creation. In AWS console: Client.InternalError. This is related to kms/ebs encryption which I turned on by default today.
It turned out, that I tried to use an asymmetrical customer-managed-key for default ebs encryption. As soon as I replaced this with a symmetrical one, it worked and the instances would start.

Create Ec2 instance and install Python package using Ansible Playbook

I've created an ansible playbook for create an Ec2 instance and Install python to connect the server via ssh.
The Playbook successfully created an EC2 instance but it doesn't install the python on newly created Ec2 Instance instead it installing the python on my Master Machine.
Can someone help me to resolve this.
My Code:
- hosts: localhost
remote_user: ubuntu
become: yes
tasks:
- name: I'm going to create a Ec2 instance
ec2:
key_name: yahoo
instance_type: t2.micro
region: "ap-south-1"
image: ami-0860c9429baba6ad2
count: 1
vpc_subnet_id: subnet-aa84fbe6
assign_public_ip: yes
tags:
- creation
- name: Going to Install Python
apt:
name: python
state: present
tags:
- Web
- name: Start the service
service:
name: python
state: started
tags:
- start
As is shown in the fine manual, ec2: operations should be combined with add_host: in order to "pivot" off of localhost over to the newly provisioned instance, which one cannot add to an inventory file because it doesn't exist yet
- hosts: localhost
gather_facts: no
tasks:
- name: I'm going to create a Ec2 instance
ec2:
...etc etc
register: run_instances
- name: Add new instance to host group
add_host:
hostname: "{{ item.public_ip }}"
groupname: just_launched
loop: "{{ run_instances.instances }}"
- name: Wait for SSH to come up
delegate_to: "{{ item.public_ip }}"
wait_for_connection:
delay: 60
timeout: 320
loop: "{{ run_instances.instances }}"
- hosts: just_launched
# and now you can do fun things to those ec2 instances
I suspect your question about "installing python" is unrelated, but just in case: if you genuinely really do need to add python to those ec2 instances, you cannot use most ansible modules to do that because they're written in python. But that is what the raw: module is designed to fix
- hosts: just_launched
# you cannot gather facts without python, either
gather_facts: no
tasks:
- raw: |
echo watch out for idempotency issues with your playbook using raw
yum install python
- name: NOW gather the facts
setup:
- command: echo and now you have working ansible modules again
I also find your invocation of service: { name: python, state: started } suspicious, but I would guess it has not run yet due to your question here

How to get IP addresses by using Ansible?

I have two DigitalOcean Droplets with public and private IP addresses, created by Vagrant and Ansible playbook.
I need to create was_route53 records for each address (two records per droplet)
How I can get addresses into vars to use it in playbook?
I made a playbook this weekend to do something very similar.
In my case, I create a droplet on digitalocean, save the IP address, and then add a DNS record to AWS.
I hope this helps. Note that the playbook still has some things hardcoded, such as region and size because I have not polished it off yet.
Also, this is the first time I have used ansible with DO and AWS, and it's the first time that I have used the "register" feature to save variables, so this is probably a long way from best practice.
One thing that seems ugly is my hardcoding of my venv python interpreter.
If you are happy to use your system python, then you don't need to worry about that. The problem is that when ansible sees connection: local, it uses the system python, which is a bit odd since the script is running in a venv on the same machine.
You need to pip install boto3
and:
ansible-galaxy collection install community.digitalocean
ansible-galaxy collection install community.aws
example playbook
---
- hosts: all
connection: local
become_user: tim
vars: #local connection defaults to using the system python
ansible_python_interpreter: /home/tim/pycharm_projects/django_api_sync/ansible/venv/bin/python3
vars_files:
- includes/secret_variables.yml
tasks:
- name: create a DigitalOcean Droplet
community.digitalocean.digital_ocean_droplet:
state: present
name: "{{droplet_name}}"
oauth_token: "{{digital_ocean_token}}"
size: "s-2vcpu-2gb"
region: SGP1
monitoring: yes
unique_name: yes
image: ubuntu-20-04-x64
wait_timeout: 500
ssh_keys: [ "{{digital_ocean_ssh_fingerprint}}"]
register: my_droplet
- name: Print IP address
ansible.builtin.debug:
msg: Droplet IP address is {{ my_droplet.data.ip_address }}
- name: Add A record with route53
community.aws.route53:
aws_access_key: "{{aws_access_key}}"
aws_secret_key: "{{aws_secret_key}}"
state: present
zone: growthpath.com.au
record: "{{ ansible_host }}"
type: A
ttl: 7200
value: "{{ my_droplet.data.ip_address }}"
wait: yes
Example inventory file:
all:
hosts:
test-dear1.growthpath.com.au:
droplet_name: test-dear1
ansible_host: test-dear1.growthpath.com.au
ansible-playbook -i inventory_aws_test.yml -v create_new_droplet.yml

Ansible-Playbook - Save output to a remote server

I'm new to all the Ansible stuff. So most of the time I'm in "Trial and Error"-Mode.
Now I'm facing a challenge with a playbook and I do not know to look further.
The main task of this playbook should be to get a "Show run" from a Cisco Device and save this in a text file on a backup server (which is a remote server).
The only task, which is not working, is the Backup Task.
Here is my playbook:
- hosts: IOSGATEWAY
gather_facts: no
connection: local
tasks:
- name: GET CREDENTIALS
include_vars: path/to/all/all.yml
- name: DEFINE CONNECTION TO GW
set_fact:
connection:
host: "{{ inventory_hostname }}"
username: "{{ creds['username'] }}"
password: "{{ creds['password'] }}"
- name: GET SHOW RUN
ios_command:
provider: "{{ connection }}"
commands:
- show run
register: show_run
- name: SAVE TO BACKUP SERVER
copy:
content: "{{ show_run.stdout[0] }}"
dest: "path/to/Directory/{{ inventory_hostname }}.txt"
delegate_to: BACKUPSERVER
Can someone hint me in the right direction?
You set connection: local for the playbook, so everything you do is executed locally (which is correct for ios_... modules, but not what you actually want for copy module).
I'd recommend to define ansible_connection variable in your inventory per group of hosts/devices, so Ansible will use local connection for your ios devices, and ssh for backup-server.

Trying to include a list of tasks from an playbook in Ansible

My folder structure:
First I'll give you this so you can see how this is laid out and reference it when reading below:
/environments
/development
hosts // Inventory file
/group_vars
proxies.yml
/custom_tasks
firewall_rules.yml // File I'm trying to bring in
playbook.yml // Root playbook, just brings in the plays
rev-proxy.yml // Reverse-proxy playbook, included by playbook.yml
playbook.yml:
---
- include: webserver.yml
- include: rev-proxy.yml
proxies.yml just contains firewall_custom_include_file: custom_tasks/firewall_rules.yml
firewall_rules.yml:
tasks:
- name: "Allowing traffic from webservers on 80"
ufw: src=10.10.10.3, port=80, direction=in, rule=allow
- name: "Allowing traffic all on 443"
ufw: port=443, rule=allow
and finally rev-proxy.yml play:
---
- hosts: proxies
become: yes
roles:
- { role: firewall }
- { role: geerlingguy.nginx }
pre_tasks:
# jessie-backports for nginx-extras 1.10
- name: "Adding jessie-backports repo"
copy: content="deb http://ftp.debian.org/debian jessie-backports main" dest="/etc/apt/sources.list.d/jessie-backports.list"
- name: Updating apt-cache.
apt: update_cache="yes"
- name: "Installing htop"
apt:
name: htop
state: present
- name: "Coopying SSL certificates"
copy: src=/vagrant/ansible/files/ssl/ dest=/etc/ssl/certs force=no
tasks:
- name: "Including custom firewall rules."
include: "{{ inventory_dir }}/{{ firewall_custom_include_file }}.yml"
when: firewall_custom_include_file is defined
vars_files:
- ./vars/nginx/common.yml
- ./vars/nginx/proxy.yml
What I'm trying to do:
Using Ansible 2.2.1.0
I'm trying to include a list of tasks that will be run if a variable firewall_custom_include_file is set. The list is included relative to the inventory directory by doing "{{ inventory_dir }}/{{ firewall_custom_include_file }}.yml" - in this case that works out to /vagrant/ansible/environments/development/custom_tasks/firewall_rules.yml
Essentially the idea here is that I need to have different firewall rules be executed based on what environment I'm in, and what hosts are being provisioned.
To give a simple example: I might want to whitelist a database server IP on the production webserver, but not on the reverse proxy, and also not on my development box.
The problem:
Whenever I include firewall_rules.yml like above, it tells me:
TASK [Including custom firewall rules.] ****************************************
fatal: [proxy-1]: FAILED! => {"failed": true, "reason": "included task files must contain a list of tasks"}
I'm not sure what it's expecting, I tried taking out the tasks: at the beginning of the file, making it:
- name: "Allowing traffic from webservers on 80"
ufw: src=10.10.10.3, port=80, direction=in, rule=allow
- name: "Allowing traffic all on 443"
ufw: port=443, rule=allow
But then it gives me the error:
root#ansible-control:/vagrant/ansible# ansible-playbook -i environments/development playbook.yml
ERROR! Attempted to execute "/vagrant/ansible/environments/development/custom_tasks/firewall_rules.yml" as inventory script: problem running /vagrant/ansible/environments/development/custom_tasks/firewall_rules.yml --list ([Errno 8] Exec format error)
Attempted to read "/vagrant/ansible/environments/development/custom_tasks/firewall_rules.yml" as YAML: 'AnsibleSequence' object has no attribute 'keys'
Attempted to read "/vagrant/ansible/environments/development/custom_tasks/firewall_rules.yml" as ini file: /vagrant/ansible/environments/development/custom_tasks/firewall_rules.yml:2: Expected key=value host variable assignment, got: name:
At this point I'm not really sure what it's looking for in the included file, and I can't seem to really find clear documentation on this, or other people having this issue.
Try to execute with -i environments/development/hosts instead of directory.
But I bet that storing tasks file inside inventory is far from best practices.
You may want to define list of custom rules as inventory variable, e.g.:
custom_rules:
- src: 10.10.10.3
port: 80
direction: in
rule: allow
- port: 443
rule: allow
And instead of include task, make something like this:
- ufw:
port: "{{ item.port | default(omit) }}"
rule: "{{ item.rule | default(omit) }}"
direction: "{{ item.direction | default(omit) }}"
src: "{{ item.src | default(omit) }}"
with_items: "{{ custom_rules }}"

Resources