VMware vSphere Tags are a great tool to build a deep integration with other VMware or third-party products. Veeam already could use vSphere Tags as a backup object in Veeam Backup & Replication 8.0 Update 2b (Thanks to Jim Jones and Luciano Patrão for helping me out with this info). This article will show how a Veeam and vSphere Tag Integration with Ansible can be done. I would like to demonstrate at first what is possible with the Ansible standard community modules and then also the possibility to create a Veeam backup job with vSphere Tags as a backup object with my own Ansible Veeam module.
That vSphere Tags can be used for a very slick integration into Veeam Backup & Replication is not a new topic. One of my blogger fellows Falko Banaszak, for example, wrote a great blog post about that topic and I have also shown some examples in my Veeam PowerShell Webinar last year.
vSphere Tag management with Ansible
The Ansible community has created three VMware vSphere Ansible modules for Tag handling:
This Ansible playbook creates a set of new VMs and assigns a newly created vSphere Tag “DemoApp” of the Tag category “Protection”. The Tag category “Protection” already exists and the Tag “Default” is known.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
- name: Manage vSphere VM Tags
hosts: veeam
gather_facts: no
vars:
tag_category: Protection
tag_name: DemoApp
vm_cluster: cluster01
vm_datacenter: lab
vm_folder: /{{ vm_datacenter }}/vm/
vm_list:
- { name: 'DemoApp01', datastore: 'datastore1', network: 'DPortGroup1', cpu: '1', mem: '256' }
- { name: 'DemoApp02', datastore: 'datastore1', network: 'DPortGroup2', cpu: '1', mem: '256' }
tasks:
- name: Create new vSphere VMs
vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
datacenter: "{{ vm_datacenter }}"
cluster: "{{ vm_cluster }}"
folder: "{{ vm_folder }}"
name: "{{ item.name }}"
state: poweredoff
guest_id: centos64Guest
datastore: "{{ item.datastore }}"
disk:
- size_gb: 10
type: thin
hardware:
memory_mb: "{{ item.mem }}"
num_cpus: "{{ item.cpu }}"
scsi: paravirtual
boot_firmware: efi
networks:
- name: "{{ item.network }}"
device_type: vmxnet3
customvalues:
- key: "svga.vgaOnly"
Value: true
delegate_to: localhost
with_items: "{{ vm_list }}"
- name: Get category id from the given tag
vmware_tag_facts:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
register: tag_details
delegate_to: localhost
- name: Create Tag for '{{ tag_name }}'
vmware_tag:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
category_id: "{{ tag_details.tag_facts['Default']['tag_category_id'] }}"
tag_name: '{{ tag_name }}'
state: present
delegate_to: localhost
- name: Add Tag '{{ tag_name }}' to VMs
vmware_tag_manager:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
tag_names:
- "{{ tag_category }}:{{ tag_name }}"
object_name: "{{ item.name }}"
object_type: VirtualMachine
state: add
with_items: "{{ vm_list }}"
delegate_to: localhost
|
The result is two VMware vSphere VMs with the newly created tag assigned.

If you now create a Veeam backup job that uses the newly created Tag as an object, both VMs are gathered from the inventory and will be processed during the backup.

Create Veeam backup job with Ansible
But since my goal is to achieve the highest possible degree of automation, I would also like to have the Backup job created with Ansible. To achieve this, I have extended my Ansible Veeam module (also shipped as a Role on Ansible Galaxy). The module is now able to create new Veeam backup jobs with Tags as backup objects.
PowerShell code of the new veeam_backup module:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
#!powershell
# Copyright: (c) 2019, Markus Kraus <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
#AnsibleRequires -OSVersion 6.2
#AnsibleRequires -CSharpUtil Ansible.Basic
$spec = @{
options = @{
type = @{ type = "str"; choices = "vi", "hv"; default = "vi" }
entity = @{ type = "str"; choices = "tag", "vm"; default = "tag" }
tag = @{ type = "str" }
state = @{ type = "str"; choices = "absent", "present"; default = "present" }
name = @{ type = "str" }
repository = @{ type = "str" }
id = @{ type = "str" }
}
required_if = @(@("state", "present", @("type", "entity", "name", "repository")),
@("state", "absent", @("id")))
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
# Functions
Function Connect-VeeamServer {
try {
Add-PSSnapin -PassThru VeeamPSSnapIn -ErrorAction Stop | Out-Null
}
catch {
Fail-Json -obj @{} -message "Failed to load Veeam SnapIn on the target: $($_.Exception.Message)"
}
try {
Connect-VBRServer -Server localhost
}
catch {
Fail-Json -obj @{} -message "Failed to connect VBR Server on the target: $($_.Exception.Message)"
}
}
Function Disconnect-VeeamServer {
try {
Disconnect-VBRServer
}
catch {
Fail-Json -obj @{} -message "Failed to disconnect VBR Server on the target: $($_.Exception.Message)"
}
}
# Connect
Connect-VeeamServer
switch ( $module.Params.state) {
"present" { $Repositroy = Get-VBRBackupRepository -Name $module.Params.repository
switch ($module.Params.type) {
"vi" { switch ($module.Params.entity) {
"tag" { $Entity = Find-VBRViEntity -Tags | Where-Object {$_.Path -match "$($module.Params.tag)"} }
"vm" { Fail-Json -obj @{} -message "Type not yet implemented."}
Default { }
}
$Backup = Add-VBRViBackupJob -Name $module.Params.name -Entity $Entity -BackupRepository $Repositroy
$module.Result.changed = $true
$module.Result.id = $Backup.id
}
"hv" { Fail-Json -obj @{} -message "Type not yet implemented."
}
Default { }
}
}
"absent" { Fail-Json -obj @{} -message "State not yet implemented."
}
Default {}
}
# Disconnect
Disconnect-VeeamServer
# Return result
$module.ExitJson()
|
Example Playbook to add a new Veeam backup job:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
- name: Add new Backup Job
hosts: veeam
gather_facts: no
roles:
- veeam
vars:
query: "veeam_facts.veeam_backups[?id=='{{ my_backup.id }}']"
tasks:
- name: Create Backup Job
veeam_backup:
state: present
type: vi
entity: tag
tag: "Protection\\\\Default"
name: BackupJob01
repository: "Default Backup repository"
register: my_backup
- name: Get Veeam Facts
veeam_connection_facts:
register: my_facts
- name: Debug Veeam Backup Job Facts
debug:
var: my_facts | json_query(query)
|
End-to-end Veeam and vSphere Tag Integration with Ansible
Bothe examples combined in one playbook form an end-to-end Veeam and vSphere Tag Integration with Ansible. Deployment of new VMware vSphere VMs (our app), a new vSphere Tag that represents the new app and a new Veeam Backup Job that uses the vSphere Tag to protect the new app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
- name: Manage vSphere VM Tags with new Veeam Backup
hosts: veeam
gather_facts: no
roles:
- veeam
vars:
query: "veeam_facts.veeam_backups[?id=='{{ my_backup.id }}']"
tag_category: Protection
tag_name: DemoApp
vm_cluster: cluster01
vm_datacenter: lab
vm_folder: /{{ vm_datacenter }}/vm/
vm_list:
- { name: 'DemoApp01', datastore: 'datastore1', network: 'DPortGroup1', cpu: '1', mem: '256' }
- { name: 'DemoApp02', datastore: 'datastore1', network: 'DPortGroup2', cpu: '1', mem: '256' }
tasks:
- name: Create new vSphere VMs
vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
datacenter: "{{ vm_datacenter }}"
cluster: "{{ vm_cluster }}"
folder: "{{ vm_folder }}"
name: "{{ item.name }}"
state: poweredoff
guest_id: centos64Guest
datastore: "{{ item.datastore }}"
disk:
- size_gb: 10
type: thin
hardware:
memory_mb: "{{ item.mem }}"
num_cpus: "{{ item.cpu }}"
scsi: paravirtual
boot_firmware: efi
networks:
- name: "{{ item.network }}"
device_type: vmxnet3
customvalues:
- key: "svga.vgaOnly"
Value: true
delegate_to: localhost
with_items: "{{ vm_list }}"
- name: Get category id from the given tag
vmware_tag_facts:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
register: tag_details
delegate_to: localhost
- name: Create Tag for '{{ tag_name }}'
vmware_tag:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
category_id: "{{ tag_details.tag_facts['Default']['tag_category_id'] }}"
tag_name: '{{ tag_name }}'
state: present
delegate_to: localhost
- name: Add Tag '{{ tag_name }}' to VMs
vmware_tag_manager:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
tag_names:
- "{{ tag_category }}:{{ tag_name }}"
object_name: "{{ item.name }}"
object_type: VirtualMachine
state: add
with_items: "{{ vm_list }}"
delegate_to: localhost
- name: Create Backup Job
veeam_backup:
state: present
type: vi
entity: tag
tag: "{{ tag_category }}\\\\{{ tag_name }}"
name: "Backup_{{ tag_name }}"
repository: "Default Backup repository"
register: my_backup
- name: Get Veeam Facts
veeam_connection_facts:
register: my_facts
- name: Debug Veeam Backup Job Facts
debug:
var: my_facts | json_query(query)
|
The return of the vmware_tag_facts module is a bit strange if you only need to gather a Tag category id. I have done the workaround to gather the Tag category id of an existing, known Tag (new issue to initiate improvement of the module).
Like the other Ansible Veeam modules, the new veeam_backup module is still under development and offers plenty of room for improvement.
Feel free to open a GitHub Issue, write a comment to this blog post or open a new pull request if you like to contribute to the project.