cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
7717
Views
17
Helpful
13
Replies

PI API 3.0 with Powershell Invoke-RestMethod

rogerjam
Level 1
Level 1

Hi All,

I'm trying to get the powershell Invoke-RestMethod to interact with the Prime Infrastructure API, but I'm not having much luck. Basically I want to retrieve a list of devices from the PI API, but I'm not sure what to put in the body for the Invoke-RestMethod command. Does anyone have any experience working with PI API and Powershell?

Thanks,

James

13 Replies 13

Same here! Were you able to connect to the server? I can't ! Did you have a chance to read the tutorial? There's some detailed info but it's to connect that's my problem. I haven't tried other REST applications though.

Serge

Spencer Zier
Cisco Employee
Cisco Employee

Hey, I'm not a PowerShell expert by any measure, but I did manage to get it working.

First, if you have a self-signed certificate for Prime Infrastructure, your requests will likely fail with the error message

The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

So you'll need to get around this by setting up a new certificate policy to trust all certs.  To do that, I found an example on Stack Overflow, and it worked when I tested it.  Note that this certificate policy will apply to the entire PowerShell session, so use this workaround at your risk.

Next, you'll need to prepare the authentication info.  You can either create a variable with your base64 encoded username:password string, or have PowerShell do the encoding for you with the following code

$basicAuth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("username:password")))

Now, you're ready to make the actual call.  The certificate policy (if you decided to use it) is session global, so the only thing you need to have when you make the invoke is the base64 authentication string and the URL.  Here's an example

$response = Invoke-RestMethod "https://my_server/webacs/api/v1/data/Devices" -headers @{Authorization=("Basic {0}" -f $basicAuth)}

Well thanks for that important nudge ;-)

I think that's what you might be looking for:

$response.queryResponse.entity.DevicesDTO | select devicename, devicetype, ipaddress, location

Now for some reason, I'm getting a 401 error :-(. I think prime might be busy !

UPDATE:

Figured out that the -DisableKeepAlive is a must with the invoke-restmethod cmdlet since Prime only allows 5 concurrent connections so in trying various steps I basically locked myself out.

Another trick to get one final variable to hold all the device info which I ended up with is to use this line:

$DeviceInfo = $((Invoke-RestMethod "https://myserver/webacs/api/v1/data/Devices?.full=true" -DisableKeepAlive -headers @{Authorization=("Basic {0}" -f $basicAuth)}) | select @{n="DeviceInfo";e={$_.queryResponse.entity.DevicesDTO}}).deviceinfo

Then you can $DeviceInfo | select whatever.. or $DeviceInfo | out-gridview to see everything in powershell's grid from which you can copy/paste or filter columns.

Thanks again

Serge

Hi Spencer,

Thanks for the reply! I tried that method and I'm getting the error:

Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.


I've tried multiple callback functions that I've found across the web including the Stack Exchange link you mentioned that trusts all certs. None of them have worked.


Here's what my code looks like minus all of the sensitive bits:


function Ignore-SSLCertificates

{

    $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider

    $Compiler = $Provider.CreateCompiler()

    $Params = New-Object System.CodeDom.Compiler.CompilerParameters

    $Params.GenerateExecutable = $false

    $Params.GenerateInMemory = $true

    $Params.IncludeDebugInformation = $false

    $Params.ReferencedAssemblies.Add("System.DLL") > $null

    $TASource=@'

        namespace Local.ToolkitExtensions.Net.CertificatePolicy

        {

            public class TrustAll : System.Net.ICertificatePolicy

            {

                public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem)

                {

                    return true;

                }

            }

        }

'@

    $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)

    $TAAssembly=$TAResults.CompiledAssembly

    ## We create an instance of TrustAll and attach it to the ServicePointManager

    $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")

    [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll

$basicAuth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("username:password")))

$url = 'https://hostname/webacs/api/v1/data/Devices'

$response = Invoke-RestMethod $url -headers @{Authorization=("Basic {0}" -f $basicAuth)}

James I had the same issue at the beginning and used the other cert method which worked for me.

add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

Followed by the $basicAuth..., $url..., $response (or even beter my $deviceinfo=... which I've included above.)

Serge

I saw the same error when after I tried an alternate approach to getting PowerShell to accept the self-signed cert.  What I tried was

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

But every Invoke-RestMethod I tried after that failed with the same error message you're seeing, about an unexpected error occuring during a send.  When I ran into it, the easiest way I found to clear that error was to start a new PowerShell session.

After implementing the certificate policy instead, in a new window, things started working.  Hope that helps.

EDIT:  From what I understand the error message is a known bug.

Also, just in case it proves relevant, here's the $PSVersionTable of my PowerShell.

NameValue
PSVersion4.0
WSManStackVersion3.0
SerializationVersion1.1.0.1
CLRVersion4.0.30319.34209
BuildVersion6.3.9600.16406
PSCompatibleVersions{1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion2.2

Ok, so it looks like I'm all caught up with you guys but now I'm getting a 401 error like Serge was getting. I tried the DisableKeepAlive but still no luck. I know the credentials work for sure. Is there anything else I should try to get this to go through? I'll try restarting my powershell setting again.

It could be that there already are 5 concurrent sessions for your user.  If possible, you could try restarting the Prime Infrastructure appliance.  Or you could try creating a new user.

I needed to wait a while before retrying again. Prime most probably has a timing threshold (maybe 15min.) before allowing connection again.

Serge

I'll try again later and update this thread. Thanks for the assistance gentlemen.

James

I'm sort of at a loss as of now. I'm still getting a 401 Unauthorized error when trying to access through Powershell, but I can access it just fine thru curl using the same credentials.

James

Ok, looks like I fixed it. I manually encoded the username:password string to base64 using a website converter and changed the quotes on my URL string to a double quote instead of single quote and it worked after that. Now, I'm curious how do I get a complete list of devices? It is only showing me 100 devices at a time and it looks like there are over 2300.

James

That is called "pagination".  You can "page" through the results using multiple calls

.maxResults - is the number of devices to return.

.firstResult - is the place to start from.  (Initially 1).

#NOTE: there is a "." for each of these keys.

this would get the next 50 results after the first 100.

requestUrl="https://my_server/webacs/api/v1/data/Devices?.full=true&.maxResults=50&.firstResult=100"