How to create instances and install packages on azure using ansible - azure

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
...

Related

Ansible gcp_compute_instance_template: argument network is of type <class 'str'> found in 'properties -> network_interfaces'

I'm currently adding GCP to our ansible system as up until now we've done the lengthy process of creating images, instance templates, groups and deploying them all manually with the CLI suite.
I'm getting stuck on an error with network interfaces trying to create a simple Instance Template using the same parameters we used to do manually.
Error:
"msg": "argument network is of type <class 'str'> found in 'properties -> network_interfaces'. and we were unable to convert to dict: dictionary requested, could not parse JSON or key=value"
We don't have a default network for our GCP instances as we have a very specific setup so omitting the network parameter isn't viable either. When I do I get the error The resource 'projects/<PROJECT_NAME>/global/networks/default' was not found\",. As a test when I put the default network as my parameter I again get the <class 'str'> error.
I'm feel like I'm losing my mind. Here is my playbook (with parts changed for company anonymity):
- hosts: localhost
vars_files:
- ../vault/vault.yml
vars:
current_date: "{{ ansible_date_time.year }}{{ ansible_date_time.month }}{{ ansible_date_time.day }}"
site_code: [ eur ]
nat_zone: [ a ]
project_name: "PROJECT_NAME"
network_name: "STG-NET"
image: "haproxy-master-20210219-01"
- name: Create instance template in staging
google.cloud.gcp_compute_instance_template:
name: "ew2-{{ item[1] }}-ig-stg-{{ item[0] }}-haproxy-tpl-{{ current_date }}-test"
properties:
disks:
- auto_delete: true
boot: true
initialize_params:
source_image: "projects/{{ project_name }}/global/images/{{ image }}"
machine_type: n1-standard-1
network_interfaces:
- network: 'projects/{{ project }}/global/networks/{{ network }}'
access_configs:
- name: access_config
type: ONE_TO_ONE_NAT
project: "{{ project_name }}"
auth_kind: "{{ gcp_auth_kind }}"
service_account_file: "{{ gcp_eur_service_account_file }}"
scopes:
- https://www.googleapis.com/auth/compute
state: present
with_nested:
- "{{ site_code }}"
- "{{ nat_zone }}"
I've also tried the network param without using variables. I've tried without quotation marks. I've tried without the hyphen which is syntax incorrect and subtly changes the error to complain about a list not being a dict instead.
Any guidance as to what I'm getting wrong here would be greatly appreciated!
Environment details:
ansible-playbook --version
ansible-playbook 2.10.5
config file = /home/USER/ansible_aws/ansible.cfg
configured module search path = ['/home/USER/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
executable location = /usr/local/bin/ansible-playbook
python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
Disclaimer: I have no experience with GCP and nothing to correctly test this against. Meanwhile I have some experience in reading ansible documentation and testing module usage for correct parameters (at least at pure ansible level...)
As I first did, you probably flew over the documentation and its examples a little to fast ;). Meanwhile the specific parameter description is very clear:
network - dictionary
Hence a str is definitely not what is expected, as explicitely reported by your error message. We get more information in the comment:
[...] This field represents a link to a Network resource in GCP. It can be specified in two ways. First, you can place a dictionary with key selfLink and value of your resource's selfLink Alternatively, you can add register: name-of-resource to a gcp_compute_network task and then set this network field to {{ name-of-resource }}
If you look correctly at the examples, you'll see that they demonstrate the second scenario above (creating/registering a network to use the registered var directly in that parameter).
Taking for granted your own example in your question is using a ressource selfLink (have no clue if your current value looks correct or not...), I guess you should modify your definition as follows (abridged to network interfaces only):
network_interfaces:
- network:
selfLink: 'projects/{{ project }}/global/networks/{{ network }}'
access_configs:
- name: access_config
type: ONE_TO_ONE_NAT

Ansible add_host not being applied in Azure dynamic inventory

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")}}"

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".

Deploy artifacts to physical machine with Azure Pipelines

I have a project that is built with an azure pipeline, the artifacts are sent to both the File Share and Azure DevOps. A self hosted agent is currently used for the pipeline that builds my project. Now I want to deploy my artifacts on a physical computer in the network of my company. Artifacts are installation files that support "silent installation mode", so I can install everything by executing corresponding files with PowerShell.
My question is: What should I do to achieve my goal? I considered that I would simply create a release pipeline (I use Microsoft's classic UI editor for pipelines) and add the necessary tasks. In the end everything will look something like this:
Copy artifacts from the computer on which the product was built (A)
to the computer on which the product is to be installed (B).
Perform the installation of services that are required for the main
application.
Perform the installation from the main application. Is this the
right course of action or should I choose something else?
One more question. It is not entirely clear to me how I should divide the deployment stages, because: Step 1 is still done by the agent on the computer A. Steps 2 and 3 should already do another agent. Hence, an agent must be also present on the computer B (in total I need two agents, one for the build pipeline on the computer A and one on the target computer B). Is that correct?
Deploy artifacts to physical machine with Azure Pipelines
I think you are very close to the answer, but I am not sure whether the advice I provided is the best, because it is more a matter of taste, you can check my suggestions below.
We could achieve this by one agent on the computer A:
Use the Remote Copy task to copy the artifact to the computer B by the agent on the computer A.
Use the Powershell execute install files with command line arguments on remote computer the agent on the computer A.
In this case, we do not need to create a new agent on the computer B, and also no need to divide the deployment stages.
We have a web application that has a similar requirement, we need to include location specific files when we deploy our application to that location.
I'm not sure this is the best route, but we incorporated using Ansible in our deployment pipeline. Our Ansible playbook utilizes the hosts file which contains variables for the location and the jar version to be deployed.
Again, our situation is different, but the same should work.
For instance, you may be able to something like this...
Your Ansible inventory (hosts) file may look like this
[servers]
xxx.xxx.xxx.xxx
[all:vars]
app_name=mfgweb
env=qa
app_version=2020.03.4
*** If your artifact is a jar file
jar_group=com.somegroup
jar_name=some_jar <-- note: no extentsion
jar_version=2019.11.3
Your playbook may look something like this
---
- hosts: localhost
connection: local
tasks:
- name: Find old working directories
find:
paths: /tmp
patterns: 'ansible.*'
file_type: directory
age: 2d
register: tmp_dirs
- name: Cleanup old working directories
file:
path: "{{ item }}"
state: absent
loop: "{{ tmp_dirs.files | map(attribute='path') | list }}"
- name: Create working directory
tempfile:
state: directory
suffix: work
register: workdir
- name: Download artifact
maven_artifact:
group_id: "{{ jar_group }}"
artifact_id: "{{ jar_name }}"
version: "{{ jar_version }}"
repository_url: "{{ artifacts_url }}"
username: "{{ artifacts_user }}"
password: "{{ artifacts_password }}"
verify_checksum: never
dest: "{{ workdir.path }}/{{ app_name }}/"
This is just a sample, but you would be able to install required software in you playbook using yum and/or pip.

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

Resources