Allow specifying OS image on a per-Azure VMSS basis (#439)

Adds support for specifying the OS image to be used at an individual
VMSS level for a multiple VMSS Azure cluster. This helps in testing some
heterogenous cluster scenarios.
diff --git a/ansible/roles/azure/tasks/create_multiple_vmss.yml b/ansible/roles/azure/tasks/create_multiple_vmss.yml
index a84f9c6..28ccce8 100644
--- a/ansible/roles/azure/tasks/create_multiple_vmss.yml
+++ b/ansible/roles/azure/tasks/create_multiple_vmss.yml
@@ -30,21 +30,21 @@
     file: "{{ deploy_path }}/conf/azure_multiple_vmss_vars.yml"
     name: azure_multiple_vmss_vars
 
-- name: "Create image plan related facts"
-  set_fact:
-    azure_image_plan_name: "{{ azure_image_plan.split('|')[0] }}"
-    azure_image_plan_dict:
-      name: "{{ azure_image_plan.split('|')[0] }}"
-      product: "{{ azure_image_plan.split('|')[1] }}"
-      publisher: "{{ azure_image_plan.split('|')[2] }}"
-
 - name: Create Scale Set
   vars:
-    image_offer: "{{ azure_image_reference.split('|')[0] }}"
-    image_publisher: "{{ azure_image_reference.split('|')[1] }}"
-    image_sku: "{{ azure_image_reference.split('|')[2] }}"
-    image_version: "{{ azure_image_reference.split('|')[3] }}"
-    image_id: "{{ azure_image_reference.split('|')[4] }}"
+    item_image_plan: "{{ item.azure_image_plan | default(azure_image_plan) }}"
+    azure_image_plan_name: "{{ item_image_plan.split('|')[0] }}"
+    item_azure_image_reference: "{{ item.azure_image_reference | default(azure_image_reference) }}"
+    item_azure_image_cloud_init_file: "{{ item.azure_image_cloud_init_file | default(azure_image_cloud_init_file) }}"
+    azure_image_plan_dict:
+      name: "{{ item_image_plan.split('|')[0] }}"
+      product: "{{ item_image_plan.split('|')[1] }}"
+      publisher: "{{ item_image_plan.split('|')[2] }}"
+    image_offer: "{{ item_azure_image_reference.split('|')[0] }}"
+    image_publisher: "{{ item_azure_image_reference.split('|')[1] }}"
+    image_sku: "{{ item_azure_image_reference.split('|')[2] }}"
+    image_version: "{{ item_azure_image_reference.split('|')[3] }}"
+    image_id: "{{ item_azure_image_reference.split('|')[4] }}"
     accnet_capable: "{{ True if item.sku in accnet_capable_skus else False }}"
     osdisk_sku: "{{ 'Premium_LRS' if item.sku in premiumio_capable_skus else 'Standard_LRS' }}"
   azure.azcollection.azure_rm_virtualmachinescaleset:
@@ -81,7 +81,7 @@
         {%- set _ = data_disks.append({'lun': lun, 'disk_size_gb': item.data_disk_size_gb, 'managed_disk_type': item.data_disk_sku, 'caching': item.data_disk_caching|default('ReadOnly') }) -%}
       {%- endfor -%}
       {{ data_disks }}
-    custom_data: "{{ lookup('file', azure_image_cloud_init_file) if azure_image_cloud_init_file }}"
+    custom_data: "{{ lookup('file', item_azure_image_cloud_init_file) if item_azure_image_cloud_init_file }}"
   with_items:
     - "{{ azure_multiple_vmss_vars.vars_list }}"
   register: _create_clusters
diff --git a/ansible/roles/azure/tasks/create_optional_proxy.yml b/ansible/roles/azure/tasks/create_optional_proxy.yml
index dae011b..090935d 100644
--- a/ansible/roles/azure/tasks/create_optional_proxy.yml
+++ b/ansible/roles/azure/tasks/create_optional_proxy.yml
@@ -60,16 +60,13 @@
     enable_accelerated_networking: "{{ True if azure_proxy_host_vm_sku in accnet_capable_skus else False }}"
   when: azure_proxy_host is defined and azure_proxy_host and azure_proxy_host != None
 
-- name: "Create image plan related facts"
-  set_fact:
+- name: Create azure proxy virtual machine
+  vars:
     azure_proxy_image_plan_name: "{{ azure_proxy_image_plan.split('|')[0] }}"
     azure_proxy_image_plan_dict:
       name: "{{ azure_proxy_image_plan.split('|')[0] }}"
       product: "{{ azure_proxy_image_plan.split('|')[1] }}"
       publisher: "{{ azure_proxy_image_plan.split('|')[2] }}"
-
-- name: Create azure proxy virtual machine
-  vars:
     image_offer: "{{ azure_proxy_image_reference.split('|')[0] }}"
     image_publisher: "{{ azure_proxy_image_reference.split('|')[1] }}"
     image_sku: "{{ azure_proxy_image_reference.split('|')[2] }}"
diff --git a/ansible/roles/azure/tasks/create_vmss.yml b/ansible/roles/azure/tasks/create_vmss.yml
index e805377..3c1d9f4 100644
--- a/ansible/roles/azure/tasks/create_vmss.yml
+++ b/ansible/roles/azure/tasks/create_vmss.yml
@@ -33,14 +33,6 @@
     luns_dict: "{{ luns_dict | default ([]) + [{ 'lun': item, 'disk_size_gb': disk_size_gb, 'managed_disk_type': data_disk_sku, 'caching': data_disk_caching } ] }}"
   with_sequence: start=0 end={{ data_disk_count-1 if data_disk_count > 0 else 0 }}
 
-- name: "Create image plan related facts"
-  set_fact:
-    azure_image_plan_name: "{{ azure_image_plan.split('|')[0] }}"
-    azure_image_plan_dict:
-      name: "{{ azure_image_plan.split('|')[0] }}"
-      product: "{{ azure_image_plan.split('|')[1] }}"
-      publisher: "{{ azure_image_plan.split('|')[2] }}"
-
 - name: Set single placement group to correct value
   set_fact:
     single_placement_group: False
@@ -48,6 +40,11 @@
 
 - name: Create Scale Set
   vars:
+    azure_image_plan_name: "{{ azure_image_plan.split('|')[0] }}"
+    azure_image_plan_dict:
+      name: "{{ azure_image_plan.split('|')[0] }}"
+      product: "{{ azure_image_plan.split('|')[1] }}"
+      publisher: "{{ azure_image_plan.split('|')[2] }}"
     image_offer: "{{ azure_image_reference.split('|')[0] }}"
     image_publisher: "{{ azure_image_reference.split('|')[1] }}"
     image_sku: "{{ azure_image_reference.split('|')[2] }}"
diff --git a/docs/azure-multiple-vmss.md b/docs/azure-multiple-vmss.md
index 7f9db02..22893b5 100644
--- a/docs/azure-multiple-vmss.md
+++ b/docs/azure-multiple-vmss.md
@@ -22,6 +22,9 @@
 |-----------|------------------------|---------|-------------|
 | `name_suffix` | Required | - | The name of each VMSS is constructed by concatenating the Muchos cluster name with this string. As an example, if your Muchos cluster is called `test`, and this field has a value of `ldr`, then the VMSS is created with a name `test-ldr`|
 | `sku` | Required | - | A string identifier specifying the Azure VM size. Refer to the [Azure documentation](https://docs.microsoft.com/en-us/azure/virtual-machines/dv3-dsv3-series) to lookup these strings. An example VM size is `Standard_D32s_v3` for a 32-vCPU [Dsv3](https://docs.microsoft.com/en-us/azure/virtual-machines/dv3-dsv3-series#dsv3-series) VM|
+| `azure_image_reference` | Optional | - | If, for whatever reason, you need to use a different Azure VM image for a specific VMSS, please specify the image details in the same format as documented in [Azure image reference](./azure-image-reference.md) |
+| `azure_image_plan` | Optional | - | If, for whatever reason, you need to use a different Azure VM image for a specific VMSS, and if that image needs purchase plan information to be specified, please specify the plan information the same format as documented in [Azure image reference](./azure-image-reference.md) |
+| `azure_image_cloud_init_file` | Optional | - | If, for whatever reason, you need to use a different Azure VM image for a specific VMSS, and if that image needs a custom cloud init file, please specify the cloud init file name as documented in [Azure image reference](./azure-image-reference.md) |
 | `vmss_priority` | Optional | None | If this not specified at each VM level, the value for `vmss_priority` from the `azure` section in [muchos.props](../conf/muchos.props.example) is used | This can be set to `None`, for regular VMs, or `Spot` for [Spot VMs](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/spot-vms).|
 | `perf_profile` | Required | - | A string identifying a corresponding performance profile configuration section in muchos.props which contains perf profile parameters |
 | `azure_disk_device_path`| Optional | If not specified, the corresponding `azure_disk_device_path` value from the `azure` section in [muchos.props](../conf/muchos.props.example) is used | This is a device path used to enumerate attached SCSI or NVME disks to use for persistent local storage |