How to Update Agent via API (Invoke-SwisVerb)?

Hello,

Does anyone know how to use the API to initiate an update of the Orion Agent?  It looks like I need to provide 'SolarWinds.AgentManagement.Contract.Models.AgentRecord' data plus a boolean (I assume true/false), but I'm not sure how to construct the AgentRecord.

I'm trying to do it via PowerShell, i.e.:

$agent = #details from 'SolarWinds.AgentManagement.Contract.Models.AgentRecord'
$updateRemoteSettings = $true
Invoke-SwisVerb -SwisConnection $swis -EntityName Orion.AgentManagement.Agent -Verb UpdateAgent -Arguments @($agent, $updateRemoteSettings)

It looks like I need to add a LOT of data to that xml string.  Is there a better way?

(sorry for code context, PowerShell not an option in code block)

  • I took a swing a modeling it after the groups code out on the SolarWinds GitHub repository because it had an XML component, but couldn't quite get there. Maybe someone with more background might be able to get over the hump.

    Import-Module SwisPowerShell
    
    $swis = Connect-Swis -Hostname <redacted> -Trusted
    
    $query = "
        SELECT TOP 1000 AgentId, AgentGuid, NodeId, Name, Hostname, DNSName, IP, OSVersion, PollingEngineId, ConnectionStatus, ConnectionStatusMessage, ConnectionStatusTimestamp, AgentStatus, AgentStatusMessage, AgentStatusTimestamp, IsActiveAgent, Mode, AgentVersion, AutoUpdateEnabled, OrionIdColumn, PassiveAgentHostname, PassiveAgentPort, ProxyId, RegisteredOn, SID, Is64Windows, CPUArch, OSArch, OSType, OSDistro, ResponseTime, Type, RuntimeOSDistro, RuntimeOSVersion, RuntimeOSLabel, OSLabel
        FROM Orion.AgentManagement.Agent
        WHERE AgentId = @agentID
    "
    
    $a = Get-SwisData -SwisConnection $swis -Query $query -Parameters @{ "agentID" = 2 }
    
    # make an arbitrary change
    $a.Name = $a.Name + ' testing'
    
    # WIN-NetSpanVM
    $arguments = @(
        ([xml]"
        <Agent xmlns:i='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://schemas.solarwinds.com/2013/AgentManagement'>
        <AgentGuid>$($a.AgentGuid)</AgentGuid>
        <AgentId>$($a.AgentId)</AgentId>
        <AgentStatus>$($a.AgentStatus)</AgentStatus>
        <AgentStatusData>$($a.AgentStatusData)</AgentStatusData>
        <AgentStatusMessage>$($a.AgentStatusMessage)</AgentStatusMessage>
        <AgentStatusTimeStamp>$($a.AgentStatusTimeStamp)</AgentStatusTimeStamp>
        <AgentVersion xmlns:d2p1='http://schemas.datacontract.org/2004/07/System'>
        <d2p1:_Build>$($a._Build)</d2p1:_Build>
        <d2p1:_Major>$($a._Major)</d2p1:_Major>
        <d2p1:_Minor>$($a._Minor)</d2p1:_Minor>
        <d2p1:_Revision>$($a._Revision)</d2p1:_Revision>
        </AgentVersion>
        <AutoUpdateEnabled>$($a.AutoUpdateEnabled)</AutoUpdateEnabled>
        <CPUArch>$($a.CPUArch)</CPUArch>
        <ConnectionStatus>$($a.ConnectionStatus)</ConnectionStatus>
        <ConnectionStatusMessage>$($a.ConnectionStatusMessage)</ConnectionStatusMessage>
        <ConnectionStatusTimeStamp>$($a.ConnectionStatusTimeStamp)</ConnectionStatusTimeStamp>
        <DNSName>$($a.DNSName)</DNSName>
        <Hostname>$($a.Hostname)</Hostname>
        <IP>$($a.IP)</IP>
        <LogLevel>$($a.LogLevel)</LogLevel>
        <Mode>$($a.Mode)</Mode>
        <Name>$($a.Name)</Name>
        <NodeId i:nil='true' />
        <OSArch>$($a.OSArch)</OSArch>
        <OSDistro>$($a.OSDistro)</OSDistro>
        <OSType>$($a.OSType)</OSType>
        <OSVersion>$($a.OSVersion)</OSVersion>
        <PassiveAgentHostname>$($a.PassiveAgentHostname)</PassiveAgentHostname>
        <PassiveAgentPort>$($a.PassiveAgentPort)</PassiveAgentPort>
        <Password>password</Password>
        <PollingEngineIP>192.168.25.30</PollingEngineIP>
        <PollingEngineId>$($a.PollingEngineId)</PollingEngineId>
        <PollingEngineName>L1SENGORION</PollingEngineName>
        <PollingEnginePort>17778</PollingEnginePort>
        <ProxyId>$($a.ProxyId)</ProxyId>
        <RegisteredOn>$($a.RegisteredOn)</RegisteredOn>
        <ResponseTime>$($a.ResponseTime)</ResponseTime>
        <RuntimeOSDistro>$($a.RuntimeOSDistro)</RuntimeOSDistro>
        <RuntimeOSVersion>$($a.RuntimeOSVersion)</RuntimeOSVersion>
        <SID>$($a.SID)</SID>
        <Type>$($a.Type)</Type>
        </Agent>
        "
        ).DocumentElement,
        $false
    )
    
    # build an argument array with the agent details and boolean value for "updateRemoteSettings"
    Invoke-SwisVerb -SwisConnection $swis -EntityName "Orion.AgentManagement.Agent" -Verb "UpdateAgent" -Arguments $arguments

    The bits that I hard-coded looked like they came from the Orion.Engines table:

        <Password>password</Password>
        <PollingEngineIP>192.168.25.30</PollingEngineIP>
        <PollingEngineName>L1SENGORION</PollingEngineName>
        <PollingEnginePort>17778</PollingEnginePort>

    I got the error you've probably got memorized at this point:

    Invoke-SwisVerb : Verb Orion.AgentManagement.Agent.UpdateAgent cannot unpackage parameter 0 of type 
    SolarWinds.AgentManagement.Contract.Models.AgentRecord
    At C:\<redacted>\UpdateAgent.ps1:69 char:1
    + Invoke-SwisVerb -SwisConnection $swis -EntityName "Orion.AgentManagem ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (:) [Invoke-SwisVerb], FaultException`1
        + FullyQualifiedErrorId : SwisError,SwisPowerShell.InvokeSwisVerb
  • Hi, did you ever get this to work? I am also looking to initiate agent updates via PowerShell 

  • When I look at the Orion.AgentManagement.Agent.UpdateAgent function within the SWQL Studio, it has two parameters:

    • agent [SolarWinds.AgentManagement.Contract.Models.AgentRecord]
    • updateRemoteSettings [boolean]

    if I copy out that sample XML block and reformat it, I get:

    <Agent xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.solarwinds.com/2013/AgentManagement">
    	<AgentGuid>00000000-0000-0000-0000-000000000000</AgentGuid>
    	<AgentId>0</AgentId>
    	<AgentStatus>Unknown</AgentStatus>
    	<AgentStatusData/>
    	<AgentStatusMessage/>
    	<AgentStatusTimeStamp>0001-01-01T00:00:00</AgentStatusTimeStamp>
    	<AgentVersion xmlns:d2p1="http://schemas.datacontract.org/2004/07/System">
    		<d2p1:_Build>-1</d2p1:_Build>
    		<d2p1:_Major>0</d2p1:_Major>
    		<d2p1:_Minor>0</d2p1:_Minor>
    		<d2p1:_Revision>-1</d2p1:_Revision>
    	</AgentVersion>
    	<AutoUpdateEnabled>false</AutoUpdateEnabled>
    	<CPUArch>Unknown</CPUArch>
    	<ConnectionStatus>Unknown</ConnectionStatus>
    	<ConnectionStatusMessage/>
    	<ConnectionStatusTimeStamp>0001-01-01T00:00:00</ConnectionStatusTimeStamp>
    	<DNSName/>
    	<Hostname/>
    	<IP/>
    	<LogLevel/>
    	<Mode>Auto</Mode>
    	<Name/>
    	<NetFrameworkRelease/>
    	<NodeId i:nil="true"/>
    	<OSArch>Unknown</OSArch>
    	<OSDistro/>
    	<OSType>Unknown</OSType>
    	<OSVersion/>
    	<PassiveAgentHostname/>
    	<PassiveAgentPort>0</PassiveAgentPort>
    	<Password/>
    	<PollingEngineIP/>
    	<PollingEngineId>0</PollingEngineId>
    	<PollingEngineName/>
    	<PollingEnginePort>0</PollingEnginePort>
    	<ProxyId>0</ProxyId>
    	<RegisteredOn>0001-01-01T00:00:00</RegisteredOn>
    	<ResponseTime>PT0S</ResponseTime>
    	<RuntimeOSDistro/>
    	<RuntimeOSVersion/>
    	<SID/>
    	<Type>FullFeaturedAgent</Type>
    </Agent>

    I'm not sure if you need all of the fields in the XML or just a few, but I'm going to do some quick testing with various degrees of completeness.  Stay tuned.

  • Wow - I overthought that way too much.  You can use the ApproveUpdate verb instead.

    # Get a list of Agents in Need of updates
    # Swap in the name/IP of your Orion server here
    $SwisHost = "kmsorion01v.kmsigma.local"
    if ( -not ( $SwisConnection ) ) {
        $SwisCreds = Get-Credential -Message "Enter Orion credentials to connect to '$SwisHost'"
        $SwisConnection = Connect-Swis -Hostname $SwisHost -Credential $SwisCreds
    }
    # For the query, all we need is the AgentId and AgentStatusMessage (or AgentStatus [numerical]), but we pull the rest to make output prettier
    $AgentQuery = @"
    SELECT [Agent].AgentId
         , [Agent].Name
         , [Agent].DNSName
         , [Agent].IP
         , [Agent].AgentStatusMessage
    FROM Orion.AgentManagement.Agent AS [Agent]
    -- ** Optionally, you can just remove the double-dash below to only process those agents with say they need an update
    --WHERE [Agent].AgentStatusMessage = 'Update available'
    "@
    
    
    # Retrieve the list from the Orion Server
    $AgentsNeedingUpdate = Get-SwisData -SwisConnection $SwisConnection -Query $AgentQuery
    
    # Cycle through each agent
    ForEach ( $Agent in $AgentsNeedingUpdate ) {
        # if the agent shows that it needs an update then...
        if ( $Agent.AgentStatusMessage -eq 'Update available' ) {
            # call the verb
            $Results = Invoke-SwisVerb -SwisConnection $SwisConnection -EntityName "Orion.AgentManagement.Agent" -Verb "ApproveUpdate" -Arguments $Agent.AgentId
            # Check to see if it ran clean (no output)
            if ( $Results.nil ) {
                Write-Host "Agent for $( $Agent.Name ) [$( $Agent.DNSName ) / $( $Agent.IP ) is updating." -ForegroundColor Green
            }
            else {
                Write-Error -Message "Error detected in sending agent update request"
            }
        }
        else {
            # This particular agent doesn't need an update or is offline or something
            Write-Warning -Message "Agent for $( $Agent.Name ) [$( $Agent.DNSName ) / $( $Agent.IP ) has a current status of: $( $Agent.AgentStatusMessage )"
        }
    }

  • Added another bit to check if plugins also needed to be updated.  (Can just be added to the bottom of the above script)

    # Now let's see if there are any plugins that require updating
    $PluginQuery = @"
    SELECT [Agent].AgentId
         , [Agent].Name
         , [Agent].DNSName
         , [Agent].IP
         , [Agent].Plugins.PluginId
         , [Agent].Plugins.Version AS [PluginVersion]
    FROM Orion.AgentManagement.Agent AS [Agent]
    WHERE [Agent].AgentStatusMessage = 'Plug-in update required'
    "@
    
    # Get the List
    $PluginsToUpdate = Get-SwisData -SwisConnection $SwisConnection -Query $PluginQuery
    # Cycle through the list
    ForEach ( $Agent in $PluginsToUpdate ) {
        # Invoke the DeployPlugin verb with the AgentId and the PluginId
        # Note: if you use the "RedeployPlugin" verb it doesn't update, just sends out the current version again
        $PluginResults = Invoke-SwisVerb -SwisConnection $SwisConnection -EntityName "Orion.AgentManagement.Agent" -Verb "DeployPlugin" @( $Agent.AgentId, $Agent.PluginId )
        if ( $PluginResults.nil ) {
            Write-Host "Plugin '$( $Agent.PluginId )' for $( $Agent.Name ) [$( $Agent.DNSName ) / $( $Agent.IP ) is updating from '$( $Agent.PluginVersion )'" -ForegroundColor Green
        } else {
            Write-Error -Message "Unable to request '$( $Agent.PluginId )' update on $( $Agent.Name )"
        }
    }

  • Many thanks for this! I will give the script a try