We created a JSON list that specifies a number of things for different server types. One JSON list provides Linux disk specs. One JSON list provides Windows disk specs.
fs_spec: [
linux apache web server servers
{ profile: apache , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: apache , name: sites , device: b, size: 32, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /sites
linux commvault media servers
{ profile: commvault , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: commvault , name: ddb , device: b, size: 512, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /ddb
{ profile: commvault , name: index , device: c, size: 512, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 1, owner: root, group: root, perms: 0755 }, # /index
linux docker container servers
{ profile: docker , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: docker , name: containers , device: b, size: 100, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /containers
linux general purpose servers
{ profile: general , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
linux CBS INFO servers
{ profile: cbsinfo , name: system , device: a, size: 80, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: cbsinfo , name: apps , device: b, size: 64, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: oranist, group: dba, perms: 0755 }, # /apps
{ profile: cbsinfo , name: ftp , device: c, size: 64, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 1, owner: ftp, group: ftp, perms: 0750 }, # /ftp
linux MySQL database servers
{ profile: mysql , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: mysql , name: tmp , device: b, size: 100, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 1, owner: root, group: root, perms: 0755 }, # /tmp
{ profile: mysql , name: apps , device: c, size: 512, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: oranist, group: dba, perms: 0755 }, # /apps
{ profile: mysql , name: archive , device: d, size: 512, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 1, owner: oranist, group: dba, perms: 0755 }, # /archive
{ profile: mysql , name: mydata , device: e, size: 512, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 0, owner: mysql, group: dba, perms: 0755 }, # /mydata
{ profile: mysql , name: mybackups , device: f, size: 1024, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 1, owner: mysql, group: dba, perms: 0755 }, # /mybackups
linux nginx web server servers
{ profile: nginx , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: nginx , name: sites , device: b, size: 32, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /sites
linux Oracle database servers
{ profile: oracle , name: system , device: a, size: 80, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: oracle , name: swap , device: b, size: 64, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 1, owner: root, group: root, perms: 0755 }, # swap
{ profile: oracle , name: tmp , device: c, size: 64, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 2, owner: root, group: root, perms: 0755 }, # /tmp
{ profile: oracle , name: apps , device: d, size: 192, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: oranist, group: dba, perms: 0755 }, # /apps
{ profile: oracle , name: external , device: e, size: 32, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 1, owner: oranist, group: dba, perms: 0755 }, # /external
{ profile: oracle , name: archive , device: f, size: 128, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 2, owner: oranist, group: dba, perms: 0755 }, # /archive
{ profile: oracle , name: oradata , device: g, size: 1024, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 0, owner: oranist, group: dba, perms: 0750 }, # /oradata
{ profile: oracle , name: orafra , device: h, size: 1024, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 1, owner: oranist, group: dba, perms: 0750 }, # /orafra
linux Oracle + MySQL database servers
{ profile: oracle_mysql , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: oracle_mysql , name: swap , device: b, size: 64, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 1, owner: root, group: root, perms: 0755 }, # swap
{ profile: oracle_mysql , name: tmp , device: c, size: 64, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 2, owner: root, group: root, perms: 0755 }, # /tmp
{ profile: oracle_mysql , name: apps , device: d, size: 192, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: oranist, group: dba, perms: 0755 }, # /apps
{ profile: oracle_mysql , name: external , device: e, size: 50, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 1, owner: oranist, group: dba, perms: 0755 }, # /external
{ profile: oracle_mysql , name: archive , device: d, size: 512, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 2, owner: oranist, group: dba, perms: 0755 }, # /archive
{ profile: oracle_mysql , name: oradata , device: f, size: 768, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 0, owner: oranist, group: dba, perms: 0750 }, # /oradata
{ profile: oracle_mysql , name: orafra , device: g, size: 1024, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 1, owner: oranist, group: dba, perms: 0750 }, # /orafra
{ profile: oracle_mysql , name: mydata , device: h, size: 640, type: thin, ctrl_type: paravirtual, ctrl: 3, unit: 0, owner: mysql, group: dba, perms: 0755 }, # /mydata
{ profile: oracle_mysql , name: mybackups , device: i, size: 960, type: thin, ctrl_type: paravirtual, ctrl: 3, unit: 1, owner: mysql, group: dba, perms: 0755 }, # /mybackups
linux tomcat application servers
{ profile: tomcat , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: tomcat , name: sites , device: b, size: 32, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /sites
linux ASD tomcat/weblogic application servers
{ profile: tomweb , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: tomweb , name: u01 , device: b, size: 100, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /u01
{ profile: tomweb , name: data , device: c, size: 100, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 0, owner: root, group: root, perms: 0755 }, # /data
linux ASD APPNODE (tomcat/weblogic) application servers
{ profile: appnode , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: appnode , name: u01 , device: b, size: 100, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /u01
{ profile: appnode , name: data , device: c, size: 100, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 0, owner: root, group: root, perms: 0755 }, # /data
{ profile: appnode , name: apps , device: d, size: 100, type: thin, ctrl_type: paravirtual, ctrl: 3, unit: 0, owner: root, group: root, perms: 0755 }, # /apps
linux ASD subversion/jira
{ profile: subversion , name: system , device: a, size: 75, type: thin, ctrl_type: paravirtual, ctrl: 0, unit: 0, owner: root, group: root, perms: 0755 }, # /
{ profile: subversion , name: u01 , device: b, size: 20, type: thin, ctrl_type: paravirtual, ctrl: 1, unit: 0, owner: root, group: root, perms: 0755 }, # /u01
{ profile: subversion , name: data , device: c, size: 200, type: thin, ctrl_type: paravirtual, ctrl: 2, unit: 0, owner: root, group: root, perms: 0755 }, # /data
all preceding lines must end with a comma - makes it easier to copy sections above to create new server roles - json_query ignores this
{ the: end }
]
use [0:-1] to drop off the last line “{ the: end }”
valid_linux_types: “{{ fs_spec[0:-1] | map(attribute=‘profile’) | sort | unique }}”
fs_spec_windows: [
Windows commvault media servers
{ profile: commvault , disk_number: 0 , name: system , device: c, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 0, unit: 0, block: 4096 }, # OS/Sys
{ profile: commvault , disk_number: 1 , name: Index , device: e, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 1, unit: 0, block: 4096 }, # Index
{ profile: commvault , disk_number: 2 , name: DDB , device: f, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 2, unit: 0, block: 4096 }, # DDB
Windows general purpose servers
{ profile: general , disk_number: 0 , name: system , device: c, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 0, unit: 0, block: 4096 }, # OS/Sys
{ profile: general , disk_number: 1 , name: Data1 , device: e, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 1, unit: 0, block: 4096 }, # Data1
Windows IIS web servers
{ profile: iis , disk_number: 0 , name: system , device: c, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 0, unit: 0, block: 4096 }, # OS/Sys
{ profile: iis , disk_number: 1 , name: Data1 , device: e, size: 300, type: thin, ctrl_type: lsilogicsas, ctrl: 1, unit: 0, block: 4096 }, # Data1
Windows MS SQL servers
{ profile: mssql , disk_number: 0 , name: system , device: c, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 0, unit: 0, block: 4096 }, # OS/Sys
{ profile: mssql , disk_number: 1 , name: Data1 , device: e, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 0, unit: 1, block: 65536 }, # Data1
{ profile: mssql , disk_number: 2 , name: Data2 , device: f, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 0, unit: 2, block: 65536 }, # Data2
{ profile: mssql , disk_number: 3 , name: Backup , device: g, size: 100, type: thin, ctrl_type: lsilogicsas, ctrl: 1, unit: 0, block: 4096 }, # Backup
{ profile: mssql , disk_number: 4 , name: Index , device: i, size: 5, type: thin, ctrl_type: lsilogicsas, ctrl: 1, unit: 1, block: 65536 }, # Index
{ profile: mssql , disk_number: 5 , name: Log , device: l, size: 50, type: thin, ctrl_type: lsilogicsas, ctrl: 2, unit: 0, block: 65536 }, # Log
{ profile: mssql , disk_number: 6 , name: SQL , device: p, size: 75, type: thin, ctrl_type: lsilogicsas, ctrl: 2, unit: 1, block: 4096 }, # SQL Program
{ profile: mssql , disk_number: 7 , name: TempDB , device: t, size: 50, type: thin, ctrl_type: lsilogicsas, ctrl: 3, unit: 0, block: 65536 }, # TempDB
all preceding lines must end with a comma - makes it easier to copy sections above to create new server roles - json_query ignores this
{ the: end }
]
use [0:-1] to drop off the last line “{ the: end }”
valid_windows_types: “{{ fs_spec_windows[0:-1] | map(attribute=‘profile’) | sort | unique }}”
We build our VMs from templates that all have a single system disk. Note the template param references an os_type var. We have templates for windows2016, windows2019, redhat7, redhat8, redhat9, ubuntu20, ubuntu22, etc. Our service portal lets the user choose which OS they want and that sends is one of these names as the template to source for the new VM.
NOTE that we create the VM “poweredoff”. We don’t power it on until we have added the disks it will need. We do that so that the first boot from the template already has all the raw storage attached to it.
- name: create the guest vm using template
community.vmware.vmware_guest:
validate_certs: no
hostname: “{{ vcenter[location|lower].vc }}”
datacenter: “{{ vcenter[location|lower].dc }}”
cluster: “{{ vcenter[location|lower].cl }}”
name: “{{ vm_guest_name | lower }}”
state: poweredoff
template: “{{ os_type }}”
folder: “{{ esx_folder }}”
datastore: “{{ vcenter[location|lower].ds }}”
hardware:
hotadd_cpu: yes
hotadd_memory: yes
memory_mb: “{{ vm_spec[vm_size].ram }}”
num_cpus: “{{ vm_spec[vm_size].cpu }}”
networks:
- name: “VLAN_{{ vlan }}”
type: dhcp
start_connected: yes
connected: yes
wait_for_ip_address: no
delegate_to: localhost
register: newvm
We use a json_query filter to extract the specific items from the list that we need for a given server type. This task sets fs_list to just the list of disks associated with a given server type. Note the start of the jinja template selects which JSON list to source. We get a param from our service portal for fs_type that we use to filter the JSON list via json_query.
- name: collect disk list for {{ fs_type }} machine
set_fact:
fs_list: “{{ (fs_spec if my_family != ‘windows’ else fs_spec_windows) | json_query(‘[?profile=='+fs_type+'
]’) }}”
We then take our fs_list and eliminate the system disk (since that comes with the template). We also create an empty disk list that will hold the final disk list we want to add.
- name: Initialize disk lists
set_fact:
type_list: “{{ (fs_spec if my_family != ‘windows’ else fs_spec_windows) | json_query(‘[?profile=='+fs_type+'
&& name!=system
]’) }}”
disk_list:
We then massage a “additional disks” list into a disk param we can send to VMware.
- name: Build vmware_guest_disk list from filesystem list
set_fact:
disk_list: “{{ disk_list + this_list }}”
loop: “{{ type_list }}”
vars:
this_list: [ { size_gb: “{{ item.size }}”, type: “{{ item.type }}”, datastore: “{{ vcenter[location|lower].ds }}”, scsi_type: “{{ item.ctrl_type }}”, scsi_controller: “{{ item.ctrl }}”, unit_number: “{{ item.unit }}” } ]
We then call vmware_guest_disk to add the disks (on the condition the system has more than one).
- name: Add disk(s) to {{ vm_guest_name | lower }}
community.vmware.vmware_guest_disk:
datacenter: “{{ vcenter[location|lower].dc }}”
hostname: “{{ vcenter[location|lower].vc }}”
name: “{{ vm_guest_name | lower }}”
validate_certs: no
disk: “{{ disk_list }}”
when: (disk_list|length) > 0
Lastly we power on the new VM. When the VM boots for the first time and does its device discovery it will find all the raw disks needed for the type of server we are standing up.
- name: Power Up New Machine ({{ vm_guest_name | lower }})
community.vmware.vmware_guest:
hostname: “{{ vcenter[location|lower].vc }}”
datacenter: “{{ vcenter[location|lower].dc }}”
cluster: “{{ vcenter[location|lower].cl }}”
validate_certs: no
name: “{{ vm_guest_name | lower }}”
state: poweredon
delegate_to: localhost
Every server we stand up on VMware goes through these tasks. We have more tasks that add tags, etc, but this is the basic framework.
The disk spec JSON lists are in a vars file. We easily can add more disk configurations by expanding the JSON lists. The ‘profile’ is the key in the JSON lists that we match to ‘os_type’ we receive from our service portal. If we want to create a new server type with a different disk layout then we offer a new os_type value in the service portal and defined a new disk list with a new profile key in the JSON list. We do this for resource settings too (CPU, RAM), and all the other parameters we use in our vmware_guest task.
Our philosophy is to make absolutely everything parameter driven. This means the tasks never have to change. The source dictionaries and JSON lists that we source for our parameters are what we change, along with the corresponding values we offer in our service portal. We let users choose the VLAN, CPU/RAM, OS, server purpose, location (VMware instance), and backup strategy.
Walter