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.
![Ansible Veeam Modules Preview - Concept](/2019/07/09/ansible-veeam-modules-preview/images/Ansible-Veeam-WinRM-v2-1024x477.png)
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.
![Ansible Veeam Modules Preview - Module Doc](/2019/07/09/ansible-veeam-modules-preview/images/grafik-1024x770.png)
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:
![Ansible Veeam Modules Preview - Add Credentials](/2019/07/09/ansible-veeam-modules-preview/images/VeeamAddCred-1024x601.png)
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:
![Ansible Veeam Modules Preview - Add VMware ESXi Server](/2019/07/09/ansible-veeam-modules-preview/images/VeeamAddEsxi-1024x691.png)
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.