Creating an initial set of Ansible Veeam modules has been my goal for a while. It was clear that this had to be possible with the Ansible concept of Windows modules written in Powershell. A Veeam Backup & Replication server needs to be a Windows Server and the Veeam console is shipped with a feature-rich PowerShell SnapIn, which can be used by the Ansible Windows modules written in Powershell.
So my basic concept is an Ansible PowerShell module running on the Veeam server. As the Veeam Backup & Replication server needs to be a Windows Server, it will be accessed via WinRM by Ansible. I have already written about Ansible and WinRM in my previous blog post: Veeam unattended installation with Ansible
In the current version of the modules, no remote connection to the Veeam Server via PowerShell is planned. So the Veeam Backup & Replication server needs to be accessed via WinRM. If an additional hop via a Windows server with only the Veeam Console installed is required, I would be glad about feedback.
Ansible Veeam Modules
So far I have created just a small set of modules to validate my concept. The goal that I have set myself was to add a VMware ESXi standalone host to the Veeam configuration.
Caution, now follows a lot of code!
Veeam Facts
This was the first Ansible Veeam Module I have created. It’s a read-only module that gathers configuration details about the Veeam Backup & Replication server. The return can be written into a variable and used by other Ansible Veeam Modules.
Please check the Github Project for the detailed syntax.
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#!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
$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
# Create a new result object
$result = @{
changed = $false
veeam_facts = @{
veeam_connection = @()
veeam_repositories = @()
veeam_servers = @()
veeam_credentials = @()
}
}
# Get Veeam Connection
try {
$Connection = Get-VBRServerSession
} catch {
Fail-Json -obj $result -message "Failed to get connection details on the target: $($_.Exception.Message)"
}
# Get Veeam Server
try {
[Array]$ServerList = Get-VBRServer
}
catch {
Fail-Json -obj $result -message "Failed to get server details on the target: $($_.Exception.Message)"
}
# Get Veeam Repositories
try {
[Array]$RepoList = Get-VBRBackupRepository | Where-Object {$_.Type -ne "SanSnapshotOnly"}
}
catch {
Fail-Json -obj $result -message "Failed to get repository details on the target: $($_.Exception.Message)"
}
# Get Veeam Credentials
try {
[Array]$CredList = Get-VBRCredentials
}
catch {
Fail-Json -obj $result -message "Failed to get credential details on the target: $($_.Exception.Message)"
}
# Create result
$connection_info = @{}
$connection_info["user"] = $Connection.user
$connection_info["server"] = $Connection.server
$connection_info["port"] = $Connection.port
$result.veeam_facts.veeam_connection += $connection_info
foreach ($Repo in $RepoList) {
$repo_info = @{}
$repo_info["name"] = $repo.name
$repo_info["type"] = $repo.typedisplay
$repo_info["host"] = $($ServerList | Where-Object {$_.Id -eq $repo.HostId}).name
$repo_info["friendlypath"] = $repo.friendlypath
$repo_info["description"] = $repo.description
$result.veeam_facts.veeam_repositories += $repo_info
}
foreach ($Server in $ServerList) {
$server_info = @{}
$server_info["id"] = $server.id
$server_info["name"] = $server.name
$server_info["description"] = $server.description
$server_info["type"] = $server.info.typedescription
$result.veeam_facts.veeam_servers += $server_info
}
foreach ($Cred in $CredList) {
$cred_info = @{}
$cred_info["id"] = $cred.id
$cred_info["name"] = $cred.name
$cred_info["username"] = $cred.username
$cred_info["encryptedpassword"] = $cred.encryptedpassword
$cred_info["description"] = $cred.description
$result.veeam_facts.veeam_credentials += $cred_info
}
# Disconnect
Disconnect-VeeamServer
# Return result
Exit-Json -obj $result
|
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
|
ok: [10.0.2.16] => {
"my_facts": {
"changed": false,
"failed": false,
"veeam_facts": {
"veeam_connection": [
{
"port": 9392,
"server": "localhost",
"user": "VEEAM01\\Administrator"
}
],
"veeam_credentials": [
{
"description": "Lab User for Standalone Host",
"encryptedpassword": null,
"id": "ae0fa0f8-d0ed-4014-9e0c-b84d56bc9084",
"name": "root",
"username": "root"
}
],
"veeam_repositories": [
{
"description": "Created by Veeam Backup",
"friendlypath": "C:\\Backup",
"host": "Veeam01",
"name": "Default Backup Repository",
"type": "Windows"
}
],
"veeam_servers": [
{
"description": "Backup server",
"id": "6745a759-2205-4cd2-b172-8ec8f7e60ef8",
"name": "Veeam01",
"type": "Microsoft Windows Server"
},
{
"description": "Created by Powershell at 25.06.2019 22:39:24.",
"id": "aedd0693-657c-4384-9e53-cd6bb605a637",
"name": "192.168.234.101",
"type": "VMware ESXi Server"
}
]
}
}
}
|
A simple Ansible playbook that returns all veeam server facts looks like that:
1
2
3
4
5
6
7
8
9
10
|
- name: Get all VBR Facts
hosts: veeam
gather_facts: no
tasks:
- name: Get Veeam Facts
veeam_connection_facts:
register: my_facts
- name: Debug Veeam Facts
debug:
var: my_facts
|
Veeam Credentials
The next step towards manage servers in Veeam Backup & Replication is adding the required credentials. The veeam_credential module is able to add and remove credentials of the types Windows, Linux and Standard. The return of the add method (state: present) is the ID of the newly created credentials. This ID can be used by other Ansible Veeam Modules.
In addition to the PowerShell script, all Ansible Windows Modules do have a python “script” that contains the documentation of the module.
Please check the Github Project for the detailed syntax.
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
|
#!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 = "windows", "linux", "standard"; default = "standard" }
username = @{ type = "str" }
password = @{ type = "str" }
state = @{ type = "str"; choices = "absent", "present"; default = "present" }
description = @{ type = "str"; default = "Created by Ansible"}
id = @{ type = "str"}
}
required_if = @(@("state", "present", @("username", "password")),
@("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" { if ($module.Params.type -eq "standard") {
$Cred = Add-VBRCredentials -User $module.Params.username -Password "$($module.Params.password)" -Description "$($module.Params.description)"
}
else {
$Cred = Add-VBRCredentials -Type $module.Params.type -User $module.Params.username -Password "$($module.Params.password)" -Description "$($module.Params.description)"
}
$module.Result.changed = $true
$module.Result.id = $Cred.id
}
"absent" { $RemoveCred = Get-VBRCredentials | Where-Object {$_.id -eq $module.Params.id}
$Cred = Remove-VBRCredentials -Credential $RemoveCred
$module.Result.changed = $true
}
Default {}
}
# Disconnect
Disconnect-VeeamServer
# Return result
$module.ExitJson()
|
1
2
3
4
5
6
7
|
ok: [10.0.2.16] => {
"my_cred": {
"changed": true,
"failed": false,
"id": "86a42430-5756-4921-9d06-fd1d56599741"
}
}
|
A simple playbook that creates new Veeam Server Credentials looks like that:
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
|
- name: Add new Credentials to VBR Server
hosts: veeam
gather_facts: no
vars:
query: "veeam_facts.veeam_credentials[?id=='{{ my_cred.id }}']"
my_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
tasks:
- name: Add Credential
veeam_credential:
state: present
type: windows
username: Administrator
password: "{{ my_password }}"
description: My dummy description
register: my_cred
- name: Debug Veeam Credentials
debug:
var: my_cred
- name: Get Veeam Facts
veeam_connection_facts:
register: my_facts
- name: Debug Veeam Credential Facts
debug:
var: my_facts | json_query(query)
- name: Remove Credential
veeam_credential:
state: absent
id: "{{ my_cred.id }}"
|
Veeam Servers
The next part of my preview is an Ansible module that is able to add servers to the Veeam Backup & Replication configuration. A server in Veeam jargon can be for example a VMware ESXi, a VMware vCenter or a Windows server (more server types). The initial version of the Ansible module can only add VMware ESXi Servers.
Please check the Github Project for the detailed syntax.
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
|
#!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 = "esxi", "vcenter", "windows"; default = "esxi" }
credential_id = @{ type = "str" }
state = @{ type = "str"; choices = "absent", "present"; default = "present" }
name = @{ type = "str" }
id = @{ type = "str" }
}
required_if = @(@("state", "present", @("type", "name", "credential_id")),
@("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" { $Cred = Get-VBRCredentials | Where-Object {$_.id -eq $module.Params.credential_id}
switch ($module.Params.type) {
"esxi" { $Server = Add-VBRESXi –Name $module.Params.name -Credentials $Cred
$module.Result.changed = $true
$module.Result.id = $Server.id
}
"vcenter" {Fail-Json -obj @{} -message "Type not yet implemented."
}
"windows" {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()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
ok: [10.0.2.16] => {
"my_facts.veeam_facts.veeam_servers": [
{
"description": "Backup server",
"id": "6745a759-2205-4cd2-b172-8ec8f7e60ef8",
"name": "Veeam01",
"type": "Microsoft Windows Server"
},
{
"description": "Created by Powershell at 25.06.2019 22:39:24.",
"id": "aedd0693-657c-4384-9e53-cd6bb605a637",
"name": "192.168.234.101",
"type": "VMware ESXi Server"
}
]
}
|
A simple playbook that executes all required tasks to add a new VMware ESXi Server to the Veeam Backup & Replication configuration looks like that:
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
|
- name: Add ESXi Host to VBR Server
hosts: veeam
gather_facts: no
vars:
root_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
tasks:
- name: Add root credential
veeam_credential:
state: present
type: standard
username: root
password: "{{ root_password }}"
description: "Lab User for Standalone Host"
register: root_cred
- name: Debug root credential
debug:
var: root_cred
- name: Add esxi server
veeam_server:
state: present
type: esxi
credential_id: "{{ root_cred.id }}"
name: 192.168.234.101
register: esxi_server
- name: Get Veeam Facts
veeam_connection_facts:
register: my_facts
- name: Debug Veeam Servers from Facts
debug:
var: my_facts.veeam_facts.veeam_servers
|
GitHub Project
The Ansible Veeam Modules and example playbooks are available via GitHub. The source code state at the publishing date of this blog post is a dedicated release number:
0.1 - Initial Release
Feel free to contribute to the project by opening an issue or creating a pull request.