Since the version 7 release of Veeam Backup & Replication all of the typical Enterprise Manager functionality has been exposed via an XML REST API. Being a pretty hefty user of vRealize Orchestrator this has proven to be extremely useful to me when looking to automate certain parts of my infrastructure. Now that said, there are times when vRO is simply out of reach – or when the person I’m creating the automation for is simply more familiar with PowerShell. Now, I understand that Veeam Backup & Replication does come with PowerShell support and what I’m about to walk through may be somewhat redundant as they have their own cmdlets built around certain tasks – but this crazy IT world we live in is changing and REST seems to be at the helm of that. We are seeing more and more vendors first creating a REST API and then consuming that themselves in order to provide customers with a GUI front-end.
So, in the spirit of learning how to work with the Veeam REST API I decided I’d take the time to document out how to perform some of the sample functions within their API reference using nothing but PowerShell. This first post, will deal solely with how to start an existing Veeam Backup & Replication job. Keep in mind the shear nature of REST is that although the bodies and headers may change, the process of consuming it is relatively the same no matter what the application – so there is some valid learning to be had regardless of the end product.
PowerShell and interacting with REST.
Before jumping right into Veeam specifics we should first discuss a few things around the PowerShell cmdlet we will need to use – as well as specifics around the Veeam Enterprise Manager REST API itself. REST APIs are nothing more than simple http requests sent to an endpoint – meaning they are consumed by simply sending a request, be it a GET, PUT, POST, etc. – whatever the API supports to a uri. From there, the API takes a look at what was passed and returns back what it normally would with an http request – a header, a status code, and a body – Its this response that we need to parse in order to discover any details or information pertaining to our request – it lets us know whether or not the operation was successful, and passes back and valid data as it relates to the request. Now, in Veeams case they use an XML based API for Enterprise Manager. This means we can expect to see the response body in an xml format – and, if at all we need to create a body to pass to the request, we would need to first form that body in an xml format before we sent it! Now all of this sounds kind of difficult – but in the end it really isn’t – and you will see that as we create our first script! Really, there are two key PowerShell specifics we are using….
- Invoke-WebRequest – this is the cmdlet in which we use to send the API call, passing a uri, method, and sometimes a header
- XML – this is a simple way to take our response and label/cast it as xml in order to more easily parse and retrieve the desired information from it
So with that said, let’s get scripting…
First Step – Get a SessionId
The first step in any API consumption is usually authentication – and aside from the scopes and methods themselves this is normally where we see the most discrepancies between vendors. With Veeam we simply send a POST request to the sessionMngr resource type and retrieve a sessionId. It’s this sessionId which will then need to be included within the header of all subsequent requests to the API – this is how we are identified and authenticated. Now you could send a get request to the root of the API scope and parse through all of the returned content to find a specific versions uri if you wanted – but I happen to know that we can simply use ?v=latest within Veeam to always use the latest and greatest version. So let’s go ahead and authenticate against the API and retrieve our sessionId with the following code
1 2 |
$response = Invoke-WebRequest –Uri “http://localhost:9399/api/sessionMngr/?v=latest" -Method "POST" -Credential (Get-Credential) $sessionId = $response.Headers["X-RestSvcSessionId"] |
Looking at the code above we are basically doing a couple of things – first, we issue our request to the http://localhost:9399/api/sessionMngr/?v=latest to our uri, and also have the system prompt us for credentials as this will be performing the actual authentication. And lastly, we parse our returned Headers in the response in order to grab our sessionId. So if all goes well, you should be left with a string in a similar format to the one shown below stored in our sessionId variable – and now we are authenticated and ready to start requesting…
Now let’s start that job!
So the first example in the REST API Reference is starting a specific job – to do this we first need to get the uri for the jobs resource. Now we could go ahead and simply look this up in the reference guide as it has all the information (***hint*** its http://localhost:9399/api/jobs) – but where’s the fun in that? The response we have just received from logging in has all of the information we need to grab the uri programmatically – and, should things ever change we won’t have to rewrite our code if we grab it from the response. So, to get the proper uri we can use the following one-liner to parse our content as xml and find the correct child node…
1 |
$uri = (([xml]$response.Content).LogonSession.Links.Link | where-object {$_.Type -eq 'JobReferenceList' }).Href |
Now that we have the proper uri we can go ahead and make a GET request to it to return a list of jobs within Enterprise Manager. But, remember we have to pass that sessionId through the request header as well – so in order to do this we issue the following commands…
1 |
$response = Invoke-WebRequest -Uri $uri -Method "GET" -Headers @{"X-RestSvcSessionId" = $sessionId} |
Again, our $response.Content will contain a lot of information, including all of our job names and subsequent metadata with them. So, in order to find the proper uri for my job (Backup Scoreboard) I can use the following command to once again retrieve the uri for our next call.
1 |
$uri = (([xml]$response.Content).EntityReferences.Ref.Links.Link | Where-object {$_.Name -eq 'Backup Scoreboard'}).Href |
Once we have that – we again send a GET request to the new uri
1 |
$response = Invoke-WebRequest -Uri $uri -Method "GET" -Headers @{"X-RestSvcSessionId" = $sessionId} |
Again, we get a lot of information when looking at our $response.Content – but let me somewhat format it for you below so we can see what we have…
As you can see we have a few different Href’s available to grab this time – each relating to a different action that can be taken on our job. In our case we are looking at simply starting the job so let’s go ahead and grab that uri with the following command…
1 |
$uri = (([xml]$response.Content).Job.Links.Link | Where-object {$_.Rel -eq 'Start'}).Href |
And finally, to kick the job off we send, this time a POST request, using the uri we just grabbed…
1 |
$response = Invoke-WebRequest -Uri $uri -Method "POST" -Headers @{"X-RestSvcSessionId" = $sessionId} |
Now if everything has went as intended we should be able to pop over to our VBR Console and see our job running. Now wasn’t that way easier than right clicking and selecting Start . One thing I should note is that we can parse this body as well and grab our taskId for the job we just started – from there we can go ahead and query the tasks resource to figure out its’ status, result, etc.. For those that learn better by simply seeing the complete script I’ve included it below (and in fairness running this script is faster than right-clicking and selecting ‘Start’). In our next go at PowerShell and the Veeam API we will take a look at how we can instantiate a restore – so keep watching for that… Thanks for reading!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$backupjobname = "Backup Scoreboard" #Log in to server $response = Invoke-WebRequest –Uri “http://localhost:9399/api/sessionMngr/?v=latest" -Method "POST" -Credential (Get-Credential) #Get Session Id $sessionId = $response.Headers["X-RestSvcSessionId"] # Get Job Reference link $uri = (([xml]$response.Content).LogonSession.Links.Link | where-object {$_.Type -eq 'JobReferenceList' }).Href # List jobs $response = Invoke-WebRequest -Uri $uri -Method "GET" -Headers @{"X-RestSvcSessionId" = $sessionId} # get specific job from list $uri = (([xml]$response.Content).EntityReferences.Ref.Links.Link | Where-object {$_.Name -eq $backupjobname }).Href #get job actions $response = Invoke-WebRequest -Uri $uri -Method "GET" -Headers @{"X-RestSvcSessionId" = $sessionId} #get start action $uri = (([xml]$response.Content).Job.Links.Link | Where-object {$_.Rel -eq 'Start'}).Href #Start job $response = Invoke-WebRequest -Uri $uri -Method "POST" -Headers @{"X-RestSvcSessionId" = $sessionId} |