Skip to content

Instantly share code, notes, and snippets.

@JustinGrote
Last active March 11, 2024 21:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JustinGrote/74af66cf3aaed3fcf201e729dd3c899f to your computer and use it in GitHub Desktop.
Save JustinGrote/74af66cf3aaed3fcf201e729dd3c899f to your computer and use it in GitHub Desktop.
Commvault REST API Commands I will make into a Module Someday
<#
Quickstart
------------------
Get-Help Connect-CVServer -detailed
Get-Help Invoke-CVCommand -detailed
Connect-CVServer mycommvaultserver -force -verbose
Invoke-CVCommand 'Client'
Invoke-CVCommand 'Client/136'
#>
function Invoke-CVCommand {
[CmdletBinding(SupportsShouldProcess)]
param (
#The path to the command relative to the API root. Examples: "Client","Client/{clientId}","Backupset"
[Parameter(Mandatory,ValueFromPipeline,Position=0)][String]$Command,
#Additional information to supply in the request. If it is a string, it is passed literally (e.g. an XML). If it is a hashtable, it will be converted to either HTTP GET parameters or POSTData, depending on the action selected
[Alias("ArgumentList")][Parameter()]$Body,
#Which HTTP Method to perform (GET, POST, or DELETE)
[ValidateSet("GET","POST","DELETE")][String]$Method = "GET",
#Specify the Base URI for the API. Used by Connect-CVServer.
[Parameter(ParameterSetName="Login")][URI]$URI
)
#Some special behavior for Login is required
if ($Command -eq "Login") {$isLogin = $true}
#Basic Status Checks
if (-not $isLogin) {
if (-not $SCRIPT:CVAPILogin) {
write-error "You must first connect to a Commvault system with Connect-CVServer."
return
}
if ((get-date) -gt ($SCRIPT:CVAPILoginExpireDate)) {
write-error "Your session has been inactive for more than 30 minutes, please log in again using Connect-CVServer"
return
}
}
$IRMParams = @{
UseBasicParsing = $true
Headers = @{
Accept = 'application/json'
}
ContentType = 'application/json'
Method = $Method
Body = $Body
}
#Login requests don't have an Auth token to give, obviously
if (-not $isLogin) {
$IRMParams.Headers.Authtoken = $SCRIPT:CVAPILogin.token
$CVAPIBaseURI = $SCRIPT:CVAPIURI
$IRMParams.WebSession = $SCRIPT:CVAPIWebSession
} else {
#Without a trailing slash, URI command builder will replace it
if ($URI -notmatch '/$') {
write-warning "The API root provided ($URI) did not end in a trailing slash. Adding it as the API root requires one"
$URI = [uri]([string]$URI + '/')
}
$CVAPIBaseURI = $URI
$IRMParams.SessionVariable = 'CVAPIWebSessionTemp'
}
#I would use Join-Path here normally but it doesn't work for HTTP URIs
$IRMParams.URI = new-object URI ($CVAPIBaseURI,$Command)
if ($PSCmdlet.ShouldProcess($IRMParams.URI.Host,"Invoking Command $Command")) {
#TODO: Better Logic Here
if ($Force) {
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
}
$restResult = Invoke-Restmethod @IRMParams -ErrorAction 'stop' -ErrorVariable 'restError'
if ($Force) {
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $null }
}
#Put the web session in the script (module) scope
if ($CVAPIWebsessionTemp) {$GLOBAL:CVAPIWebSession = $CVIAPIWebSessionTemp}
if ($restResult.errList) {
write-error "Error returned from $Computername`: $($restResult.errList.errLogMessage)"
continue
}
}
$restResult
#Reset the clock on token since we used a command
$SCRIPT:CVAPILoginExpireDate = (get-date).AddMinutes(29)
}
function Connect-CVServer {
[CmdletBinding(SupportsShouldProcess)]
param (
#Hostname of the Commvault System
[Parameter(Mandatory=$true,ValueFromPipeline)][String]$ComputerName,
#Credentials to Use. For Active Directory logins, you can specify in either NT format (DOMAIN\Username) or the email address of the user as configured in Commvault/AD.
[Parameter(Mandatory=$true)]
[System.Management.Automation.PSCredential]$Credential = (Get-Credential -Message "Enter your Commvault Credentials"),
#Specify which remote CommServe you wish to connect to (example: commserve_host_name*commserve_name). Default is the same one as ComputerName.
$CommServer = "",
#Override the default base URI. You shouldn't need to do this unless you have a special configuration, proxy, or custom port. Your URI must end in a trailing slash!
$URI = "https://$($ComputerName)/webconsole/api/",
#Force the login even if already logged in or SSL cert cannot be validated
[Switch]$Force,
#Provide the Credential Object Including the API token
[Switch]$Passthru,
#Go directly to the Tomcat webserver on port 81 rather than via Web Console. May be required for older Commvault versions before v11. WARNING: Login occurs in plaintext!
[Switch]$Direct
)
if ((get-date) -lt ($SCRIPT:CVAPILoginExpireDate) -and -not $Force) {
write-warning "You are already connected to $($SCRIPT:CVAPIURI.host) as $SCRIPT:CVAPILoginUserName. Use -Force to reconnect anyways."
return
} else {
$SCRIPT:CVAPILogin = $null
[String]$SCRIPT:CVAPILoginUserName = $null
[URI]$SCRIPT:CVAPIURI = $null
$SCRIPT:CVAPILoginExpireDate = $null
$SCRIPT:CVAPIWebSession = $null
}
if ($Direct) {
write-warning "WARNING: Direct Login Method passes username and password in cleartext. Only use this on a local trusted network."
$URI = "http://$($ComputerName):81/SearchSvc/CVWebService.svc/"
}
$LoginRequestBody = @{
domain = $credential.GetNetworkCredential().domain
username = $credential.GetNetworkCredential().username
#Base64-encoded password
password = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Credential.getNetworkCredential().password))
commserver = $CommServer
} | convertto-json -compress
if ($PSCmdlet.ShouldProcess($Computername,"Connecting as $($Credential.username)")) {
$restResult = Invoke-CVCommand -Command 'Login' -Method 'POST' -URI $URI -Verbose:$VerbosePreference -ErrorVariable RestError -Body $LoginRequestBody
#Immediately remove plaintext credentials from memory once task is complete
Remove-Variable "LoginRequestBody"
if ($RestError) {continue}
if ($restResult.token) {
$SCRIPT:CVAPILogin = $RestResult
$SCRIPT:CVAPILoginUserName = $Credential.username
$SCRIPT:CVAPIURI = $URI
write-host -fore Green "Connected to $ComputerName as $($Credential.username) ($($RestResult.username))"
if ($passThru) {
$SCRIPT:CVAPILogin
}
} else {
write-error "No authentication token was received from $ComputerName. Review for any errors above and check your connectivity."
}
}
}
function Disconnect-CVServer {
$logoutresult = Invoke-CVCommand 'Logout' -Method POST
if ($logoutResult -eq 'User logged out') {
get-variable CVAPI* -Scope script | Remove-Variable -Scope Script
} else {
write-error "Did not receive a valid logout response when disconnecting. Check your connectivity."
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment