Ansible add_host not being applied in Azure dynamic inventory - azure

I am trying to add a host in Ansible to the Azure dynamic inventory at run time.
First I log the "before add_host" contents of the 'ansible_play_hosts_all' inventory variable
Then I use "add_host" to add a new host
When I log the "after add_host" contents of the 'ansible_play_hosts_all' inventory variable, I can see my new host added to the list.
But when the next task in the playbook is run, it is not run on the newly added host.
I have also tried using "meta: refresh_inventory", but to no avail.
Any help is appreciated - thanks
- name: "Log the contents of the 'ansible_play_hosts_all' magic inventory variable before testing ssh connectivity"
debug:
msg: "{{ ansible_play_hosts_all }}"
# Wait for vm_to_be_added to become contactable via ssh, then refresh the inventory
- name: Waits for SSH port 22 of the EPMP host to become available
wait_for:
host: vm_to_be_added
port: 22
state: started
timeout: 600
# Add vm_to_be_added host to the dynamic inventory
- name: Add vm_to_be_addedhost to the dynamic inventory
add_host:
hostname: "vm_to_be_added"
group: tag_workspace_cluster
# Log the contents of the 'ansible_play_hosts_all' magic inventory variable after testing ssh connectivity
- name: "Log the contents of the 'ansible_play_hosts_all' magic inventory variable after testing ssh connectivity"
debug:
msg: "{{ ansible_play_hosts_all }}"
# Record the IP of the machine currently running(hosting) Ansible.
- set_fact: ANSIBLE_HOST_IP="{{lookup("pipe","hostname -I")}}"

The solution I have ended up with is:
I removed add_host and used meta: refresh_inventory (after I had waited for a connection & confirmed I could shh to the new server )
After the refresh I then started a new play. This is important, as it is only in the new play I see the new host now added to the dynamic inventory group.
In short it now looks like this:
- name: Play 1
hosts: localhost
become: true
pre_tasks:
- name: Waits for SSH port 22 of the host to become available
wait_for:
host: vm_to_be_added
port: 22
state: started
timeout: 600
# Now that we know that the vm_to_be_added is contactable, refresh to have it included in the inventory.
# The newly refreshed inventory will only be availble to the next play.
- meta: refresh_inventory
- name: Play 2
hosts: dynamic_group
become: true
pre_tasks:
# This task will be run against the newly added host
- set_fact: ANSIBLE_HOST_IP="{{lookup("pipe","hostname -I")}}"

Related

ansible become method - change between hosts

lets say I have a playbook:
---
- name
host: testserver
become_method = sudo
become_user = root
tasks:
- name: copystuff
module: copy
do stuff
- name: copy stuff
delegate_to: localhost
module: copy
do stuff
I have same user with the same password on both localhost and the testserver. But on testserver, I have to use sudo to beocme a root but on localhost I have to use dzdo to become a root.
So I was wondering if there is a way for me use either one, if one method does not work?
Yes, that's what hostvars are designed to do: declare vars per host.
You can do that in your inventory file, as shown in the fine manual, if you have an inventory file (or in most dynamic inventory sources). Or, if you are running with a more ad-hoc inventory list (such as -i machine1,localhost,machine99) then you can use a conditional set_fact: to declare that hostvar after ansible has started running:
- set_fact:
ansible_become_method: dzdo
when: inventory_hostname == "localhost"
so long as you do that before using a task that has become: yes on it
The pre_tasks: keyword may interest you, too

I wanted to deploy multiple VMs from ansible, from values given by the enduser

I wanted to deploy multiple VMS from my ansible-playbook, so I used split function and I am getting the error of dict does not have user_inout error. Please have a look at my code.
Code:
- name: os system
pause:
prompt: |
Which os do you want to use?
1- Windows Server
2- CentOS_7
3- CentOs_8
4- Ubuntu
5- Others
register: os_system
- set_fact:
o_name: "{{ os_system.user_input.split(',') }}"
- name: Domain Decision
pause:
prompt: Do you want your PC in Domain
register: decision
when: 'item|string == "1"'
with_items:
- "{{ o_name }}"
- set_fact:
dec: "{{ decision.user_input.split(',') }}"
Now my real issue, if the user chooses option 1,2 then it will be split by set_fact for os_system and according to that input it will decide for the domain decision my main issue is that while the task is at set_fact for the decision, it will give me the error like this:
Error:
fatal: [x.x.x.x]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'user_input'\n\nThe error appears to be in '/home/x.x.x.x/sites/playbook.yml': line 82, column 6, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - set_fact:\n ^ here\n"
}
IF I remove the loop from task domain decision, then set_fact works very perfectly.
When you want to collect user input, it can be in two ways:
Interactive (prompted), using vars_prompt.
Through variables (non-prompted). For example, you could have users create a file with variables and load with vars_file.
I would prefer the second approach if possible.
However, to get user input interactively through prompts:
- hosts: my_hosts
vars_prompt:
- name: os_system
prompt: |
Which os do you want to use?
1- Windows Server
2- CentOS_7
3- CentOs_8
4- Ubuntu
5- Others
private: no
tasks:
- set_fact:
o_name: "{{ os_system.split(',') }}"
You should note that by splitting the input of 1,2 - you will not get OS name. You will again have to set a fact based on the number (1 = Windows Server). IMHO this is unnecessary complication.
A much better option would be to have users create a variables file like below:
my_vars.yml:
os_choices:
- { name: Windows Server, domain: yes }
- { name: CentOS_7, domain: no }
And load it in play using:
- hosts: my_hosts
vars_file:
- my_vars.yml

With ansible, how do I read a file and use the IP address contained as an argument to update an Azure MySQL firewall?

My current project is to create a DigitalOcean Droplet and add the IP address to the Azure MySQL server firewall. In my script to spin up the Droplet I save the IP address as a .txt with only the IPv4 address. The ansible playbook I have now uses the following to read from the file and store the IP address as a variable.
vars:
droplet_ip: "{{ lookup('file', '/tmp/created-droplet-ip.txt') }}"
I print this out in my playbook for debugging and it's the correct IP in the correct format (x.x.x.x).
I'm using azure_rm_mysqlfirewallrule to create the rule for this new address. I can login to Azure, find the resource group and server name but I get an error when using my droplet_ip variable, which is:
File \"/home/me/.local/lib/python3.8/site-packages/msrest/serialization.py\", line 674, in validate\n raise ValidationError(key, name, value)\nmsrest.exceptions.ValidationError: Parameter 'FirewallRule.start_ip_address' must conform to the following pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'.\n"
When I type the IP address out as the parameter it works fine so I'm 99% certain it's either how I'm reading the file or using the variable name. For this to be automated that isn't a possibility for me so if anyone can help I would be extremely grateful.
Below is my playbook:
---
- hosts: local
vars:
droplet_ip: "{{ lookup('file', '/tmp/created-droplet-ip.txt') }}"
tasks:
- name: Print droplet_ip
debug:
msg: "{{ droplet_ip }}"
- name: Azure login
shell: az login
- name: Update firewall
azure_rm_mysqlfirewallrule:
ad_user: x#y.com
resource_group: example_group
server_name: example-dbs
name: new_machine
password: ******
start_ip_address: droplet_ip
end_ip_address: droplet_ip
subscription_id: xxxxxx-xxxxxxxx-xxxxxxxx
tenant: yyyyyyyy-yyyyyyyy-yyyyyyyyyyyyy
If you want to include the value of the droplet_ip variable in your task, you need to write:
start_ip_address: "{{ droplet_ip }}"
The way it's written write now, you're trying to set start_ip_address (et al) to the literal string "droplet_ip".

How can I configure IIS subfeatures in Ansible

New to Ansible I'm experimenting with setting up a website under IIS.
I can create and configure an application pool, but I'm struggling with the website. The basic site works, HTTPS/SSL is still troublesome, but I read there are some bugs in the win_iis_website/win_iis_webbinding scripts that are being worked on. The part I'm stuck with are IIS' features per site.
In IIS (in the GUI) there are sub-features that can be configured for a site:
I was unable to find how to configure these using Ansible (more specifically Ansible's win_iis_website module).
I'm looking to configure ASP, Handler mappings, URL rewrites and Default documents.
Is there any way to do so?
My current yml for creating the site looks like this:
- name: create new website {{ websitename}}
win_iis_website:
name: "{{ websitename}}"
state: started
port: 443
ip: *
ssl: true
hostname: "{{ websitename }}"
application_pool: "{{ websitename }}"
physical_path: c:\inetpub\wwwroot\{{ websitename }}
parameters: logfile.directory:c:\inetpub\logs\
register: website
I am currently making Playbooks for IIS and indeed to perform the configuration there is no particular module that allows you to modify the functions of the sections, I looked in some places and the information was very scarce, there are modules for applicationPool, but for this you have to use win_shell as follows
- name: Name of playbook
win_shell: |
<PowerShell command>
You can base on the CIS BENCHMARK guide of IIS.
Check the win_feature module:
- name: Install IIS Web-Server with sub features and management tools
win_feature:
name: Web-Server
state: present
restart: True
include_sub_features: True
include_management_tools: True
if you want to do in a more controlled manner, check the installed features with the command:
Get-WindowsFeature
And add like:
- name: Install IIS
win_feature:
name: "Web-Filtering,Web-Dir-Browsing,Web-Default-Doc"
state: present
restart: no
include_sub_features: no
include_management_tools: yes

How to create instances and install packages on azure using ansible

We are trying to create and install packages on Azure using Ansible. We are able to create the instance using Ansible Azure module but we are stuck at installing the packages once the VM is created because we don't know what the IP address of the newly created VM is.
We want to complete this in single run. Is this possible?
I've not used the Azure module so could be wrong but you should be able to use register to store some data about the instances you've just created.
You can then pass this data into a dynamically defined host group in a task by iterating through the output of the first task by using the add_host module.
So your playbook may look something like:
- hosts: local
connection: local
tasks:
- name : Create Windows instance
azure :
name: "ben-Winows-23"
hostname: "win123"
os_type: windows
enable_winrm: yes
subscription_id: "{{ azure_sub_id }}"
management_cert_path: "{{ azure_cert_path }}"
role_size: Small
image: 'bd507d3a70934695bc2128e3e5a255ba__RightImage-Windows-2012-x64-v13.5'
location: 'East Asia'
password: "xxx"
storage_account: benooytes
user: admin
wait: yes
virtual_network_name: "{{ vnet_name }}"
register : azure
- name : Debug Azure output
debug :
var : azure
### Assumes that the output from the previous task has an instances key which in turn has a public_ip key. This may need updating to give the proper path to a resolvable hostname or connectable IP. Use the output of the debug task to help with this. ###
- name : Add new instance to host group
add_host :
hostname : {{ item.public_ip }}
groupname : launched
with_items : azure.instances
### Only target newly launched instances from previous play ###
- hosts: launched
tasks:
- name : Start foo service and make it auto start
win_service :
name : foo
start_mode : auto
state : started
- name : Do some thing else
...

Resources