1+ <#
2+ . SYNOPSIS
3+ This sample script demonstrates the use of NetBackup REST API for launching a manual backup from a Windows client system
4+ . DESCRIPTION
5+ This script will query the master server for policy information and existing client backup images. If the latest full backup
6+ is older than the frequency defined within the policy schedule a full backup will be launched; otherwise, an incremental will
7+ be performed.
8+ . EXAMPLE
9+ .\ClientBackup.ps1 -p "ExampleClientBackup-Win" -k "AybRCz3UE_YOpCFD7_5mzQQfJsRXj_pN6WXLA7boX4EAuKD_kwBfXWQ5bFNWDiuJ"
10+ #>
11+
12+ <#
13+ Requirements and comments for running this script
14+ * Tested with PowerShell 5.1 but should work with PowerSell 3.0 or later
15+ * Tested with NetBackup 8.3
16+ * NetBackup client software already installed, configured and tested
17+ * A policy must be defined on the master server with the following details
18+ * Policy type must be MS-Windows
19+ * At least 2 schedules define with no backup windows
20+ * one full
21+ * one incremental
22+ * Client name added to Clients tab
23+ * Use command line parameters to specify the following parameters
24+ * -policy (to reference above policy)
25+ * -apikey (generated through NetBackup web UI)
26+ * API key uesr must have following privileges assigned to it's role:
27+ * Minimum specific privileges:
28+ * Global -> NetBackup management -> NetBackup images -> View
29+ * Global -> Protection -> Policies -> View
30+ * Global -> Protection -> Policies -> Manual Backup
31+ * PowerShell Execution Policy needs to be opened
32+ #>
33+
34+
35+ param (
36+ [string ]$p = $ (throw " Please speicfy the policy name using -p parameter." ),
37+ [string ]$k = $ (throw " Please specify the password using -k parameter." ),
38+ [switch ]$v = $false ,
39+ [switch ]$t = $false
40+ )
41+ $policy = $p
42+ $apikey = $k
43+ $verbose = $v
44+ $testmode = $t
45+
46+ # ####################################################################
47+ # Initial Setup
48+ # Note: This allows self-signed certificates and enables TLS v1.2
49+ # ####################################################################
50+
51+ function InitialSetup ()
52+ {
53+ # Allow self-signed certificates
54+ if ([System.Net.ServicePointManager ]::CertificatePolicy -notlike ' TrustAllCertsPolicy' )
55+ {
56+ Add-Type - TypeDefinition @"
57+ using System.Net;
58+ using System.Security.Cryptography.X509Certificates;
59+ public class TrustAllCertsPolicy : ICertificatePolicy {
60+ public bool CheckValidationResult(
61+ ServicePoint srvPoint, X509Certificate certificate,
62+ WebRequest request, int certificateProblem) {
63+ return true;
64+ }
65+ }
66+ "@
67+ [System.Net.ServicePointManager ]::CertificatePolicy = New-Object - TypeName TrustAllCertsPolicy
68+
69+ # Force TLS v1.2
70+ try {
71+ if ([Net.ServicePointManager ]::SecurityProtocol -notmatch ' Tls12' ) {
72+ [Net.ServicePointManager ]::SecurityProtocol += [Net.SecurityProtocolType ]::Tls12
73+ }
74+ }
75+ catch {
76+ Write-Host $_.Exception.InnerException.Message
77+ }
78+ }
79+ }
80+
81+ InitialSetup
82+
83+ # ####################################################################
84+ # Looking in the registry for NetBackup details
85+ # ####################################################################
86+ $a = Get-ItemPropertyValue - path HKLM:\SOFTWARE\VERITAS\NetBackup\CurrentVersion\Config - name Server
87+ $b = $a.Split (" " )
88+ if ( $b -is [system.array ] ) {
89+ $nbmaster = $b [0 ]
90+ } else {
91+ $nbmaster = $b
92+ }
93+ $clientname = Get-ItemPropertyValue - path HKLM:\SOFTWARE\VERITAS\NetBackup\CurrentVersion\Config - name Client_Name
94+
95+ if ( $verbose ) {
96+ Write-Host " Looking at local NetBackup configuration for client name and master server"
97+ Write-Host " nbmaster=$nbmaster "
98+ Write-Host " clientname=$clientname "
99+ Write-Host
100+ }
101+
102+ # ####################################################################
103+ # Global Variables
104+ # ####################################################################
105+ $port = 1556
106+ $basepath = " https://" + $nbmaster + " :" + $port + " /netbackup"
107+ $content_type = " application/vnd.netbackup+json;version=4.0"
108+ $days2lookback = 30
109+ if ( $verbose ) {
110+ Write-Host " Base URI = $basepath "
111+ Write-Host " Looking back $days2lookback days for previous backups"
112+ Write-Host
113+ }
114+
115+ # ####################################################################
116+ # Getting the policy details
117+ # ####################################################################
118+ $uri = $basepath + " /config/policies/" + $policy
119+ if ( $verbose ) {
120+ Write-Host " Getting $policy policy details"
121+ Write-Host " Using URI $uri "
122+ }
123+
124+ $headers = @ {
125+ " Authorization" = $apikey
126+ " X-NetBackup-Policy-Use-Generic-Schema" = " true"
127+ }
128+
129+ $response = Invoke-WebRequest `
130+ - Uri $uri `
131+ - Method GET `
132+ - Body $query_params `
133+ - ContentType $content_type `
134+ - Headers $headers
135+
136+ if ($response.StatusCode -ne 200 )
137+ {
138+ throw " Unable to get the list of Netbackup images!"
139+ }
140+
141+ # Converting JSON output into PowerShell object format
142+ $content = (ConvertFrom-Json - InputObject $response )
143+
144+ # Determining backup frequency for full backup and getting
145+ # the full and incr schedule names
146+ for ( $i = 0 ; $i -lt $content.data.attributes.policy.schedules.count ; $i ++ ) {
147+ if ( $content.data.attributes.policy.schedules [$i ].schedulename -eq " FULL" ) {
148+ $fullfrequency = $content.data.attributes.policy.schedules [$i ].frequencyseconds
149+ $fullschedule = $content.data.attributes.policy.schedules [$i ].schedulename
150+ }
151+ if ( $content.data.attributes.policy.schedules [$i ].schedulename -like " INCR" ) {
152+ $incrfrequency = $content.data.attributes.policy.schedules [$i ].frequencyseconds
153+ $incrschedule = $content.data.attributes.policy.schedules [$i ].schedulename
154+ }
155+ }
156+
157+ if ( $verbose ) {
158+ Write-Host " Incremental schedule $incrschedule frequency is $incrfrequency seconds"
159+ Write-Host " Full schedule $fullschedule frequency is $fullfrequency seconds"
160+ Write-Host
161+ }
162+
163+ # ####################################################################
164+ # Get NetBackup Images from last days2lookback (30 default) days for this client
165+ # ####################################################################
166+ $uri = $basepath + " /catalog/images"
167+ if ( $verbose ) {
168+ Write-Host " Looking for most recent backup images to see what kind of backup to run"
169+ Write-Host " Using URI $uri "
170+ }
171+
172+ $headers = @ {
173+ " Authorization" = $apikey
174+ }
175+
176+ # Note that currentDate and lookbackDate are DateTime objects while
177+ # backupTimeStart and backupTimeEnd are string date in ISO 8601 format
178+ # using Zulu (Greenwich Mean Time) time: YYYY-MM-DDThh:mm:ssZ
179+ # Date/Time format example: November 13, 1967 at 3:22:00 PM = 1967-11-13T15:22:00Z
180+ # Getting current date
181+ $a = Get-Date
182+ $currentDate = $a.ToUniversalTime ()
183+ $backupTimeEnd = (Get-Date - format s - date $currentDate ) + " Z"
184+
185+ # Set starting date to 30 days from current date
186+ $lookbackDate = (Get-Date ).AddDays(- $days2lookback )
187+ $backupTimeStart = (Get-Date - format s - date $lookbackDate ) + " Z"
188+
189+ $query_params = @ {
190+ " page[limit]" = 50 # This changes the default page size to 50
191+ # The following filter variable adds a filter to only show for this client in past 30 days
192+ # "filter" = "clientName eq '$clientname' and backupTime ge $backupTimeStart and backupTime le $backupTimeEnd"
193+ " filter" = " clientName eq '$clientname '"
194+ }
195+ if ( $verbose ) {
196+ Write-Host " backupTimeEnd = $backupTimeEnd "
197+ Write-Host " backupTimeStart = $backupTimeStart "
198+ }
199+
200+ $response = Invoke-WebRequest `
201+ - Uri $uri `
202+ - Method GET `
203+ - Body $query_params `
204+ - ContentType $content_type `
205+ - Headers $headers
206+
207+ if ($response.StatusCode -ne 200 )
208+ {
209+ throw " Unable to get the list of Netbackup images!"
210+ }
211+
212+ # Write-Host "response=$response"
213+ # Write-Host "Response JSON"
214+ # $response | ConvertFrom-Json | ConvertTo-Json
215+
216+ # Convert the JSON output into PowerShell object format
217+ $content = (ConvertFrom-Json - InputObject $response )
218+
219+ # Converting the JSON data for the image attributes into an array for looping through
220+ $imageinfo = % {$content.data.attributes }
221+ # Write-Host "Image info JSON"
222+ # $imageinfo | ConvertTo-Json
223+ # Get-Member -InputObject $imageinfo
224+
225+ # Setting values to validation variables
226+ $schedulerun = " none"
227+ $fulltime = " no"
228+ $incrtime = " no"
229+
230+ # Looping through all the images found for this client looking for most recent
231+ # full and incr backup image. Image data is listed in date descending order, i.e.,
232+ # newest to oldest. We just need to capture the first instance of either backup type
233+ # Handling 3 scenarios of returned images counts:
234+ # 1 image doesn't create an array of objects so need to process
235+ # 2 or more images create array of objects to process in a loop
236+ if ( $content.meta.pagination.count -eq 1 ) {
237+ if ( $imageinfo.scheduleName -eq " FULL" ) {
238+ $fulltime = [datetime ]::Parse($imageinfo.backuptime )
239+ } else {
240+ $incrtime = [datetime ]::Parse($imageinfo.backuptime )
241+ }
242+ } else {
243+ for ( $i = 0 ; $i -lt $content.meta.pagination.count ; $i ++ ) {
244+ # Can skip if we've identified that we don't need to run any backups
245+ if ( $fulltime -ne " no" -AND $incrtime -ne " no" ) {
246+ continue
247+ } elseif ( $imageinfo [$i ].scheduleName -eq " FULL" ) {
248+ $fulltime = [datetime ]::Parse($imageinfo [$i ].backuptime)
249+ } elseif ( $imageinfo [$i ].scheduleName -eq " INCR" ) {
250+ $incrtime = [datetime ]::Parse($imageinfo [$i ].backuptime)
251+ }
252+ }
253+ }
254+
255+ # Define the full and incr window by subtracting the schedule frequency from
256+ # the current time.
257+ $fullwindow = $currentDate.AddSeconds (- $fullfrequency )
258+ $incrwindow = $currentDate.AddSeconds (- $incrfrequency )
259+
260+ # Now, run through the logic to determine what kind of backup to run
261+ if ( $fulltime -eq " no" ) {
262+ # No recent backup images found for this client, run full backup
263+ $schedulerun = " FULL"
264+ } elseif ( $fullwindow -ge $fulltime ) {
265+ # Found a FULL backup older than current full window
266+ $schedulerun = " FULL"
267+ } elseif ( $fulltime -ne " no" -AND $incrtime -eq " no" ) {
268+ # Full backup found but less than window and no incremental
269+ $schedulerun = " INCR"
270+ } elseif ( $incrwindow -ge $incrtime ) {
271+ # Full backup less than window and incremental older than window
272+ $schedulerun = " INCR"
273+ } else {
274+ $schedulerun = " none"
275+ }
276+
277+ if ( $verbose ) {
278+ Write-Host " schedulerun=$schedulerun "
279+ Write-Host " fulltime=$fulltime "
280+ Write-Host " incrtime=$incrtime "
281+ Write-Host " fullwindow=$fullwindow "
282+ Write-Host " incrwindow=$incrwindow "
283+ }
284+
285+ # If schedulerun is equal to none, then skip running anything
286+ if ( $schedulerun -eq " none" ) {
287+ Write-Host " Too soon to take a backup"
288+ exit
289+ }
290+
291+ # Running this in testing mode which means we don't want to run a backup,
292+ # just see what wwould be run
293+ if ( $testmode ) {
294+ exit
295+ }
296+
297+ # ####################################################################
298+ # Launch the backup now
299+ # ####################################################################
300+ $uri = $basepath + " /admin/manual-backup"
301+ if ( $verbose ) {
302+ Write-Host " Launching the backup now"
303+ Write-Host " Using URI $uri "
304+ }
305+
306+ $headers = @ {
307+ " Authorization" = $apikey
308+ }
309+
310+ $backup_params = @ {
311+ data = @ {
312+ type = " backupRequest"
313+ attributes = @ {
314+ policyName = $policy
315+ scheduleName = $schedulerun
316+ clientName = $clientname
317+ }
318+ }
319+ }
320+
321+ $body = ConvertTo-Json - InputObject $backup_params
322+
323+ $response = Invoke-WebRequest `
324+ - Uri $uri `
325+ - Method POST `
326+ - Body $body `
327+ - ContentType $content_type `
328+ - Headers $headers
329+
330+ if ($response.StatusCode -ne 202 )
331+ {
332+ # Backup job did not start successfully
333+ " API StatusCode = " + $response.StatusCode
334+ # Write-Host $response.errorResponse
335+ # $content = (ConvertFrom-Json -InputObject $response)
336+ # "NetBackup error code = "+$content.errorResponse.errorCode
337+ # "NetBackup error message ="+$content.errorResponse.errorMessage
338+ throw " Unable to start the backup for " + $clientname + " with schedule " + $schedulerun + " for policy " + $policy
339+ }
340+
341+ if ( $verbose ) {
342+ Write-Host " Backup $schedulerun successfully started"
343+ }
0 commit comments