Traverse.psm1
#Requires -Version 3 function Connect-TraverseBVE { <# .SYNOPSIS Connects to a Traverse BVE system with the Web Services API enabled. .PARAMETER Hostname The DNS name or IP address of the Traverse BSM system .PARAMETER Credential The username and password needed to access the system in secure PSCredential format. .PARAMETER Force Create a new session even if one already exists .PARAMETER NoREST Skips the connection to the REST API .PARAMETER RESTSessionPassTHru Pass the REST session object to the pipeline. Useful if you want to work with multiple sessions simultaneously .PARAMETER WSSessionPassThru Pass the SOAP session object to the pipeline. Useful if you want to work with multiple sessions simultaneously #> param ( [Parameter(Mandatory=$true)][String]$Hostname, [Parameter(Mandatory=$true)][PSCredential]$Credential = (get-credential -message "Enter your Traverse Username and Password"), [Switch]$Force, [Switch]$NoREST, [Switch]$RESTSessionPassThru, [Switch]$WSSessionPassThru ) # Param if (!$NoREST) { #Check for existing session if ($global:TraverseSessionREST -and !$force) {write-warning "You already have a Traverse REST session. Use the -force parrameter if you want to connect to a different one or use a different username";return} #Log in using Credentials $RESTLoginURI = "https://$Hostname/api/rest/command/login?" + $Credential.GetNetworkCredential().UserName + "/" + $Credential.GetNetworkCredential().Password $loginResult = Invoke-RestMethod -sessionvariable TraverseSessionREST -Uri $RESTLoginURI if ($loginresult -notmatch "OK") {throw "The connection failed to $Hostname. Reason: $loginResult"} $Global:TraverseSessionREST = $TraverseSessionREST write-verbose "Connected to $Hostname BVE as $($Credential.GetNetworkCredential().Username) using REST API" $TraverseSessionREST } if ($global:TraverseSession -and !$force) {write-warning "You are already logged into Traverse. Use the -force parameter if you want to connect to a different one or use a different username";return} #Workaround for bug with new-webserviceproxy (http://www.sqlmusings.com/2012/02/04/resolving-ssrs-and-powershell-new-webserviceproxy-namespace-issue/) $TraverseBSMLoginWS = (new-webserviceproxy -uri "https://$($hostname)/api/soap/login?wsdl" -ErrorAction stop) $TraverseBSMLoginNS = $TraverseBSMLoginWS.gettype().namespace #Create the login request and unpack the password from the encrypted credentials $loginRequest = new-object ($TraverseBSMLoginNS + '.loginRequest') $loginRequest.username = $credential.GetNetworkCredential().Username $loginRequest.password = $credential.GetNetworkCredential().Password $loginResult = $TraverseBSMLoginWS.login($loginRequest) if (!$loginResult.success) {throw "The connection failed to $Hostname. Reason: Error $($loginresult.errorcode) $($loginresult.errormessage)"} set-variable -name TraverseSession -value $loginresult -scope global set-variable -name TraverseHostname -value $hostname -scope global write-verbose "Connected to $hostname BVE as $($loginrequest.username) using SOAP API" $LoginResult } #Connect-TraverseBSM function Get-TraverseDevice { #.Description #Gets all listed Traverse devices. #TODO: Add SearchCriteria param ( ) # Param #Exit if not connected if (!$global:TraverseSession) {write-warning "You are not connected to a Traverse BSM system. Use Connect-TraverseBSM first";return} #Connect to the Device Web Service $TraverseBSMDeviceWS = (new-webserviceproxy -uri "https://$($TraverseHostname)/api/soap/device?wsdl" -ErrorAction stop) $TraverseBSMDeviceNS = $TraverseBSMDeviceWS.gettype().namespace #Create device request $DeviceRequest = new-object ($TraverseBSMDeviceNS + '.deviceStatusesRequest') $DeviceRequest.sessionid = $TraverseSession.result.sessionid $DeviceResult = $TraverseBSMDeviceWS.getStatuses($DeviceRequest) if (!$DeviceResult.success) {throw "The connection failed to $TraverseHostname. Reason: Error $($DeviceResult.errorcode) $($DeviceResult.errormessage)"} return $DeviceResult.result.devices } #Get-TraverseDevice workflow Get-TraverseWindowsServerExtendedInfo { <# .SYNOPSIS Gets extended information about a Traverse Windows Device such as BMC and Serial Number, and adds an ExtendedInfo property to the device object .PARAMETER TraverseDeviceObject One or more Traverse Device Objects obtained via Get-TraverseDevice .PARAMETER ThrottleLimit How many devices to process concurrently if multiple devices are specified. Default is 5 .PARAMETER GetHPInfo If enabled, system will try additional techniques to get HP iLO BMC information. Requires the HPILOStatus module and PSExec from Sysinternals to be present in the path. #> param( $TraverseDeviceObject, [int]$ThrottleLimit = 5, [switch]$UseHPONCFG ) foreach -parallel -throttle $ThrottleLimit ($device in $TraverseDeviceObject) { inlinescript{ $device = $USING:Device $deviceAddress = $device.deviceaddress #Construct the result hashtable $InfoResult = @{} #Get the system Hostname, Make, Model, and Serial Number Information write-progress -Activity "Get Traverse Windows Extended Info" -CurrentOperation "$($devices.DeviceName): Querying WMI Information" $deviceComputerSystemInfo = Get-WMIObject win32_computersystem -computername $deviceAddress -erroraction stop $deviceBIOSInfo = Get-WMIObject Win32_bios -computername $deviceAddress -erroraction stop if ($deviceComputerSystemInfo -and $deviceBIOSInfo) { if ($deviceComputerSystemInfo.model -match "Virtual") { $infoResult.isVirtual = $true } else { $infoResult.Manufacturer = $deviceComputerSystemInfo.Manufacturer.Trim() $infoResult.Model = $deviceComputerSystemInfo.Model.Trim() $infoResult.SerialNumber = $deviceBIOSInfo.SerialNumber.Trim() $infoResult.isVirtual = $false } #Else } #If #Get BMC IP Information $BMCResult = get-wmibmcipaddress $deviceAddress if ($BMCResult) {$InfoResult.BMCIPAddress = $BMCResult.BMCIPAddress} #Attach the Extended Attribute to the device and return it $device | Add-Member -Name "extendedInfo" -MemberType NoteProperty -Value $InfoResult return $device } #InlineScript } #Foreach -Parallel } #Get-TraverseExtendedInfo function Set-TraverseDevice { <# .SYNOPSIS Update the configuration of a device. Currently this only supports some basic descriptive information. .NOTES This is a wrapper around the Device.Update FlexAPI command http://help.kaseya.com/webhelp/EN/tv/7000000/dev/index.asp#30181.htm Supports Common Parameters -Whatif and -Confirm .PARAMETER TraverseDevice A Traverse Device, represented as a Traverse deviceStatus object. .PARAMETER NewDeviceName Rename a device. THIS IS DANGEROUS IF USED ON THE PIPELINE AND YOU CAN ACCIDENTALLY SET A LOT OF DEVICES TO THE SAME NAME. Please be careful with this parameter .EXAMPLE Set the description for all devices to "this is a test" (remove the -whatif to do it for real) PS C:\> Get-TraverseDevice | Set-TraverseDevice -Comment #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory=$true,ValueFromPipeline=$true)]$TraverseDevice, [Alias("Description")][String]$Comment, [String]$Tag5, [String]$NewDeviceName ) begin { if (!$global:TraverseSessionREST) {write-warning "You are not connected to a Traverse BSM system via REST. Use Connect-TraverseBSM first";return} #Populate the update information based on what was provided $setDeviceParams = [ordered]@{} if ($Comment) {$setDeviceParams.Comment = $Comment.trim()} if ($NewDeviceName) {$setDeviceParams.DeviceName = $newDeviceName.trim()} #Tag5 might store extended properties as XML. If so, replace XML brackets with benign character so that it is not flagged by API if ($Tag5) { #Strip out any curly brackets and carriage returns. Not allowed for extended properties anyways $Tag5 = $Tag5.replace("`r",'').replace("`n",'').replace("`{",'').replace("`}",'').trim() #Tag5 might store extended properties as XML. If so, replace XML brackets with benign curly brackets so that it is not flagged by API $setDeviceParams.Tag5 = $Tag5.replace('<','{').replace('>','}') } #Exit if nothing was specified if ($setDeviceParams.count -eq 0) {throw "No parameters for the device has been specified to be set. Use the arguments to add information to set on the device. See the help for examples."} } process { foreach ($Device in $TraverseDevice) { $setDeviceParams.DeviceSerial = $Device.serialnumber if ($PSCmdlet.ShouldProcess("$($Device.devicename) `($($Device.serialnumber)`)","Setting Traverse Device Properties")) { $uriSetDevice = "https://$TraverseHostname/api/rest/command/devices.update" $resultSetDevice = invoke-restmethod -WebSession $TraverseSessionREST -uri $uriSetDevice -body $setDeviceParams if (!$resultSetDevice) {$resultSetDevice = "Error: No Response from Traverse BVE"} #Return a Result Object $resultSetDeviceProperty = [ordered]@{} $resultSetDeviceProperty.TraverseDeviceName=$TraverseDevice.DeviceName $resultSetDeviceProperty.TraverseDeviceSerial=$setDeviceParams.DeviceSerial $resultSetDeviceProperty.Result=$ResultSetDevice new-object PSObject -property $resultSetDeviceProperty }#If }#Foreach } } function Update-TraverseWindowsExtendedInfo { <# .SYNOPSIS Queries Customer Windows Servers and updates their Traverse Extended Info (stored in Tag5) .PARAM TraverseAccountName Name of the customer account to update. The computer system must have network access to the systems to be updated. Must match exactly for safety .NOTES THIS ASSUMES LOGGED IN USER HAS RIGHTS TO THE TARGET SYSTEM. TODO: Allow for alternate credentials TODO: Add full device search criteria .EXAMPLE Update all Systems for Customer "Contoso Corp" PS C:\> Update-TraverseWindowsExtendedInfo -TraverseAccountName "Contoso Corp" #> param ( [String]$TraverseAccountName, $credential = $seasonsCredential ) $devices = Get-TraverseDevice | where {$_.accountname -eq $TraverseAccountName} $windevices = $devices | where {$PSItem.devicetypestr -match "Windows Server"} $resultExtendedInfo = get-TraverseWindowsServerExtendedInfo $windevices foreach ($result in $resultExtendedInfo) { if ($result.extendedInfo) { $xmlExtendedInfo = ($result.extendedinfo | convert-hashtabletoxml -root DeviceExtendedInfo).OuterXML.replace("`n","") set-traversedevice $result -Tag5 $xmlExtendedInfo } } } |