Skip to content
English
  • There are no suggestions because the search field is empty.

Automating NovaBACKUP 22 Agents with the Local API

Use PowerShell and the local API of a NovaBACKUP 22 agent to add a backup device, create and run a backup job, restore files, and restore the computer to a Hyper-V virtual machine.

Applies to: NovaBACKUP PC Agent and NovaBACKUP Server Agent, version 22.0 and newer. It does not apply to NovaBACKUP 21.x or earlier.

Every NovaBACKUP 22 agent runs a local API. It's the same API the NovaBACKUP desktop application uses, so a script can do almost anything the application does. This article shows how to use it from PowerShell to set up and protect a computer from start to finish. You'll add a backup device, create and run a backup job, restore files, and restore the whole computer to a Hyper-V virtual machine. You can store the scripts in your RMM tool's script library and run them for each customer.

Note: To set policies for many agents at once, use the NovaBACKUP Central Management Console (CMon). The local API is for one computer at a time: automated onboarding, scripted restore tests, and tasks your RMM tool runs on a single agent.

What You'll Learn

  • How the local API works and how to browse it
  • How to connect to it with a short PowerShell helper
  • How to add a local, network, or S3-compatible backup device
  • How to create a scheduled backup job that protects the whole computer, including its data drives, and run it
  • How to restore files and folders
  • How to restore the computer to a Hyper-V virtual machine
  • How to run the scripts from an RMM tool

Prerequisites

  • NovaBACKUP 22 installed and activated on the computer. Disaster Recovery backups and restores need a license that includes Disaster Recovery.
  • Windows PowerShell 5.1, which is part of every supported Windows version, or PowerShell 7. The helper needs no extra modules.
  • A Windows user logged on to the computer's desktop. The script runs as that user (see How the Local API Works below). It does not need to run as administrator.
  • For Step 6, the Hyper-V role on the computer that runs the restore, and enough free disk space for the virtual machine's disks.

How the Local API Works

The NovaBACKUP service (Windows service Backup Client) hosts the API at http://localhost:5000/backup-client-service-hub. It is a SignalR hub that exchanges JSON messages over a WebSocket.

  • Local only. The service listens on 127.0.0.1 and ::1. It cannot be reached from another computer, and it needs no firewall rule.
  • Windows authentication. The script signs in with the Windows account it runs as. There are no API keys.
  • Runs as the logged-on user. Each call must name the Windows desktop session it comes from (header X-Windows-SessionId), and that session must belong to the account that signed in. The service then does the work as that user, exactly as if the user had clicked through the desktop application.
  • Same rules as the application. The API applies the same validation rules and license checks to every request as the desktop application does.

Important: The API refuses calls that do not come from a logged-on user's desktop session. This includes remote PowerShell sessions and scripts that run as SYSTEM, which is how most RMM tools run scripts by default. They can connect, but every call returns Unauthorized: Session does not belong to authenticated user. To run a script from your RMM tool, start it in the logged-on user's session as shown in Running the Scripts from Your RMM Tool below. On a computer where nobody is logged on, use the CMon instead.

Requests and results

  • Methods are called by name, for example CreateDeviceAsync. Names are case-sensitive and most end in Async.
  • Write property names exactly as shown in this article (for example JobName), and send choices as text (for example "OverwriteMode": "NoOverwrite").
  • Where a property can take different kinds of values, such as a schedule or a retention rule, the JSON object names its kind in a $type property, which must come first. In PowerShell, build those objects with [ordered]@{ '$type' = ... } so the order is kept.
  • Every method returns a result with these properties:
PropertyMeaning
IsSuccesstrue when the call worked
StatusOk, Created, or Accepted on success. Invalid, NotFound, Unauthorized, Forbidden (license), Error, or CriticalError on failure.
ValueThe data the method returns, such as a device list or the job that was started
ValidationErrorsFor Invalid: the property (Identifier) and the reason (ErrorMessage) for each rule the request broke
ErrorsFor other failures: the error message

Browse the API in Your Web Browser

Each agent includes a reference page for its API. On the agent, open http://localhost:5000/signalr-swagger in a web browser. The page, titled NovaBACKUP SignalR API, lists every method with its parameters, validation rules, and example requests. It's the best place to look up a method or property this article doesn't cover.

Note: The page can also send live requests. Before you send one, click Authorize and enter your Windows session ID, which you can get by running (Get-Process -Id $PID).SessionId in PowerShell. Without it, requests fail with Session ID header is missing. The reference page omits the Async suffix from method names, but you need the suffix when you call a method from a script.

Step 1: Add the PowerShell Helper

The helper below connects to the API, calls methods, and waits for jobs to finish. It uses only .NET classes that ship with Windows. Save it on the agent as C:\Scripts\NovaBACKUPApi.ps1.

# NovaBACKUPApi.ps1 - minimal client for the NovaBACKUP 22 local API (a SignalR hub that speaks JSON).
# Works in Windows PowerShell 5.1 and PowerShell 7. Load it with:  . C:\Scripts\NovaBACKUPApi.ps1
# Run it as the user who is logged on to the computer, in that user's desktop session.

$script:Nb = $null

function Connect-NovaBACKUPApi {
    param([int]$Port = 5000)
    $sessionId = [System.Diagnostics.Process]::GetCurrentProcess().SessionId
    $ws = New-Object System.Net.WebSockets.ClientWebSocket
    $ws.Options.UseDefaultCredentials = $true                          # Windows authentication
    $ws.Options.SetRequestHeader('X-Windows-SessionId', "$sessionId")  # the caller's own desktop session
    [void]$ws.ConnectAsync([Uri]"ws://localhost:$Port/backup-client-service-hub", [Threading.CancellationToken]::None).GetAwaiter().GetResult()
    $script:Nb = @{ Socket = $ws; NextId = 0; Pending = $null; Chunk = $null; Buffer = New-Object IO.MemoryStream
                    Inbox = New-Object Collections.Queue; Events = New-Object Collections.ArrayList; LastSend = Get-Date }
    Send-NbFrame '{"protocol":"json","version":1}'                       # SignalR handshake
    $reply = Receive-NbMessage -TimeoutSeconds 15
    if (-not $reply -or $reply.error) { throw "Could not connect to the NovaBACKUP service: $($reply.error)" }
}

function Disconnect-NovaBACKUPApi {
    if ($script:Nb) {
        [void]$script:Nb.Socket.CloseAsync('NormalClosure', '', [Threading.CancellationToken]::None).GetAwaiter().GetResult()
        $script:Nb = $null
    }
}

function Send-NbFrame([string]$Json) {
    $bytes = [Text.Encoding]::UTF8.GetBytes($Json + [char]0x1e)         # every message ends with 0x1E
    $segment = New-Object 'ArraySegment[byte]' (, $bytes)
    [void]$script:Nb.Socket.SendAsync($segment, 'Text', $true, [Threading.CancellationToken]::None).GetAwaiter().GetResult()
    $script:Nb.LastSend = Get-Date
}

# Returns the next message from the service, or $null when none arrives within the timeout.
# Sends a ping every 10 seconds so the service keeps the connection open.
function Receive-NbMessage([int]$TimeoutSeconds = 30) {
    $nb = $script:Nb
    $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
    while ($nb.Inbox.Count -eq 0) {
        if (-not $nb.Pending) {
            $nb.Chunk = New-Object 'ArraySegment[byte]' (, (New-Object byte[] 65536))
            $nb.Pending = $nb.Socket.ReceiveAsync($nb.Chunk, [Threading.CancellationToken]::None)
        }
        if (((Get-Date) - $nb.LastSend).TotalSeconds -ge 10) { Send-NbFrame '{"type":6}' }
        if (-not $nb.Pending.Wait(1000)) {
            if ((Get-Date) -gt $deadline) { return $null }
            continue
        }
        $r = $nb.Pending.Result; $nb.Pending = $null
        if ($r.MessageType -eq 'Close') { throw "The NovaBACKUP service closed the connection: $($nb.Socket.CloseStatusDescription)" }
        $nb.Buffer.Write($nb.Chunk.Array, 0, $r.Count)
        if ($r.EndOfMessage) {
            $text = [Text.Encoding]::UTF8.GetString($nb.Buffer.ToArray()); $nb.Buffer.SetLength(0)
            foreach ($part in $text.Split([char]0x1e)) { if ($part.Trim()) { $nb.Inbox.Enqueue(($part | ConvertFrom-Json)) } }
        }
    }
    return $nb.Inbox.Dequeue()
}

# Calls an API method and returns its result (Value, Status, IsSuccess, Errors, ValidationErrors).
# Throws when the method does not succeed. Job events that arrive meanwhile are kept for Wait-NovaBACKUPJob.
function Invoke-NovaBACKUPApi {
    param([Parameter(Mandatory)][string]$Method, [object[]]$Arguments = @(), [int]$TimeoutSeconds = 300)
    $script:Nb.NextId++; $id = "$($script:Nb.NextId)"
    $argumentsJson = ConvertTo-Json -InputObject @($Arguments) -Depth 20 -Compress
    Send-NbFrame ('{"type":1,"invocationId":"' + $id + '","target":"' + $Method + '","arguments":' + $argumentsJson + '}')
    $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
    while ((Get-Date) -lt $deadline) {
        $m = Receive-NbMessage -TimeoutSeconds 5
        if (-not $m) { continue }
        if ($m.type -eq 1) { [void]$script:Nb.Events.Add($m); continue }      # an event, such as JobProgress
        if ($m.type -eq 3 -and $m.invocationId -eq $id) {
            if ($m.error) { throw "$Method failed: $($m.error)" }
            $result = $m.result
            if (-not $result.IsSuccess) {
                $details = @($result.Errors) + @($result.ValidationErrors | ForEach-Object { "$($_.Identifier): $($_.ErrorMessage)" })
                throw "$Method returned $($result.Status): $($details -join '; ')"
            }
            return $result
        }
    }
    throw "$Method did not answer within $TimeoutSeconds seconds."
}

# Waits for a job to finish, shows its progress, and returns the finished job.
function Wait-NovaBACKUPJob {
    param([Parameter(Mandatory)][string]$JobId, [int]$TimeoutMinutes = 480)
    [void](Invoke-NovaBACKUPApi JoinActivityAsync)        # ask the service to report the progress of backup engine jobs
    [void](Invoke-NovaBACKUPApi SubscribeToJobAsync @($JobId))
    $deadline = (Get-Date).AddMinutes($TimeoutMinutes); $shown = -1
    while ((Get-Date) -lt $deadline) {
        $events = @($script:Nb.Events); $script:Nb.Events.Clear()
        $m = Receive-NbMessage -TimeoutSeconds 5
        if ($m -and $m.type -eq 1) { $events += $m }
        foreach ($e in $events) {
            $job = $e.arguments[0]
            if ($job.Id -ne $JobId) { continue }
            if ($e.target -eq 'JobProgress' -and $job.Progress -ne $shown) {
                Write-Host ('{0:HH:mm:ss} {1,3}% {2}' -f (Get-Date), $job.Progress, $job.Message); $shown = $job.Progress
            }
            if ($e.target -eq 'JobFinished') { return $job }
        }
    }
    throw "Job $JobId did not finish within $TimeoutMinutes minutes."
}

The helper provides four commands:

CommandWhat it does
Connect-NovaBACKUPApiOpens the connection. Use -Port if you changed the service's port.
Invoke-NovaBACKUPApiCalls a method and returns its result. Pass the method's arguments as an array, for example @($device). Throws an error with the reasons if the call fails.
Wait-NovaBACKUPJobShows a job's progress and returns the job when it has finished
Disconnect-NovaBACKUPApiCloses the connection

Test the connection by listing the backup devices the agent already has:

. C:\Scripts\NovaBACKUPApi.ps1
Connect-NovaBACKUPApi
(Invoke-NovaBACKUPApi GetAllDevicesAsync).Value | Format-Table Name, Type, Path

The examples in the rest of this article assume you have loaded the helper and connected in the same script.

Step 2: Add a Backup Device

A backup device is where the agent stores its backups. Each device has a name, which jobs and restores use to refer to it.

A local or external disk

$device = [ordered]@{
    Name          = 'Local Backups'
    Path          = 'E:\Backups'
    EncryptionKey = 'Replace-With-A-Unique-Key'
}
$result = Invoke-NovaBACKUPApi CreateDeviceAsync @($device)
$result.Status    # Created

The agent creates the folder if it does not exist, as long as the drive does. It stores the backups in a subfolder named after the device, for example E:\Backups\Local Backups.

A network share

$device = [ordered]@{
    Name          = 'Office NAS'
    Path          = '\\nas01\backups\pc-042'
    UserName      = 'backupuser'
    Password      = $nasPassword
    EncryptionKey = 'Replace-With-A-Unique-Key'
}
Invoke-NovaBACKUPApi CreateDeviceAsync @($device)

Use a UNC path, not a mapped drive letter. The agent connects to the share with these credentials while it checks the request, so a wrong path or password is reported right away. Set Domain as well if the account belongs to a domain.

S3-compatible cloud storage

$device = [ordered]@{
    Name            = 'Offsite S3'
    URL             = 'https://s3.us-east-1.wasabisys.com'
    BucketName      = 'msp-client-backups'
    AccessKeyId     = $s3AccessKeyId
    SecretAccessKey = $s3SecretAccessKey
    UserName        = 'contoso'
    BackupDirectory = 'pc-042'
    EncryptionKey   = 'Replace-With-A-Unique-Key'
}
Invoke-NovaBACKUPApi CreateCloudDeviceAsync @($device)

URL, BucketName, AccessKeyId, and SecretAccessKey come from your storage provider. UserName and BackupDirectory are names you choose (letters, digits, and ! ' ( ) * - . _ only). The agent uses them to keep its data apart from other agents' data in the same bucket, so give every agent a unique pair, such as the customer name and the computer name. The agent connects to the bucket while it checks the request. The backups are stored in the bucket under Appl <UserName>/<BackupDirectory>/<Domain>/Node <MachineName>/. Domain defaults to the computer's domain or workgroup, and MachineName defaults to the computer's name.

Note: You cannot add NovaBACKUP Cloud storage through the API. Instead, assign it to the agent from the CMon, which pushes the managed cloud device to the agent.

Encryption

For these device types, encryption is set on the device, not on the backup job. When a device has an EncryptionKey (10 to 30 characters), every backup written to it is encrypted, and the agent reuses the key for restores. Keep a copy of each key in your password manager. Without it, the backups cannot be restored on another computer.

Important: Your scripts need passwords, secret keys, and encryption keys. Store them in your RMM tool's secure variables or a password vault rather than in the script, and delete the script and its log from the agent after the script finishes.

Step 3: Create a Backup Job

This job backs up the whole computer every weekday at 10:00 PM and keeps 30 days of backups. It selects Disaster Recovery (Windows and the system drive) and the computer's data drive, D:\:

$job = [ordered]@{
    JobName     = 'Full System Backup'
    Destination = [ordered]@{ Path = 'Local Backups'; SessionName = 'Full System Backup' }
    Selections  = @(
        [ordered]@{ Path = 'Disaster Recovery'; Type = '+' }   # Windows and what is needed to rebuild the computer
        [ordered]@{ Path = 'D:\'; Type = '+' }                  # each data drive must be added as well
    )
    Schedule    = [ordered]@{
        '$type'        = 'DailySchedule'
        StartDateTime  = (Get-Date '22:00').ToUniversalTime().ToString('o')
        IntervalInDays = 1
        DaysOfTheWeek  = 'Monday, Tuesday, Wednesday, Thursday, Friday'
    }
    Settings    = [ordered]@{
        BackupOpenFilesUsingVolumeShadowCopyService = $true
        Retention = [ordered]@{ '$type' = 'RetentionWithDays'; Period = 30 }
    }
}
$jobFile = "$env:ProgramData\NovaStor\NovaStor NovaBACKUP\UserScripts\$env:USERNAME\Backup\$($job.JobName).nbk"
Invoke-NovaBACKUPApi SaveBackupAsync @($job, $jobFile)

SaveBackupAsync takes two arguments: the job and the file to save it to. Saving the job in the logged-on user's job folder, as shown above, makes it appear on the desktop application's Backup page, where you can check it or change it later.

What to back up (Selections)

Add one entry per item, with Type set to + to include it or - to exclude it.

PathBacks up
Disaster RecoveryThe drive Windows runs from (usually C:) and everything needed to start the computer and rebuild it on new hardware or in a virtual machine. It does not include other drives.
A drive, for example D:\Everything on that drive
A folder or file, for example C:\Users\jsmith\DocumentsThat folder or file. Use UNC paths for network locations.
System State, Microsoft SQL Server, Hyper-VPlug-in data, where the plug-in is available on the computer

Important: Disaster Recovery selects the drive Windows runs from and everything needed to make the computer work again, so you can rebuild it. It does not back up other drives. If the computer keeps data on other drives, such as D: or E:, add each of those drives (or the folders on them that you need) to Selections, as the example does with D:\. Data on a drive that is not selected is not protected.

To see what can be selected on a computer, call GetBackupSourceObjectsAsync, which returns the same tree the Backup page shows.

When to run it (Schedule)

$typeProperties
NoScheduleNone. The job runs only when you start it.
OneTimeScheduleStartDateTime
HourlyScheduleStartDateTime, IntervalInHours (1 to 23)
DailyScheduleStartDateTime, IntervalInDays (1 to 999), DaysOfTheWeek
WeeklyScheduleStartDateTime, IntervalInWeeks (1 to 52)
MonthlyScheduleStartDateTime, DayOfMonth (1 to 31), IntervalInMonths (1 to 12)

Important: StartDateTime is stored as UTC. Convert the local start time to UTC, as the example does with .ToUniversalTime(), or the job runs at the wrong time of day. DaysOfTheWeek lists English day names separated by commas.

Note: Scheduled jobs run only while Allow scheduled jobs to execute is turned on in the application's Settings. The setting is on by default. To check it from a script, call GetSchedulingSettingsAsync.

Job settings (Settings)

Any setting you leave out uses its default value. These are the ones you are most likely to set:

SettingValues
RetentionInfiniteRetention to keep everything, or RetentionWithDays, RetentionWithMonths, or RetentionWithYears with a Period of 1 to 99
BackupOpenFilesUsingVolumeShadowCopyService$true to back up files that are in use. It defaults to $false when you send Settings from a script, so set it for every job.
CommandToRunBeforeJob, CommandToRunAfterJobA command line to run before or after the backup
EmailNoEmailNotifications (default) or SendEmailNotifications
RunAsRunAsLoggedInUser (default) or RunAsUser with UserName and Password

For local, network, and S3-compatible devices, keep the remaining settings at their defaults. Leave Encryption at None and Verification at Quick, because the API refuses a request that changes them. The job name can be up to 50 characters.

Step 4: Run the Backup and Check the Result

$job = (Invoke-NovaBACKUPApi StartBackupJobAsync @(@{ JobScriptFilePath = $jobFile })).Value
$result = Wait-NovaBACKUPJob -JobId $job.Id
"$($result.Name): $($result.Status) $($result.ErrorMessage) $($result.WarningMessage)"

StartBackupJobAsync starts the saved job and returns immediately with the job's Id. Wait-NovaBACKUPJob then prints the progress and returns the job when it has finished. Its Status is one of:

StatusMeaning
CompletedThe backup finished without problems.
CompletedWithWarningsThe backup finished, but something needs your attention, for example files that were skipped. See WarningMessage.
FailedThe backup did not finish. See ErrorMessage.
CanceledSomeone canceled the job.

The run also appears in the application's Job History and, for agents connected to the CMon, in the CMon.

To cancel a running job, call CancelJobAsync with the job's Id. Wait-NovaBACKUPJob then returns it with the status Canceled.

Note: An agent runs one backup or restore at a time. If another job is running, StartBackupJobAsync returns Another job is already running. Wait for it to finish, then try again.

Step 5: Restore Files and Folders

Find the backup set to restore from, then start the restore:

# The newest backup set of the job
$set = (Invoke-NovaBACKUPApi GetBackupSetsForDeviceAsync @('Local Backups')).Value |
    Where-Object JobName -eq 'Full System Backup' |
    Sort-Object BackupDate -Descending | Select-Object -First 1

$restore = [ordered]@{
    DeviceName        = $set.DeviceName
    Dictionary        = Split-Path $set.DictionaryPath -Leaf
    JobName           = $set.JobName
    JobStartDate      = $set.BackupDate
    Includes          = @('C:\Users\jsmith\Documents')
    AlternateLocation = 'C:\Restored'
    OverwriteMode     = 'NoOverwrite'
}
$job = (Invoke-NovaBACKUPApi RestoreAsync @($restore)).Value
$result = Wait-NovaBACKUPJob -JobId $job.Id
"$($result.Status) $($result.ErrorMessage)"
PropertyMeaning
DeviceName, Dictionary, JobName, JobStartDateIdentify the backup set. Take these values from the set that GetBackupSetsForDeviceAsync returns, as shown. Dictionary is the file name only.
IncludesThe folders and files to restore, by their original paths
AlternateLocationThe folder to restore to. Leave it out to restore to the original location. The contents of each folder in Includes are restored directly into this folder.
OverwriteModeNoOverwrite, OverwriteOlderTarget, or OverwriteAlways (default)

You can restore files from any backup set, including a Disaster Recovery backup set, as in this example.

Step 6: Restore the Computer to a Hyper-V Virtual Machine

You can restore a Disaster Recovery backup set to a new Hyper-V virtual machine, for example to test that a customer's server can be recovered or to get a failed computer's workload running again while its hardware is repaired. Run the script on a Hyper-V host that has NovaBACKUP 22 installed and the backup device added. The host can be the original computer or a different one.

(Invoke-NovaBACKUPApi GetVirtualSwitchesAsync).Value.Name    # the switches you can connect to

$set = (Invoke-NovaBACKUPApi GetBackupSetsForDeviceAsync @('Local Backups')).Value |
    Where-Object JobName -eq 'Full System Backup' |
    Sort-Object BackupDate -Descending | Select-Object -First 1

$vm = [ordered]@{
    DeviceName         = $set.DeviceName
    Dictionary         = $set.DictionaryPath
    AlternateLocation  = 'D:\Hyper-V'
    VirtualMachineName = 'PC-042 Recovered'
    ProcessorCount     = 2
    MemoryInMegabytes  = 4096
    VirtualSwitchName  = 'External Switch'
}
$job = (Invoke-NovaBACKUPApi DRRestoreToVmAsync @($vm)).Value
$result = Wait-NovaBACKUPJob -JobId $job.Id
"$($result.Status) $($result.ErrorMessage) $($result.WarningMessage)"
PropertyMeaning
DeviceName, DictionaryIdentify the Disaster Recovery backup set. Unlike in the file restore, Dictionary is the full path here (DictionaryPath).
AlternateLocationRequired. The full path of the folder where the virtual machine and its disks are created.
VirtualMachineNameOptional. Up to 100 characters. If left out, the name is based on the original computer's name.
ProcessorCount, MemoryInMegabytesOptional. If left out, the original computer's processor count and memory are used. MemoryInMegabytes is the startup memory. Give the virtual machine less memory than the host has free.
VirtualSwitchNameOptional. If left out, the network adapter is not connected.

The restore creates a Generation 2 (UEFI) virtual machine with dynamically expanding VHDX disks that match the original computer's disk layout. When the status is Completed, start the virtual machine in Hyper-V Manager or with Start-VM.

Important: The virtual machine gets a disk for each internal disk of the original computer. Every drive that was selected in the backup, such as D:\ in the example job, is restored onto its disk. Drives that were not selected are created empty. In our test, the original computer's data drive was not selected, and its disk in the virtual machine was empty.

Note: Only Disaster Recovery backup sets can be restored to a virtual machine. Other sets are refused with an Invalid result.

As a guide, in our tests, a Windows 11 computer with 68 GB of data took 26 minutes to back up to a local disk and 21 minutes to restore to a virtual machine on the same computer. The restored virtual machine started Windows normally.

Running the Scripts from Your RMM Tool

Because the API accepts calls only from the logged-on user's desktop session, a script that your RMM tool runs as SYSTEM must hand the work over to that session. The following wrapper does this with a one-time scheduled task. Run the wrapper from your RMM tool as SYSTEM and pass it the path of your API script:

# Runs a NovaBACKUP API script as the user who is logged on to this computer, in that user's
# desktop session, and prints its output. Run this wrapper from your RMM tool as SYSTEM.
param([string]$ScriptPath = 'C:\Scripts\Backup-Now.ps1', [int]$TimeoutMinutes = 240)

$user = (Get-CimInstance Win32_ComputerSystem).UserName
if (-not $user) { throw 'No user is logged on to the desktop. The NovaBACKUP API needs one.' }

$log = [IO.Path]::ChangeExtension($ScriptPath, '.log')
Remove-Item $log -ErrorAction SilentlyContinue
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
    -Argument "-NoProfile -ExecutionPolicy Bypass -Command `"& '$ScriptPath' *>&1 | Out-File -Encoding utf8 '$log'`""
$principal = New-ScheduledTaskPrincipal -UserId $user -LogonType Interactive
Register-ScheduledTask -TaskName 'NovaBACKUP API' -Action $action -Principal $principal -Force | Out-Null
Start-ScheduledTask -TaskName 'NovaBACKUP API'

$deadline = (Get-Date).AddMinutes($TimeoutMinutes)
do { Start-Sleep -Seconds 5 } while ((Get-ScheduledTask -TaskName 'NovaBACKUP API').State -eq 'Running' -and (Get-Date) -lt $deadline)
Unregister-ScheduledTask -TaskName 'NovaBACKUP API' -Confirm:$false
Get-Content $log

The wrapper waits for the API script to finish and prints its output, so the result shows up in your RMM tool's job log. If nobody is logged on, it stops with an error.

Note: The work runs as the logged-on user's Windows account, the same as when that user works in the desktop application. Give a network device its own credentials (Step 2) unless that user already has access to the share.

Troubleshooting

MessageCause and solution
Unauthorized: Session does not belong to authenticated user.The script runs as SYSTEM, in a remote PowerShell session, or as a user other than the one logged on to the desktop. Run it in the logged-on user's session, as shown in Running the Scripts from Your RMM Tool.
Session ID header is missing.The request did not include the X-Windows-SessionId header. The helper sends it for you. On the reference page, click Authorize first.
The connection fails or is refusedCheck that the Backup Client service is running, and that antivirus or EDR software allows local connections to localhost:5000.
Invalid with ValidationErrorsThe request broke a rule, for example Path: The drive 'Q:' could not be found. Correct the property named before the colon. The reference page lists each method's rules.
A storage device named '...' already exists.Device names must be unique on the agent. Call GetAllDevicesAsync first and skip devices that already exist, so the script can safely run again.
Another job is already running.Wait for the running job to finish, or check for it on the application's Activity page.
ForbiddenThe license does not include the feature (for example, Disaster Recovery), or the license has expired.

Best Practices

  • Check each computer for data drives before you create its job, for example with Get-Volume, and add every drive that holds data to Selections. Leave out the drive that holds a local backup device.
  • Make onboarding scripts safe to run twice: check for existing devices with GetAllDevicesAsync and existing jobs with GetBackupJobsAsync before you create them.
  • Give every agent's device a unique encryption key, and record it in your password manager before the first backup runs.
  • Test restores on a schedule. A monthly restore of each server's Disaster Recovery backup to a virtual machine on your lab host proves the backup works.
  • Use the reference page at http://localhost:5000/signalr-swagger on an agent of the same version to look up methods, since the API grows with each release.

Related Articles


Still Need Help?

Contact NovaBACKUP Support: