-
Notifications
You must be signed in to change notification settings - Fork 8
/
Installer.ps1
456 lines (411 loc) · 15.5 KB
/
Installer.ps1
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
<#
.SYNOPSIS
Installer script for VeeamNotify.
.DESCRIPTION
Installs VeeamNotify from one of the following:
1) Latest release;
2) Latest prerelease;
3) Specific version;
4) A named branch.
This script can also optionally launch a deployment script to apply the VeeamNotify configuration to all or selected Veeam jobs. You will be prompted for this after installation.
.PARAMETER Latest
Choose between "Release" or "Prerelease" to install the latest release or prerelease.
.PARAMETER Version
Specify a version to install (e.g. 'v1.0').
.PARAMETER Branch
Specify a branch name to install from. Useful for testing.
.PARAMETER NonInteractive
Switch for noninteractive installation. No prompts to choose versions or configurations will appear when specified, and one of the above parameters must also be specified.
.PARAMETER InstallParentPath
Path to Telegraf destination directory. Defaults to 'C:\VeeamScripts'.
.INPUTS
None.
.OUTPUTS
None.
.EXAMPLE
PS> Installer.ps1
.EXAMPLE
PS> Installer.ps1 -Latest release
.EXAMPLE
PS> Installer.ps1 -Version 'v1.0' -NonInteractive
.NOTES
Authors: tigattack, philenst
.LINK
https://github.com/tigattack/VeeamNotify/wiki
#>
#Requires -RunAsAdministrator
[CmdletBinding(DefaultParameterSetName='None')]
param(
[Parameter(ParameterSetName = 'Version', Position = 0, Mandatory = $true)]
# Built-in parameter validation disabled - See https://github.com/tigattack/VeeamNotify/issues/50
# [ValidatePattern('^v(\d+\.)?(\d+\.)?(\*|\d+)$')]
[String]$Version,
[Parameter(ParameterSetName = 'Release', Position = 0, Mandatory = $true)]
# Built-in parameter validation disabled - See https://github.com/tigattack/VeeamNotify/issues/50
# [ValidateSet('Release', 'Prerelease')]
[String]$Latest,
[Parameter(ParameterSetName = 'Branch', Position = 0, Mandatory = $true)]
[String]$Branch,
[Parameter(ParameterSetName = 'Version', Position = 1)]
[Parameter(ParameterSetName = 'Release', Position = 1)]
[Parameter(ParameterSetName = 'Branch', Position = 1)]
[String]$InstallParentPath = 'C:\VeeamScripts',
[Parameter(ParameterSetName = 'Version', Position = 2)]
[Parameter(ParameterSetName = 'Release', Position = 2)]
[Parameter(ParameterSetName = 'Branch', Position = 2)]
[Switch]$NonInteractive
)
# Prepare variables
$project = 'VeeamNotify'
$ErrorActionPreference = 'Stop'
Write-Output @'
#######################################
# #
# VeeamNotify Installer #
# #
#######################################
'@
# Check if this project is already installed and if so, exit
if (Test-Path "$InstallParentPath\$project\resources\version.txt") {
$installedVersion = (Get-Content -Raw "$InstallParentPath\$project\resources\version.txt").Trim()
Write-Output "`n$project ($installedVersion) is already installed. This script cannot update an existing installation."
Write-Output "Please manually update or delete/rename the existing installation and retry.`n`n"
exit 1
}
elseif ((Test-Path "$InstallParentPath\$project") -and (Get-ChildItem "$InstallParentPath\$project").Count -gt 0) {
"`nThe install path ($InstallParentPath\$project) already exists with children, " `
+ "but an existing installation couldn't be detected (looking for $InstallParentPath\$project\resources\version.txt)." | Write-Output
Write-Output "Please remove the install path and retry.`n`n"
exit 1
}
If ($Version -and $Version -notmatch '^v(\d+\.)?(\d+\.)?(\*|\d+)$') {
Write-Warning "Version parameter value '$Version' does not match the version naming structure."
exit 1
}
If ($Latest -and $Latest -notin 'Release', 'Prerelease') {
Write-Warning "Latest parameter value must be one of 'Release' or 'Prelease'."
exit 1
}
# Get releases and branches from GitHub
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
try {
$releases = Invoke-RestMethod -Uri "https://api.github.com/repos/tigattack/$project/releases" -Method Get
$branches = (Invoke-RestMethod -Uri "https://api.github.com/repos/tigattack/$project/branches" -Method Get).name
}
catch {
$versionStatusCode = $_.Exception.Response.StatusCode.value__
Write-Warning "Failed to query GitHub for $project releases."
throw "HTTP status code: $versionStatusCode"
}
# Parse latest release and latest prerelease
foreach ($i in $releases) {
if ($i.prerelease) {
$latestPrerelease = $i.tag_name
break
}
}
foreach ($i in $releases) {
if (-not $i.prerelease) {
$latestStable = $i.tag_name
break
}
}
# Query download type if not specified
If (-not $Version -and
-not $Latest -and
-not $Branch -and
-not $NonInteractive) {
# Query download type / release stream
If ($releases) {
[System.Management.Automation.Host.ChoiceDescription[]]$downloadQuery_opts = @()
$downloadQuery_opts += New-Object System.Management.Automation.Host.ChoiceDescription '&Release', "Download the latest release or prerelease. You will be prompted if there's a choice between the two."
$downloadQuery_opts += New-Object System.Management.Automation.Host.ChoiceDescription '&Version', 'Download a specific version.'
$downloadQuery_opts += New-Object System.Management.Automation.Host.ChoiceDescription '&Branch', 'Download a branch.'
$downloadQuery_result = $host.UI.PromptForChoice(
'Download type',
"Please select how you would like to download $project.",
$downloadQuery_opts,
0
)
}
Else {
$branchQuery_yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Install from a branch.'
$branchQuery_no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Cancel installation.'
$host.UI.PromptForChoice(
'Would you like to install from a branch?',
"There are currently no releases or prereleases available for $project.",
@($branchQuery_yes, $branchQuery_no),
0
) | ForEach-Object {
If ($_ -eq 0) { $downloadQuery_result = 2 }
Else { exit }
}
}
# Set download type
Switch ($downloadQuery_result) {
0 {
If ($latestStable -and $latestPrerelease) {
# Query release stream
$releasePrompt = $true
# Query release stream
$versionQuery_stable = New-Object System.Management.Automation.Host.ChoiceDescription 'Latest &stable', "Latest stable: $latestStable."
$versionQuery_prerelease = New-Object System.Management.Automation.Host.ChoiceDescription 'Latest &prerelease', "Latest prelease: $latestPrerelease."
$versionQuery_result = $host.UI.PromptForChoice(
'Release Selection',
"Which release type would you like to install?`nEnter '?' to see versions.",
@(
$versionQuery_stable,
$versionQuery_prerelease),
0
)
Switch ($versionQuery_result) {
0 {
$Latest = 'Release'
}
1 {
$Latest = 'Prerelease'
}
}
}
ElseIf ($latestStable) {
$Latest = 'Release'
}
ElseIf ($latestPrerelease) {
$prereleaseQuery_yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Install the latest prerelease.'
$prereleaseQuery_no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Cancel installation.'
$host.UI.PromptForChoice(
'Do you wish to install the latest prerelease?',
'You chose release, but the only available releases are prereleases.',
@($prereleaseQuery_yes, $prereleaseQuery_no),
0
) | ForEach-Object {
If ($_ -eq 0) { $Latest = 'Prerelease' }
Else { exit }
}
}
}
1 {
do {
$Version = ($host.UI.Prompt(
'Version Selection',
"Please enter the version you wish to install.`nAvailable versions:`n $(foreach ($tag in $releases.tag_name) {"$tag`n"})",
'Version'
)).Version
If ($releases.tag_name -notcontains $Version) { Write-Output "`nInvalid version, please try again." }
} until (
$releases.tag_name -contains $Version
)
}
2 {
do {
$Branch = ($host.UI.Prompt(
'Branch Selection',
"Please enter the name of the branch you wish to install.`nAvailable branches:`n $(foreach ($branch in $branches) {"$branch`n"})",
'Branch'
)).Branch
If ($branches -notcontains $Branch) { Write-Output "`nInvalid branch name, please try again." }
} until (
$branches -contains $Branch
)
}
}
}
# Download branch if specified
If ($Branch) {
# Throw if branch not found
If (-not $branches.Contains($Branch)) {
throw "Branch '$Branch' not found. Will not prompt for branch in non-interactive mode."
}
# Set $releaseName to branch name
$releaseName = $Branch
# Define download URL
$downloadUrl = "https://api.github.com/repos/tigattack/$project/zipball/$Branch"
}
# Otherwise work with versions
Else {
# Define release to use
If ($Latest) {
Switch ($Latest) {
'Release' {
$releaseName = $latestStable
}
'Prerelease' {
$releaseName = $latestPrerelease
}
}
}
ElseIf ($Version) {
$releaseName = $Version
}
If (($Latest -or $releasePrompt) -and (-not $releaseName)) {
Write-Warning 'A release of the specified type could not found.'
exit
}
# Define download URL
$downloadUrl = Invoke-RestMethod "https://api.github.com/repos/tigattack/$project/releases" | ForEach-Object {
If ($_.tag_name -eq $releaseName) {
$_.assets[0].browser_download_url
}
}
}
# Sanitise releaseName for OutFile if installing from branch
If ($Branch) {
$outFile = "$project-$($releaseName -replace '[\W]','-')"
}
Else {
$outFile = "$project-$releaseName"
}
# Download project from GitHub
$DownloadParams = @{
Uri = $downloadUrl
OutFile = "$env:TEMP\$outFile.zip"
}
Try {
Write-Output "`nDownloading $project $releaseName from GitHub..."
Invoke-WebRequest @DownloadParams
}
catch {
$downloadStatusCode = $_.Exception.Response.StatusCode.value__
Write-Warning "Failed to download $project $releaseName."
throw "HTTP status code: $downloadStatusCode"
}
# Unblock downloaded ZIP
try {
Write-Output 'Unblocking ZIP...'
Unblock-File -Path "$env:TEMP\$outFile.zip"
}
catch {
Write-Warning 'Failed to unblock downloaded files. You will need to run the following commands manually once installation is complete:'
Write-Output "Get-ChildItem -Path $InstallParentPath -Filter *.ps* -Recurse | Unblock-File"
}
# Extract release to destination path
Write-Output "Extracting files to '$InstallParentPath'..."
Expand-Archive -Path "$env:TEMP\$outFile.zip" -DestinationPath "$InstallParentPath" -Force
# Rename destination and tidy up
Write-Output 'Renaming directory and tidying up...'
If (Test-Path "$InstallParentPath\$outFile") {
Rename-Item -Path "$InstallParentPath\$outFile" -NewName "$project"
}
Else {
# Necessary to handle branch downloads, which come as a ZIP containing a directory named similarly to "tigattack-VeeamNotify-2100906".
# Look for a directory less than 5 minutes old which matches the example name stated above.
(Get-ChildItem $InstallParentPath | Where-Object {
$_.LastWriteTime -gt (Get-Date).AddMinutes(-5) -and
$_.Name -match "tigattack-$project-.*" -and
$_.PsIsContainer
})[0] | Rename-Item -NewName "$project"
}
Remove-Item -Path "$env:TEMP\$outFile.zip"
If (-not $NonInteractive) {
Write-Output "`nBeginning configuration..."
# Join config path
$configPath = Join-Path -Path $InstallParentPath -ChildPath $project | Join-Path -ChildPath 'config\conf.json'
# Get config
$config = Get-Content "$configPath" -Raw | ConvertFrom-Json
# Prompt user with config options
$servicePrompt_discord = New-Object System.Management.Automation.Host.ChoiceDescription '&Discord', 'Send notifications to Discord.'
$servicePrompt_slack = New-Object System.Management.Automation.Host.ChoiceDescription '&Slack', 'Send notifications to Slack.'
$servicePrompt_teams = New-Object System.Management.Automation.Host.ChoiceDescription '&Teams', 'Send notifications to Teams.'
$servicePrompt_result = $host.UI.PromptForChoice(
'Notification Service',
'Which service do you wish to send notifications to?',
@(
$servicePrompt_discord,
$servicePrompt_slack,
$servicePrompt_teams
),
-1
)
$webhookPrompt = "`nPlease enter your webhook URL"
Switch ($servicePrompt_result) {
0 {
$config.services.discord.webhook = Read-Host -Prompt $webhookPrompt
}
1 {
$config.services.slack.webhook = Read-Host -Prompt $webhookPrompt
}
2 {
$config.services.teams.webhook = Read-Host -Prompt $webhookPrompt
}
}
$mentionPreference_no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Do not mention me.'
$mentionPreference_warn = New-Object System.Management.Automation.Host.ChoiceDescription '&Warning', 'Mention me when a session finishes in a warning state.'
$mentionPreference_fail = New-Object System.Management.Automation.Host.ChoiceDescription '&Failure', 'Mention me when a session finishes in a failed state.'
$mentionPreference_warnfail = New-Object System.Management.Automation.Host.ChoiceDescription '&Both', 'Notify me when a session finishes in either a warning or a failed state.'
$mentionPreference_result = $host.UI.PromptForChoice(
'Mention Preference',
'Do you wish to be mentioned/tagged when a session finishes in one of the following states?',
@(
$mentionPreference_no,
$mentionPreference_warn,
$mentionPreference_fail,
$mentionPreference_warnfail
),
2
)
If ($mentionPreference_result -ne 0) {
Switch ($servicePrompt_result) {
0 {
$config.services.discord.user_id = Read-Host -Prompt "`nPlease enter your Discord user ID"
}
1 {
$config.services.slack.user_id = Read-Host -Prompt "`nPlease enter your Slack member ID"
}
2 {
$config.services.teams.user_id = Read-Host -Prompt "`nPlease enter your Teams email address"
Write-Output "`nTeams also requires a name to be specified for mentions.`nIf you do not specify anything, your username (from your email address) will be used."
$config.services.teams.user_name = Read-Host -Prompt 'Please enter your name on Teams (e.g. John Smith)'
}
}
}
# Set config values
Switch ($mentionPreference_result) {
0 {
$config.mentions.on_failure = $false
$config.mentions.on_warning = $false
}
1 {
$config.mentions.on_failure = $false
$config.mentions.on_warning = $true
}
2 {
$config.mentions.on_failure = $true
$config.mentions.on_warning = $false
}
3 {
$config.mentions.on_failure = $true
$config.mentions.on_warning = $true
}
}
# Write config
Try {
Write-Output "`nSetting configuration..."
ConvertTo-Json $config | Set-Content "$configPath"
Write-Output "`nConfiguration set successfully. Configuration can be found in `"$configPath`"."
}
catch {
Write-Warning "Failed to write configuration file at `"$configPath`". Please open the file and complete configuration manually."
}
# Query for configuration deployment script.
$configPrompt_yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Execute configuration deployment tool.'
$configPrompt_no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Skip configuration deployment tool.'
$host.UI.PromptForChoice(
'Configuration Deployment Tool',
"Would you like to to run the VeeamNotify configuration deployment tool?`nNone of your job configurations will be modified without confirmation.",
@(
$configPrompt_yes,
$configPrompt_no
),
0
) | ForEach-Object {
If ($_ -eq 0) {
Write-Output "`nRunning configuration deployment script...`n"
& "$InstallParentPath\$project\resources\DeployVeeamConfiguration.ps1" -InstallParentPath $InstallParentPath
}
}
}
Else {
Write-Output "`nWill not prompt for VeeamNotify configuration, or to run Veeam configuration deployment script in non-interactive mode.`n"
Write-Output "`nConfiguration can be found in `"$configPath`"."
}
Write-Output "`nInstallation complete!`n"